Wie man die Erweiterung in PHP7 schwieriger macht als "Hallo Welt" und nicht rotäugig wird. Teil 2

Zusammenfassung des ersten Teils


Im ersten Teil habe ich eine Erweiterung leer gemacht, sie in der Clion-IDE korrekt funktionieren lassen, eine analoge Funktion, my_array_fill (), geschrieben und ihre Funktionsfähigkeit in PHP überprüft.

Was jetzt?


Jetzt werde ich den libtrie-Bibliothekscode in unsere Erweiterung einfügen .

Ich werde ein wenig darüber sprechen, wie Sie alte PHP5-Erweiterungen in PHP7 zum Laufen bringen können.
Weiter werde ich einige Grundfunktionen aus dieser Bibliothek in PHP machen und überprüfen, was passiert ist.

Lass uns gehen


Holen Sie sich libtrie Code in unserer Erweiterung


Ich gehe in das Erweiterungsverzeichnis

cd ~/Documents/libtrie/ 

Klonen des libtrie-Repositorys

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



Ich öffne die Datei mit dem Erweiterungscode php_libtrie.c und die Datei mit dem Bibliothekscode libtrie/src/libtrie.c .



Ich werde letzteres verwenden, um die Namen und die Syntax von Funktionen zu überprüfen.

Die in PHP erstellten Funktionen, die ich verwenden werde, sind die gleichen wie in der Bibliothek selbst.
Zunächst müssen Sie die Bibliotheks-Header-Datei in den Code unserer Erweiterung aufnehmen.
Wir schreiben in php_libtrie.c:

 #include "libtrie/src/libtrie.h" 

Yatrie_new Funktion


Ich mache die erste Funktion, die den Präfixbaum erstellt. In der Bibliothek heißt es

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

Wie Sie dem Code trie_s können, verwendet die Funktion 3 numerische Argumente am Eingang und gibt einen Zeiger auf die Struktur trie_s . Einfach ausgedrückt, gibt einen Link zum erstellten Präfixbaum zurück.

Um unseren Präfixbaum in PHP zu ziehen, verfügt PHP über einen speziellen Ressourcendatentyp. Wenn eine Funktion in PHP ausgeführt wird

 fopen("filename.ext"); 

In C-Sprache fordert das Programm das Betriebssystem auf, die angegebene Datei zu öffnen, erstellt einen Zeiger auf diese Datei, die in Form einer Ressource vorliegt und außerhalb von PHP zurückgegeben wird.

Wir werden dasselbe mit unserem Baum tun.

Machen wir eine Funktion in php_libtrie.c:

Funktionscode
 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)); } 


Jetzt müssen Sie die erstellte Funktion zum Array der Erweiterungsfunktionen hinzufügen, da die Funktion sonst in PHP nicht sichtbar ist.

 PHP_FE(yatrie_new, NULL) 



Damit es schön aussieht, füge ich der Header-Datei eine Funktionsdeklaration hinzu. Dies ist nicht erforderlich, da unsere PHP-Funktionen nicht miteinander interagieren, aber ich bevorzuge es dennoch, alle Funktionen in der Header-Datei zu deklarieren.

Fügen Sie einfach die Zeilen hinzu:

 PHP_FUNCTION(confirm_libtrie_compiled); PHP_FUNCTION(my_array_fill); PHP_FUNCTION(yatrie_new); 
in die Datei php_libtrie.h. Überall zwischen:
 #ifndef PHP_LIBTRIE_H #define PHP_LIBTRIE_H 
und
 #endif /* PHP_LIBTRIE_H */ 



PHP hat einen Ressourcenzerstörer erstellt


Die erstellte Funktion yatrie_new () erstellt einen Baum und registriert auch eine PHP-Ressource. Jetzt brauchen wir eine Funktion, die die erstellte Ressource schließt und den vom Präfixbaum belegten Speicher frei macht.

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

Da die Funktion innerhalb des Arrays der Erweiterungsfunktionen liegt, ist sie nicht enthalten. Fügen Sie ihre Erklärung zu php_libtrie.h hinzu:



Jetzt müssen Sie die erstellte Destruktorfunktion in PHP registrieren. Dies erfolgt über eine spezielle Erweiterungsinitialisierungsfunktion. Zuvor hat diese Funktion einfach sofort SUCCESS zurückgegeben. Dort muss die Registrierung des Destruktors hinzugefügt werden.

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



Löschen Sie die erstellte Baumfunktion


So wie die Funktion fopen() ein paar fclose() , sollte meine fclose() eine Freundin haben, die das ausgleicht.

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


Fügen Sie dem Array der Erweiterungsfunktionen eine Funktion hinzu:

 PHP_FE(yatrie_free, NULL) 

Fügen Sie der Header-Datei die Funktionsdeklaration hinzu:

 PHP_FUNCTION(yatrie_free); 



Wie Sie im Screenshot sehen können, habe ich der Header-Datei den internen Namen der Ressource in PHP sowie die PHP5-Makros hinzugefügt, die aus irgendeinem Grund aus PHP7 entfernt wurden. Ich benutze sie nicht, aber wenn jemand mit ihnen will, können Sie einfach die PHP5-Erweiterung auf PHP7 bauen.

 #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)) 

Funktion füge Wörter in trie hinzu


Lassen Sie uns nun die Funktion ausführen, dem Präfixbaum ein Wort hinzuzufügen.


  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. Fügen Sie dem Funktionsfeld einen Eintrag hinzu:

     PHP_FE(yatrie_add, NULL) 
  3. Fügen Sie der Header-Datei eine Deklaration hinzu:

     PHP_FUNCTION(yatrie_add) 

Die Funktion zum Ausgeben aller Wörter aus dem Wörterbuch


Lassen Sie uns nun eine Funktion erstellen, die alle Wörter aus dem Präfixbaum auswählt und sie in PHP als Array ausgibt.


  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. Fügen Sie dem Funktionsfeld einen Eintrag hinzu:

     PHP_FE(node_traverse, NULL) 

  3. Fügen Sie der Header-Datei eine Deklaration hinzu:

     PHP_FUNCTION(node_traverse) 


Erweiterungsbaugruppe


Da die Erweiterung jetzt Bibliotheksdateien von Drittanbietern verwendet, müssen diese Dateien ebenfalls kompiliert werden. Ich öffne die Datei config.m4 und füge dort die 2 libtrie-Quelldateien hinzu:

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


Hier ist der vollständige Inhalt der Datei nach den Änderungen.

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 




Jetzt müssen Sie das Skript ./configure erneut ausführen. Ich starte aus dem Stammverzeichnis der Erweiterung:

 phpize && ./configure 

Jetzt baue ich die Erweiterung:

 make 

Testen


Für den Test ist es am besten, ein PHP-Skript zu erstellen, um nicht viel in die Konsole zu schreiben. Ich werde das tun:

 nano yatrie_test.php 

Und das ist der Inhalt der Datei:

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

Wir führen in der Konsole aus:

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

Folgendes sollten Sie bekommen:



Wir nehmen den Quellcode der Erweiterung von hier . Sei nicht schüchtern und setze Sternchen :-)

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


All Articles