Cómo hacer que la extensión en PHP7 sea más difícil que "hola, mundo", y no enrojecer. Parte 2

Resumen de la primera parte


En la primera parte, puse una extensión en blanco, la hice funcionar correctamente en Clion IDE, escribí una función analógica, my_array_fill (), y verifiqué su operatividad en php.

Que ahora


Ahora voy a poner el código de la biblioteca libtrie en nuestra extensión.

Hablaré un poco sobre cómo puede hacer que las antiguas extensiones php5 funcionen en php7.
Además, haré algunas funciones básicas de esta biblioteca en php y comprobaré qué sucedió.

Vamos


Obtenga el código libtrie en nuestra extensión


Voy al directorio de extensiones

cd ~/Documents/libtrie/ 

Clonando el repositorio libtrie

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



Abro el archivo con el código de extensión php_libtrie.c y el archivo con el código de biblioteca libtrie/src/libtrie.c .



Usaré este último para verificar los nombres y la sintaxis de las funciones.

Las funciones creadas en php que usaré son las mismas que en la biblioteca misma.
En primer lugar, debe incluir el archivo de encabezado de la biblioteca en el código de nuestra extensión.
Escribimos en php_libtrie.c:

 #include "libtrie/src/libtrie.h" 

Función Yatrie_new


Hago la primera función que creará el árbol de prefijos. En la biblioteca se llama

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

Como puede ver en el código, la función toma 3 argumentos numéricos en la entrada y devuelve un puntero a la estructura trie_s . En pocas palabras, devuelve un enlace al árbol de prefijos creado.

Para extraer nuestro árbol de prefijos a PHP, PHP tiene un tipo de datos de recursos especial. Cuando una función se ejecuta en PHP

 fopen("filename.ext"); 

Hablando en lenguaje C, el programa le pide al sistema operativo que abra el archivo especificado, crea un puntero a este archivo, que en forma de recurso y regresa a PHP.

Haremos lo mismo con nuestro árbol.

Hagamos una función en php_libtrie.c:

Código de función
 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)); } 


Ahora necesita agregar la función creada a la matriz de funciones de extensión, de lo contrario, la función no será visible desde PHP.

 PHP_FE(yatrie_new, NULL) 



Para que se vea hermoso, agregaré una declaración de función al archivo de encabezado. Esto no es necesario, ya que nuestras funciones PHP no interactúan entre sí, pero aún así prefiero declarar todas las funciones en el archivo de encabezado.

Solo agrega las líneas:

 PHP_FUNCTION(confirm_libtrie_compiled); PHP_FUNCTION(my_array_fill); PHP_FUNCTION(yatrie_new); 
al archivo php_libtrie.h. En cualquier lugar entre:
 #ifndef PHP_LIBTRIE_H #define PHP_LIBTRIE_H 
y
 #endif /* PHP_LIBTRIE_H */ 



PHP creado destructor de recursos


La función yatrie_new () creada crea un árbol y también registra un recurso PHP. Ahora necesitamos una función que cierre el recurso creado y libere la memoria ocupada por el árbol de prefijos.

 /** * @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); } 

Como la función es interna a la matriz de funciones de extensión, no está incluida. Agregue su declaración a php_libtrie.h:



Ahora necesita registrar la función destructora creada en PHP. Esto se hace a través de una función especial de inicialización de extensión. Antes de esto, esta función simplemente devolvía el ÉXITO inmediatamente. Es necesario agregar allí el registro del destructor.

 //  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; } 



Eliminar la función de árbol creada


Así como la función fopen() tiene un par de fclose() , la función de creación de mi árbol debería tener una novia que lo equilibre.

Código
 /** * @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; } 


Agregue una función a la matriz de funciones de extensión:

 PHP_FE(yatrie_free, NULL) 

Agregue la declaración de función al archivo de encabezado:

 PHP_FUNCTION(yatrie_free); 



Como puede ver en la captura de pantalla, agregué el nombre interno del recurso en PHP al archivo de encabezado, así como las macros PHP5, que por alguna razón se eliminaron de PHP7. No los uso, pero si alguien quiere hacerlo, puede construir fácilmente la extensión PHP5 a 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)) 

Función agregar palabras en trie


Ahora hagamos la función de agregar una palabra al árbol de prefijos.


  1. Código
     /** * @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. Agregue una entrada a la matriz de funciones:

     PHP_FE(yatrie_add, NULL) 
  3. Agregue una declaración al archivo de encabezado:

     PHP_FUNCTION(yatrie_add) 

La función para generar todas las palabras del diccionario


Ahora hagamos una función que seleccionará todas las palabras del árbol de prefijos y las generará en PHP como una matriz.


  1. Código
     /** * @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. Agregue una entrada a la matriz de funciones:

     PHP_FE(node_traverse, NULL) 

  3. Agregue una declaración al archivo de encabezado:

     PHP_FUNCTION(node_traverse) 


Conjunto de extensión


Dado que la extensión ahora usa archivos de biblioteca de terceros, estos archivos también deben compilarse. Abro el archivo config.m4 y agrego los 2 archivos fuente de libtrie allí:

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


Aquí está el contenido completo del archivo después de los cambios.

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 




Ahora necesita volver a hacer el script ./configure. Corro desde el directorio raíz de la extensión:

 phpize && ./configure 

Ahora estoy construyendo la extensión:

 make 

Prueba


Para la prueba, es mejor hacer un script php para no escribir mucho en la consola. Haré esto:

 nano yatrie_test.php 

Y este es el contenido del archivo:

 <?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); 

Ejecutamos en la consola:

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

Esto es lo que debes obtener:



Tomamos el código fuente de la extensión desde aquí . No seas tímido y pon asteriscos :-)

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


All Articles