Cara membuat ekstensi di PHP7 lebih sulit daripada "halo, dunia", dan tidak menjadi mata merah. Bagian 2

Ringkasan bagian pertama


Pada bagian pertama, saya membuat ekstensi kosong, membuatnya bekerja dengan benar di IDE Clion, menulis fungsi analog, my_array_fill (), dan memeriksa kinerjanya di php.

Apa sekarang?


Sekarang saya akan memasukkan kode libtrie library ke ekstensi kita.

Saya akan berbicara sedikit tentang bagaimana Anda dapat membuat ekstensi php5 lama berfungsi di php7.
Selanjutnya saya akan membuat beberapa fungsi dasar dari perpustakaan ini di php dan memeriksa apa yang terjadi.

Ayo pergi


Dapatkan kode libtrie di ekstensi kami


Saya pergi ke direktori ekstensi

cd ~/Documents/libtrie/ 

Mengkloning repositori libtrie

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



Saya membuka file dengan kode ekstensi php_libtrie.c dan file dengan kode perpustakaan libtrie/src/libtrie.c .



Saya akan menggunakan yang terakhir untuk memeriksa nama dan sintaks fungsi.

Fungsi-fungsi yang dibuat dalam php yang akan saya gunakan sama dengan di perpustakaan itu sendiri.
Pertama-tama, Anda perlu memasukkan file header perpustakaan dalam kode ekstensi kami.
Kami menulis di php_libtrie.c:

 #include "libtrie/src/libtrie.h" 

Fungsi Yatrie_new


Saya membuat fungsi pertama yang akan membuat pohon awalan. Di perpustakaan itu disebut

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

Seperti yang dapat Anda lihat dari kode, fungsi mengambil 3 argumen numerik pada input dan mengembalikan pointer ke struktur trie_s . Sederhananya, mengembalikan tautan ke pohon awalan yang dibuat.

Untuk menarik pohon awalan kami ke dalam PHP, PHP memiliki tipe data sumber daya khusus. Ketika suatu fungsi dieksekusi dalam PHP

 fopen("filename.ext"); 

Berbicara dalam bahasa C, program meminta sistem operasi untuk membuka file yang ditentukan, membuat pointer ke file ini, yang dalam bentuk sumber daya dan kembali ke luar ke PHP.

Kami akan melakukan hal yang sama dengan pohon kami.

Mari kita membuat fungsi di php_libtrie.c:

Kode fungsi
 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)); } 


Sekarang Anda perlu menambahkan fungsi yang dibuat ke array fungsi ekstensi, jika tidak fungsi tersebut tidak akan terlihat dari PHP.

 PHP_FE(yatrie_new, NULL) 



Agar terlihat indah, saya akan menambahkan deklarasi fungsi ke file header. Ini tidak perlu, karena fungsi PHP kami tidak berinteraksi satu sama lain, tetapi saya masih lebih suka untuk mendeklarasikan semua fungsi dalam file header.

Cukup tambahkan baris:

 PHP_FUNCTION(confirm_libtrie_compiled); PHP_FUNCTION(my_array_fill); PHP_FUNCTION(yatrie_new); 
ke file php_libtrie.h. Di mana saja di antara:
 #ifndef PHP_LIBTRIE_H #define PHP_LIBTRIE_H 
dan
 #endif /* PHP_LIBTRIE_H */ 



PHP menciptakan destruktor sumber daya


Fungsi yatrie_new () yang dibuat membuat pohon dan juga mendaftarkan sumber daya PHP. Sekarang kita membutuhkan fungsi yang akan menutup sumber daya yang dibuat dan membebaskan memori yang ditempati oleh pohon awalan.

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

Karena fungsi internal untuk berbagai fungsi ekstensi, itu tidak termasuk. Tambahkan deklarasi ke php_libtrie.h:



Sekarang Anda perlu mendaftarkan fungsi destruktor yang dibuat di PHP. Ini dilakukan melalui fungsi inisialisasi ekstensi khusus. Sebelum ini, fungsi ini langsung mengembalikan SUKSES. Perlu menambahkan pendaftaran destruktor di sana.

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



Hapus fungsi pohon yang dibuat


Sama seperti fungsi fopen() memiliki beberapa fclose() , jadi fungsi pembuatan pohon saya harus memiliki pacar yang akan menyeimbangkannya.

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


Tambahkan fungsi ke array fungsi ekstensi:

 PHP_FE(yatrie_free, NULL) 

Tambahkan deklarasi fungsi ke file header:

 PHP_FUNCTION(yatrie_free); 



Seperti yang dapat Anda lihat di tangkapan layar, saya menambahkan nama internal sumber daya dalam PHP ke file header, serta makro PHP5, yang karena alasan tertentu dihapus dari PHP7. Saya tidak menggunakannya, tetapi jika seseorang ingin menggunakannya, Anda dapat dengan mudah membangun ekstensi PHP5 ke 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)) 

Fungsi menambahkan kata dalam trie


Sekarang mari kita membuat fungsi menambahkan kata ke pohon awalan.


  1. Kode
     /** * @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. Tambahkan entri ke berbagai fungsi:

     PHP_FE(yatrie_add, NULL) 
  3. Tambahkan deklarasi ke file header:

     PHP_FUNCTION(yatrie_add) 

Fungsi untuk mengeluarkan semua kata dari kamus


Sekarang mari kita membuat fungsi yang akan memilih semua kata dari pohon awalan dan mengeluarkannya dalam PHP sebagai array.


  1. Kode
     /** * @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. Tambahkan entri ke berbagai fungsi:

     PHP_FE(node_traverse, NULL) 

  3. Tambahkan deklarasi ke file header:

     PHP_FUNCTION(node_traverse) 


Perakitan ekstensi


Karena ekstensi sekarang menggunakan file perpustakaan pihak ketiga, file-file ini juga harus dikompilasi. Saya membuka file config.m4 dan menambahkan file sumber 2 libtrie di sana:

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


Berikut adalah isi lengkap file setelah perubahan.

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 




Sekarang Anda perlu melakukan kembali skrip ./configure. Saya menjalankan dari direktori root ekstensi:

 phpize && ./configure 

Sekarang saya sedang membangun ekstensi:

 make 

Pengujian


Untuk pengujian, yang terbaik adalah membuat skrip php agar tidak banyak menulis di konsol. Saya akan melakukan ini:

 nano yatrie_test.php 

Dan ini adalah isi file:

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

Kami mengeksekusi di konsol:

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

Inilah yang harus Anda dapatkan:



Kami mengambil kode sumber ekstensi dari sini . Jangan malu dan letakkan tanda bintang :-)

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


All Articles