Comment rendre l'extension en PHP7 plus difficile que "bonjour, monde", et ne pas devenir aux yeux rouges. 2e partie

Résumé de la première partie


Dans la première partie, j'ai fait une extension vierge , je l'ai fait fonctionner correctement dans l'IDE Clion, j'ai écrit une fonction analogique, my_array_fill (), et vérifié son opérabilité en php.

Et maintenant


Maintenant, je vais mettre le code de la bibliothèque libtrie dans notre extension.

Je vais parler un peu de la façon dont vous pouvez faire fonctionner les anciennes extensions php5 en php7.
De plus, je ferai quelques fonctions de base de cette bibliothèque en php et vérifierai ce qui s'est passé.

On y va


Obtenez le code libtrie dans notre extension


Je vais dans le répertoire des extensions

cd ~/Documents/libtrie/ 

Clonage du référentiel libtrie

 git clone https://github.com/legale/libtrie 



J'ouvre le fichier avec le code d'extension php_libtrie.c et le fichier avec le code de bibliothèque libtrie/src/libtrie.c .



Je vais utiliser ce dernier pour vérifier les noms et la syntaxe des fonctions.

Les fonctions créées dans php que j'utiliserai sont les mêmes que dans la bibliothèque elle-même.
Tout d'abord, vous devez inclure le fichier d'en-tête de bibliothèque dans le code de notre extension.
Nous écrivons dans php_libtrie.c:

 #include "libtrie/src/libtrie.h" 

Fonction Yatrie_new


Je fais la première fonction qui créera l'arbre de préfixe. Dans la bibliothèque ça s'appelle

 trie_s *yatrie_new(uint32_t max_nodes, uint32_t max_refs, uint32_t max_deallocated_size) {...} 

Comme vous pouvez le voir dans le code, la fonction prend 3 arguments numériques à l'entrée et renvoie un pointeur sur la structure trie_s . Autrement dit, renvoie un lien vers l'arbre de préfixe créé.

Afin de tirer notre arborescence de préfixes en PHP, PHP a un type de données de ressource spécial. Lorsqu'une fonction est exécutée en PHP

 fopen("filename.ext"); 

Parlant en langage C, le programme demande au système d'exploitation d'ouvrir le fichier spécifié, crée un pointeur vers ce fichier, qui sous la forme d'une ressource et retourne en dehors de PHP.

Nous ferons de même avec notre arbre.

Faisons une fonction dans php_libtrie.c:

Code de fonction
 PHP_FUNCTION (yatrie_new) { /*     */ trie_s *trie; //     zend_long max_nodes; // -      zend_long max_refs; /*  -   . *        . -  +25%     . * ,    OpenCorpora ~3.    5.   5.  */ zend_long max_deallocated_size; /*        *    .    96    , 1  ,  95. *              95,  , *  .         94. */ //   PHP if (zend_parse_parameters(ZEND_NUM_ARGS(), "lll", &max_nodes, &max_refs, &max_deallocated_size) == FAILURE) { RETURN_FALSE; } //             trie = yatrie_new((uint32_t)max_nodes, (uint32_t)max_refs, (uint32_t)max_deallocated_size); //   -   if (!trie) { RETURN_NULL(); } //  2  /*  zend_register_resource()     Zend, *        le_libtrie,   ZVAL_RES() *     zval return_value */ ZVAL_RES(return_value, zend_register_resource(trie, le_libtrie)); } 


Vous devez maintenant ajouter la fonction créée au tableau des fonctions d'extension, sinon la fonction ne sera pas visible depuis PHP.

 PHP_FE(yatrie_new, NULL) 



Pour le rendre beau, j'ajouterai une déclaration de fonction au fichier d'en-tête. Ce n'est pas nécessaire, car nos fonctions PHP n'interagissent pas entre elles, mais je préfère quand même déclarer toutes les fonctions dans le fichier d'en-tête.

Ajoutez simplement les lignes:

 PHP_FUNCTION(confirm_libtrie_compiled); PHP_FUNCTION(my_array_fill); PHP_FUNCTION(yatrie_new); 
dans le fichier php_libtrie.h. N'importe où entre:
 #ifndef PHP_LIBTRIE_H #define PHP_LIBTRIE_H 
et
 #endif /* PHP_LIBTRIE_H */ 



PHP a créé le destructeur de ressources


La fonction yatrie_new () créée crée un arbre et enregistre également une ressource PHP. Nous avons maintenant besoin d'une fonction qui fermera la ressource créée et libérera la mémoire occupée par l'arborescence des préfixes.

 /** * @brief  ,           * @param rsrc : zend_resource *  * @return void */ static void php_libtrie_dtor(zend_resource *rsrc TSRMLS_DC) { //     trie   trie_s *trie = (trie_s *) rsrc->ptr; //   ,   , //   trie yatrie_free(trie); } 

Étant donné que la fonction est interne au tableau de fonctions d'extension, elle n'est pas incluse. Ajoutez sa déclaration à php_libtrie.h:



Vous devez maintenant enregistrer la fonction destructrice créée en PHP. Cela se fait via une fonction spéciale d'initialisation d'extension. Auparavant, cette fonction renvoyait simplement SUCCESS immédiatement. Il faut y ajouter l'enregistrement du destructeur.

 //  PHP     trie PHP_MINIT_FUNCTION (libtrie) { le_libtrie = zend_register_list_destructors_ex( php_libtrie_dtor, NULL, PHP_LIBTRIE_RES_NAME, module_number); return SUCCESS; } 



Supprimer la fonction d'arborescence créée


Tout comme la fonction fopen() a quelques fclose() , ma fonction de création d'arbre devrait avoir une petite amie qui l'équilibrera.

Code
 /** * @brief     * @param trie : resource * @return true/false : bool */ PHP_FUNCTION (yatrie_free) { zval *resource; //  zval    //    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &resource) == FAILURE) { RETURN_FALSE; } /*    ,    zend_resource, *      zval.     Z_RES_P() */ if (zend_list_close(Z_RES_P(resource)) == SUCCESS) { //  true  return_vale   return RETURN_TRUE; } //  false  return_vale   return RETURN_FALSE; } 


Ajoutez une fonction au tableau des fonctions d'extension:

 PHP_FE(yatrie_free, NULL) 

Ajoutez la déclaration de fonction au fichier d'en-tête:

 PHP_FUNCTION(yatrie_free); 



Comme vous pouvez le voir sur la capture d'écran, j'ai ajouté le nom interne de la ressource en PHP au fichier d'en-tête, ainsi que les macros PHP5, qui pour une raison quelconque ont été supprimées de PHP7. Je ne les utilise pas, mais si quelqu'un le souhaite, vous pouvez facilement créer l'extension PHP5 en PHP7.

 #define PHP_LIBTRIE_VERSION "0.1.0" /* Replace with version number for your extension */ #define PHP_LIBTRIE_RES_NAME "libtrie data structure" /* PHP resource name */ //previously (php5) used MACROS #define ZEND_FETCH_RESOURCE(rsrc, rsrc_type, passed_id, default_id, resource_type_name, resource_type) \ (rsrc = (rsrc_type) zend_fetch_resource(Z_RES_P(*passed_id), resource_type_name, resource_type)) #define ZEND_REGISTER_RESOURCE(return_value, result, le_result) ZVAL_RES(return_value,zend_register_resource(result, le_result)) 

Fonction ajouter des mots dans trie


Faisons maintenant l'ajout d'un mot à l'arbre des préfixes.


  1. Code
     /** * @brief   trie    node_id     * @param trie : resource * @param word : string * @return node_id : int */ PHP_FUNCTION (yatrie_add) { trie_s *trie; //   zval *resource; //  zval    unsigned char *word = NULL; //     size_t word_len; //  word uint32_t node_id; //id  ,   //  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &resource, &word, &word_len) == FAILURE) { RETURN_FALSE } /*    PHP,  : * 1    PHP ( zval,   ), * 2    *         * 3   id ,       *     void *,        trie_s * *  PHP5      ZEND_FETCH_RESOURCE(),  -    PHP7. */ trie = (trie_s *) zend_fetch_resource(Z_RES_P(resource), PHP_LIBTRIE_RES_NAME, le_libtrie); /*    trie *   -      *   - id     ,      *   -    . */ node_id = yatrie_add(word, 0, trie); //   RETURN_LONG(node_id); } 


  2. Ajoutez une entrée au tableau de fonctions:

     PHP_FE(yatrie_add, NULL) 
  3. Ajoutez une déclaration au fichier d'en-tête:

     PHP_FUNCTION(yatrie_add) 

La fonction pour sortir tous les mots du dictionnaire


Maintenant, créons une fonction qui sélectionnera tous les mots de l'arborescence des préfixes et les affichera en PHP sous forme de tableau.


  1. Code
     /** * @brief    ,    ,      * ,     * @param trie : resource * @param node_id : int * @param head () : string ,   *       * @return array */ PHP_FUNCTION (node_traverse) { trie_s *trie; //  trie words_s *words; //     trie zval * resource; //  zval  zend_long node_id; //  unsigned char *head = NULL; //    size_t head_len; //  //   PHP if (zend_parse_parameters(ZEND_NUM_ARGS(), "rl|s", &resource, &node_id, &head, &head_len) == FAILURE) { RETURN_NULL(); // null    } //     trie = (trie_s *) zend_fetch_resource(Z_RES_P(resource), PHP_LIBTRIE_RES_NAME, le_libtrie); //    trie  node_traverse()    words_s //    words = (words_s *) calloc(1, sizeof(words_s)); words->counter = 0; //    0 //  1     string_s *head_libtrie = calloc(1, sizeof(string_s)); // head  if(head != NULL) { head_libtrie->length = (uint32_t)head_len; //  memcpy(&head_libtrie->letters, head, head_len); //   head_libtrie } //    trie node_traverse(words, (uint32_t) node_id, head_libtrie, trie); //  PHP ,       words array_init_size(return_value, words->counter); //    php while (words->counter--) { //  trie     ,    //     uint8_t dst[256]; //   libtrie decode_string(dst, words->words[words->counter]); //  Zend API,        php string    char * add_next_index_string(return_value, (const char *) dst); } //      words  head_libtrie free(words); free(head_libtrie); } 

  2. Ajoutez une entrée au tableau de fonctions:

     PHP_FE(node_traverse, NULL) 

  3. Ajoutez une déclaration au fichier d'en-tête:

     PHP_FUNCTION(node_traverse) 


Ensemble d'extension


Étant donné que l'extension utilise désormais des fichiers de bibliothèque tiers, ces fichiers doivent également être compilés. J'ouvre le fichier config.m4 et j'y ajoute les 2 fichiers sources libtrie:

libtrie/src/libtrie.c
libtrie/src/single_list.c


Voici le contenu complet du fichier après les modifications.

config.m4
 PHP_ARG_ENABLE(libtrie, whether to enable libtrie support, [ --enable-libtrie Enable libtrie support]) if test "$PHP_LIBTRIE" != "no"; then #    -    # PHP_ADD_INCLUDE(libtrie/src/) #   PHP_NEW_EXTENSION(libtrie, \ libtrie/src/libtrie.c \ libtrie/src/single_list.c \ php_libtrie.c \ , $ext_shared) # PHP_NEW_EXTENSION(libtrie, php_libtrie.c libtrie/src/libtrie.c libtrie/src/single_list.c, $ext_shared,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1) fi 




Vous devez maintenant refaire le script ./configure. Je cours depuis le répertoire racine de l'extension:

 phpize && ./configure 

Maintenant je construis l'extension:

 make 

Test


Pour le test, il est préférable de faire un script php afin de ne pas trop écrire dans la console. Je vais le faire:

 nano yatrie_test.php 

Et voici le contenu du fichier:

 <?php echo "C   500   500 \n\n"; $trie = yatrie_new(500, 500, 100); echo "!\n  ,  id    \$nodes\n"; $nodes[] = yatrie_add($trie, ""); $nodes[] = yatrie_add($trie, ""); $nodes[] = yatrie_add($trie, ""); echo "     .\n    2 ,    2.\n    3 ,  2     ,\n   1  \n"; print_r($nodes); print_r(node_traverse($trie, 0)); yatrie_free($trie); 

Nous exécutons dans la console:

 php -d extension=modules/libtrie.so yatrie_test.php 

Voici ce que vous devriez obtenir:



Nous prenons le code source de l'extension d'ici . Ne soyez pas timide et mettez des astérisques :-)

Source: https://habr.com/ru/post/fr428732/


All Articles