Como tornar a extensão no PHP7 mais difícil do que "olá, mundo" e não ficar com os olhos vermelhos. Parte 2

Resumo da primeira parte


Na primeira parte, deixei uma extensão em branco, fiz funcionar corretamente no Clion IDE, escrevi uma função análoga a my_array_fill () e verifiquei sua operabilidade no php.

O que agora


Agora vou colocar o código da biblioteca libtrie em nossa extensão.

Falarei um pouco sobre como você pode fazer com que extensões antigas do php5 funcionem no php7.
Além disso, farei algumas funções básicas desta biblioteca em php e verificarei o que aconteceu.

Vamos lá


Obtenha código libtrie em nossa extensão


Eu vou para o diretório de extensão

cd ~/Documents/libtrie/ 

Clonando o repositório libtrie

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



Abro o arquivo com o código de extensão php_libtrie.c e o arquivo com o código da biblioteca libtrie/src/libtrie.c .



Usarei o último para verificar os nomes e a sintaxe das funções.

As funções criadas no php que utilizarei são as mesmas da própria biblioteca.
Primeiro de tudo, você precisa incluir o arquivo de cabeçalho da biblioteca no código da nossa extensão.
Escrevemos em php_libtrie.c:

 #include "libtrie/src/libtrie.h" 

Função Yatrie_new


Eu faço a primeira função que criará a árvore de prefixos. Na biblioteca é chamado

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

Como você pode ver no código, a função pega 3 argumentos numéricos na entrada e retorna um ponteiro para a estrutura trie_s . Simplificando, retorna um link para a árvore de prefixos criada.

Para inserir nossa árvore de prefixos no PHP, o PHP possui um tipo de dados de recurso especial. Quando uma função é executada em PHP

 fopen("filename.ext"); 

Falando na linguagem C, o programa solicita ao sistema operacional que abra o arquivo especificado, cria um ponteiro para esse arquivo, que na forma de um recurso e retorna para o PHP fora.

Faremos o mesmo com a nossa árvore.

Vamos criar uma função em php_libtrie.c:

Código de função
 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)); } 


Agora você precisa adicionar a função criada à matriz de funções de extensão, caso contrário, a função não será visível no PHP.

 PHP_FE(yatrie_new, NULL) 



Para torná-la bonita, adicionarei uma declaração de função ao arquivo de cabeçalho. Isso não é necessário, pois nossas funções PHP não interagem umas com as outras, mas ainda prefiro declarar todas as funções no arquivo de cabeçalho.

Basta adicionar as linhas:

 PHP_FUNCTION(confirm_libtrie_compiled); PHP_FUNCTION(my_array_fill); PHP_FUNCTION(yatrie_new); 
para o arquivo php_libtrie.h. Em qualquer lugar entre:
 #ifndef PHP_LIBTRIE_H #define PHP_LIBTRIE_H 
e
 #endif /* PHP_LIBTRIE_H */ 



Destruidor de recursos criado pelo PHP


A função yatrie_new () criada cria uma árvore e também registra um recurso PHP. Agora precisamos de uma função que feche o recurso criado e libere a memória ocupada pela árvore de prefixos.

 /** * @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 a função é interna à matriz de funções de extensão, ela não está incluída. Adicione a declaração dela ao php_libtrie.h:



Agora você precisa registrar a função destruidora criada no PHP. Isso é feito através de uma função especial de inicialização de extensão. Antes disso, essa função simplesmente retornava SUCESSO imediatamente. É necessário adicionar lá o registro do destruidor.

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



Excluir função de árvore criada


Assim como a função fopen() possui algumas funções fclose() , minha função de criação de árvore deve ter uma namorada que a 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; } 


Adicione uma função à matriz de funções de extensão:

 PHP_FE(yatrie_free, NULL) 

Adicione a declaração da função ao arquivo de cabeçalho:

 PHP_FUNCTION(yatrie_free); 



Como você pode ver na captura de tela, adicionei o nome interno do recurso no PHP ao arquivo de cabeçalho, bem como as macros do PHP5, que por algum motivo foram removidas do PHP7. Eu não os uso, mas se alguém quiser, você pode facilmente criar a extensão PHP5 para 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)) 

Função adicionar palavras em trie


Agora vamos fazer a função de adicionar uma palavra à árvore de prefixos.


  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. Adicione uma entrada à matriz de funções:

     PHP_FE(yatrie_add, NULL) 
  3. Adicione uma declaração ao arquivo de cabeçalho:

     PHP_FUNCTION(yatrie_add) 

A função para gerar todas as palavras do dicionário


Agora vamos criar uma função que selecionará todas as palavras da árvore de prefixos e as produzirá em PHP como uma 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. Adicione uma entrada à matriz de funções:

     PHP_FE(node_traverse, NULL) 

  3. Adicione uma declaração ao arquivo de cabeçalho:

     PHP_FUNCTION(node_traverse) 


Conjunto de extensão


Como a extensão agora usa arquivos de biblioteca de terceiros, esses arquivos também devem ser compilados. Abro o arquivo config.m4 e adiciono os 2 arquivos de origem libtrie:

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


Aqui está o conteúdo completo do arquivo após as alterações.

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 




Agora você precisa refazer o script ./configure. Eu corro a partir do diretório raiz da extensão:

 phpize && ./configure 

Agora estou construindo a extensão:

 make 

Teste


Para o teste, é melhor criar um script php para não escrever muito no console. Eu farei isso:

 nano yatrie_test.php 

E este é o conteúdo do arquivo:

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

Executamos no console:

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

Aqui está o que você deve obter:



Nós pegamos o código fonte da extensão daqui . Não seja tímido e coloque asteriscos :-)

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


All Articles