كيفية جعل التمديد في PHP7 أكثر صعوبة من "مرحبًا بالعالم" ، وألا تصبح عيونًا حمراء. الجزء الأول

لماذا؟


أنا أكتب هذه المقالة بحيث المسار الذي سلكته ما لا يقل عن عام واحد ، يمكن للقارئ المشي في بضع ساعات. كما أظهرت تجربتي الشخصية ، فإن البرمجة فقط في لغة C أسهل قليلاً من عمل امتداد جدي لعمل PHP. هنا سأخبرك بأكبر قدر ممكن حول كيفية إنشاء ملحق باستخدام مثال مكتبة libtrie التي تنفذ شجرة البادئة ، والمعروفة باسم trie. سأكتب وأقوم بتنفيذ الإجراءات الموضحة في وقت واحد على نظام Lubuntu 18.04 المثبت حديثًا.

دعنا نبدأ.

تثبيت البرنامج


فب


  1. أولاً نضع حزمة php7.2-dev ، فيه نص phpize المطلوب لبناء الامتداد. بالإضافة إلى ذلك ، سنحتاج إلى إصدار عمل من php ، والذي سنقوم بفحص امتداده. سيؤدي تثبيت هذه الحزمة إلى سحب عدد من الحزم التابعة ، نضع كل ما يتم تقديمه.

    sudo apt install php7.2-dev 

  2. ننتقل إلى موقع php.net ، وننتقل إلى قسم التنزيلات وسحب رابط الأرشيف بأحدث إصدار ثابت من php ، والآن هو الإصدار 7.2.11.
    تنزيل أرشيف مصدر php:

     cd /tmp && wget http://it2.php.net/get/php-7.2.11.tar.gz/from/this/mirror -O php7.tar.gz 

  3. الآن قم بفك الأرشيف لأنفسنا:

     sudo tar -xvf php7.tar.gz -C /usr/local/src 


محرري التعليمات البرمجية


عادة ما أستخدم محررين كود. geany بسيط وسريع و nerdy جميلة ، ولكن clion متقدمة للغاية من JetBrains. تثبيت Geany من اللفت أوبونتو القياسية.

 sudo apt install geany 

قم بتنزيل Clion من موقع JetBrains الرسمي:

 cd ~/Downloads && wget https://download.jetbrains.com/cpp/CLion-2018.2.5.tar.gz -O clion.tar.gz 

 sudo tar -xvf clion.tar.gz -C /usr/share 

لنقم بإنشاء ارتباط لجعل تشغيل clion من وحدة التحكم مناسبًا.

 sudo ln -s /usr/share/clion-2018.2.5/bin/clion.sh /usr/bin/clion 

بعد الإطلاق الأول ، ستقوم clion نفسها بإنشاء اختصارات لنفسها من قائمة LXpanel shell ، ولكن في المرة الأولى التي تحتاج فيها إلى تشغيلها يدويًا.

 # clion 

إنشاء ملحق


هنا لدينا 3 خيارات على الأقل:

  1. خذ القرص القياسي الخام من مصادر php التي قمنا بتنزيلها.
  2. رأيت القرص القياسي قليلاً باستخدام نص خاص ext_skel
  3. احصل على قرص جيد من الحد الأدنى من هنا .

يعجبني الخيار الثالث أكثر ، ولكني سأستخدم الخيار الثاني ، في حالة الفشل ، لتقليل عدد الأماكن التي قد أكون مخطئًا فيها. على الرغم من أن اختيار فراغ من المطورين لا يزال من دواعي سروري :-)

  1. دعنا نذهب إلى الدليل مع ملحقات php القياسية.

     cd /usr/local/src/php-7.2.11/ext 

    بالإضافة إلى اسم البرنامج النصي ، يمكنك تحديد بعض معلمات الامتداد من خلال ملف proto. كل هذا لا يمكن القيام به. سأفعل كل شيء يدويًا ، لكنني سأوضح كيف يعمل proto. نحن نفعل trie ، لذلك دعونا نسمي libtrie التمديد لدينا. للعمل في الدليل / usr / local / src ، تحتاج إلى امتيازات المسؤول ، حتى لا ينتهي بكتابة sudo ، سوف أقوم بتضمين باش بأذونات مرتفعة.

     sudo bash 

  2. هنا سوف أقوم بتعيين معلمات وظيفة واحدة فقط سيقوم الملحق الملحق بتنفيذها. هذه مجرد وظيفة عرضية لتوضيح كيف يتم ذلك.

    سنفعل تناظرية كاملة للوظيفة القياسية

     array array_fill ( int $start_index , int $num , mixed $value ) 

    بناء الجملة في ملف proto بسيط للغاية ، ما عليك سوى تحديد اسم الوظيفة. سأكتب أكثر قليلاً لجعلها تبدو أكثر إفادة.

     echo my_array_fill \( int start_index , int num , mixed value \) >> libtrie.proto 

  3. الآن قم بتشغيل البرنامج النصي ext_skel ، مع إعطائه اسم الملحق وملف proto الذي أنشأناه.

     ./ext_skel --extname=libtrie --proto=./libtrie.proto 

  4. قمنا بإنشاء دليل بملحق لدينا. دعنا نذهب إلى ذلك.

     cd libtrie 


هيكل الملف ومبدأ التجميع


هيكل الملف
 config.m4 -           phpize   ./configure,       makefile. CREDITS -  ,     ,    libtrie.c -      php_libtrie.h -     config.w32 -        windows EXPERIMENTAL -  .    ,    . libtrie.php -  php      . tests -   


لإنشاء الملحق بنجاح ، تحتاج فقط إلى 3 ملفات. في القرص البسيط للملحق ، الذي ذكرته أعلاه ، هناك 3 ملفات فقط.

 config.m4 php_libtrie.h libtrie.c 

لا أحب التسمية القياسية المقبولة في php ، أنا أحب أن يتم تسمية ملفات الرأس والملفات مع نص البرنامج بنفس الاسم. لذلك إعادة تسمية
libtrie.c
في
php_libtrie.c

 mv libtrie.c php_libtrie.c 

تحرير config.m4


ملف config.m4 الافتراضي مكتظ بالمحتوى ، وفرة منه مربكة ومربكة. كما قلت ، هذا الملف مطلوب لتكوين برنامج تكوين. اقرأ المزيد عن هذا هنا .

 geany 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() #   PHP_NEW_EXTENSION(libtrie, php_libtrie.c, $ext_shared) # PHP_NEW_EXTENSION(libtrie, php_libtrie.c, $ext_shared,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1) fi 



ينشئ الماكرو الأول القدرة على تمكين وتعطيل امتدادنا عند تشغيل البرنامج النصي للتكوين الذي تم إنشاؤه.

الكتلة الثانية هي الأكثر أهمية ، فهي تحدد الملفات التي سيتم تجميعها كجزء من الامتداد الخاص بنا ، وما إذا كان الملحق سيتم توصيله ديناميكيًا عبر ملف .so ، أو ما إذا كان الملحق ثابتًا وسيتم دمجه أثناء إنشاء php. ستكون ديناميكية لدينا.
احفظ الملف.

نقوم بنسخ الملف إلى دليل المستخدم بحيث لا يعمل في وضع الجذر.

 #    exit 

نسخ:

 cp /usr/local/src/php-7.2.11/ext/libtrie ~/Documents/ -r 

وظيفة تجريبية


دعني أذكرك أننا سنفعل التناظرية الكاملة لـ array_fill (). سأعمل من خلال clion ، ولكن يمكن القيام بهذا الدليل في أي محرر. Clion جيد لأنه يسمح لك بإجراء فحص بناء الجملة الأساسي تلقائيًا ، كما أنه يدعم التنقل السريع عبر الملفات أو الوظائف عبر ctrl + click. لكي تعمل مثل هذه الانتقالات في امتدادنا ، سيتعين عليك تكوين ملف CMakeLists.txt

لكي تعمل clion بشكل صحيح ، ستحتاج إلى تثبيت نظام بناء cmake ، حيث سيتم تثبيت مجموعة من الحزم التابعة. قم بتثبيت كل هذا باستخدام الأمر:

 sudo apt install cmake 

تكوين cmake


نفتح كتالوجنا مع التوسع في clion. قم بإنشاء ملف CMakeLists.txt بالمحتويات التالية من خلال قائمة السياق عن طريق النقر على اسم الدليل الجذر في أعلى الشاشة:

 cmake_minimum_required(VERSION 3.12) project(php-ext-libtrie C) set(CMAKE_C_STANDARD 11) #   phproot,       php set(phproot /usr/local/src/php-7.2.11/) #   ,      #      clion       php include_directories(${phproot}) include_directories(${phproot}TSRM/) include_directories(${phproot}main/) include_directories(${phproot}Zend/) #    clion          add_executable(php-ext-libtrie php_libtrie.c) 



ربما شخص يعرف كيفية جعل هذا الملف أقصر بحيث يبدأ clion في فهرسة ملفات المشروع. لم أجد طريقة أقصر. إذا كان شخص يعرف ، اكتب في التعليقات.

رمز الوظيفة التجريبية


افتح php_libtrie.c مع php_libtrie.c و
حذف جميع التعليقات حتى لا تربكنا.





يتحقق Clion لمعرفة ما إذا تم الإعلان عن كافة الوظائف ووحدات الماكرو المستخدمة في التعليمات البرمجية وإلقاء خطأ إذا لم يكن كذلك. من الواضح أن مطوري PHP لا يستخدمون clion ، وإلا ربما سيفعلون شيئًا حيال ذلك. لمنع حدوث هذه الأخطاء في امتدادنا ، سنقوم بتضمين ملفات العناوين المفقودة إلينا.

لتبسيط كل شيء ، أفعل ذلك:
أقوم بنقل الكل مع الرؤوس من ملف php_libtrie.h إلى php_libtrie.h ، يبقى إدخال واحد فقط في الملف الأول:

 #include "php_libtrie.h" 



php_libtrie.h ملف php_libtrie.h على جميع الشوائب الضرورية الأخرى.



محتويات ملف الرأس الخاص بي
 #ifndef PHP_LIBTRIE_H #define PHP_LIBTRIE_H #ifdef HAVE_CONFIG_H #include "config.h" #endif #include <stdarg.h> //   va_start() #include <inttypes.h> //    //  #if defined(__GNUC__) && __GNUC__ >= 4 # define ZEND_API __attribute__ ((visibility("default"))) # define ZEND_DLEXPORT __attribute__ ((visibility("default"))) #else # define ZEND_API # define ZEND_DLEXPORT #endif # define SIZEOF_SIZE_T 8 //   ZVAL_COPY_VALUE() #ifndef ZEND_DEBUG #define ZEND_DEBUG 0 #endif //  ,      #include "php.h" #include "php_ini.h" #include "zend.h" #include "zend_types.h" //ZVAL_COPY_VALUE #include "ext/standard/info.h" #include "zend_API.h" #include "zend_modules.h" #include "zend_string.h" #include "spprintf.h" extern zend_module_entry libtrie_module_entry; ... 


إذا تم عمل كل شيء بشكل صحيح ، فسوف يظهر اختبار clion مربعًا أصفر أو أخضر في الزاوية اليمنى العليا ، مما يعني أنه لا توجد أخطاء فادحة.



انحراف نظري صغير


من أجل التمديد للعمل بشكل صحيح ، هناك شيئان ضروريان:

  1. تحتاج إلى تهيئة البنية الخاصة zend_module_entry ، والتي تحتوي على ما يلي:

     zend_module_entry libtrie_module_entry = { STANDARD_MODULE_HEADER, //  "libtrie", //  libtrie_functions, //     PHP_MINIT(libtrie), //,     PHP_MSHUTDOWN(libtrie), //   PHP_RINIT(libtrie), /* Replace with NULL if there's nothing to do at request start */ PHP_RSHUTDOWN(libtrie), /* Replace with NULL if there's nothing to do at request end */ PHP_MINFO(libtrie), // ,    php  phpinfo() PHP_LIBTRIE_VERSION, // ,     STANDARD_MODULE_PROPERTIES //    }; 

  2. قم بتهيئة نفس المصفوفة التي تحتوي على جميع وظائف امتدادنا.

    هنا ، من خلال مُجمّع ماكرو خاص PHP_FE () ، يتم تعيين أسماء جميع الوظائف في الامتداد الخاص بنا. بشكل عام ، يتم استخدام وحدات الماكرو بنشاط كبير في PHP ، وهناك الكثير من وحدات الماكرو التي تشير ببساطة إلى وحدات ماكرو أخرى ، وهذه بدورها أبعد من ذلك. عليك أن تعتاد عليه. يمكنك معرفة ما إذا كنت تمر عبر وحدات الماكرو عبر ctrl + click. clion هنا لا غنى عنه.

    تذكر ملف بروتو؟ وضعنا هناك وظيفة واحدة my_array_fill (). الآن لدينا 3 عناصر هنا:

     const zend_function_entry libtrie_functions[] = { PHP_FE(confirm_libtrie_compiled, NULL) /* For testing, remove later. */ PHP_FE(my_array_fill, NULL) PHP_FE_END /* Must be the last line in libtrie_functions[] */ }; 

    السطر الأول هو وظيفة اختبار ستظهر أن الامتداد يعمل ، والثاني هو وظيفتنا ، والثالث هو ماكرو خاص ، والذي يجب أن يكون الأخير في هذا الصفيف.

ابحث عن وظيفتنا:

 PHP_FUNCTION(my_array_fill) 

كما ترى ، تتم تهيئته أيضًا من خلال ماكرو. الشيء هو أن جميع وظائف php لا ترجع أي شيء (على وجه الدقة ، ترجع باطلة) داخل C ، ولا يمكن تغيير حججهم. في مكان ما ، إنها مريحة.

إذا نظرت إلى بنية الملف (جزء من النافذة على اليسار) ، يتم سرد جميع وظائف الملف هنا ، ولكن بالشكل الذي ستكون عليه بعد تجميع وحدات الماكرو. تظهر لقطة الشاشة أن وظيفتنا my_array_fill ستكون في الواقع zif_my_array_fill.



الحجج من أحشاء php إلى دالة C التي نحصل عليها باستخدام الماكرو. يمكن العثور على مزيد من التفاصيل حول هذا الماكرو في الملف:

 /usr/local/src/php-7.2.11/README.PARAMETER_PARSING_API 

يوجد أدناه رمز الوظيفة التناظرية مع شرح مفصل.

كود
 PHP_FUNCTION(my_array_fill) { //   ,     //    2 : // zend_execute_data *execute_data, zval *return_value //     ,      //zend_long  int64_t  x64   int32_t  x86  //      zend_long zend_long start_index; //1  , zend_long num; //2   zval *value; //   mixed ,   zval,      //    ,          if (zend_parse_parameters(ZEND_NUM_ARGS(), "llz", &start_index, &num, &value) == FAILURE) { /*     *    RETURN_    * return_value     */ RETURN_FALSE; } //   ,   -    if (num <= 0) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "argument 2 must be > 0"); //     RETURN_FALSE; } //zval *return_value  ,        //       zval,     ,  -  //   zend_long  unsigned int32. //      + -. ..   1,    3,     4  array_init_size(return_value, (uint32_t)(start_index + num)); //  ,   ,   for(zend_long i = start_index, last = start_index + num; i < last; ++i) { //   zval       add_index_zval(return_value, i, value); } //   ,      return_value return; } 




بناء واختبار ملحقات


أولاً ، قم بتشغيل phpize ، مما سيجعلنا نقوم بتهيئة الملف.

 phpize 

الآن قم بتشغيل ./configure ، والتي ستؤدي ملف makefile.

 ./configure 

أخيرًا ، قم بإجراء الأمر الذي سيجمع امتدادنا.

 make 

دعونا نتحقق مما فعلناه.

 #    php,       # modules.  -a  php      php -d extension=modules/libtrie.so -a 

ندخل في وحدة تحكم php:

 print_r(my_array_fill(50, 2, "hello, baby!")); 

استمتع بالنتيجة.



شخص ما يسأل ، أين تري هنا؟ حول الوظائف التي تنفذ عمل trie ، سأكتب في الجزء الثاني.

ترقبوا.

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


All Articles