
- تركيب وتكوين الأدوات.
- كتابة
helloWorld()
في Kotlin Native وتجميعها في مكتبة مشتركة. - الوصول إلى هذه الوظيفة من رمز PHP الملحق C.
في هذه المقالة ، سأتحدث عن إنشاء أدوات لكتابة امتداد PHP دون الحاجة إلى لمس C ، حصريًا على K / N.
من يهتم - مرحبا بكم في القطط.
من يقرأ ليس مهتمًا ، ولكنه يريد فقط رؤيته - مرحبًا بك في
githubفي البداية ، أود أن أقول شكراً جزيلاً لنيكولاي إيغوتي على إجابات سريعة وعالية الجودة لأسئلتي ، أحيانًا سخيفة وساذجة ، في قناة Kotlin Native Slack.قم فورًا بإجراء حجز لا أدعي أنه أنشأ إطار عمل كامل (ربما لاحقًا) ، لذلك سنحد من الوظائف بهذه الطريقة:
- إنشاء وظائف يمكن استدعاؤها من كود PHP.
- تعريف الثوابت.
- نحن نعمل فقط مع أنواع PHP البسيطة:
string
، boolean
، int
، float
(و null
). لا توجد صفائف أو كائنات أو موارد أو عمليات نقل حسب المرجع ، إلخ. - سأخبرك لماذا أدناه.
تفاصيل تطوير امتدادات PHP هي أن جميع رموز الأداة المساعدة والتواصل مع
zend engine
مكتوبة على وحدات الماكرو. من ناحية ، فإنه يسهل إلى حد كبير كتابة الامتدادات بلغة C ، ومن ناحية أخرى ، فإنه يجعل من الصعب جدًا القيام بنفس الشيء في جميع لغات البرمجة الأخرى.
مع هذه المقدمة ، كان الحل الأكثر وضوحًا هو استخدام الكودريناريوم. وبالنظر إلى أن Kotlin يوفر إمكانات واسعة جدًا لإنشاء DSLs ، يمكن جعل عملية وصف بنية التمديد بسيطة وبديهية.
من أجل بناء مكتبة الامتداد بطريقة كلاسيكية (phpize ، تكوين ، صنع) ، هناك حاجة إلى صنفين على الأقل - رمز الامتداد في C وملف
config.m4
.
سيناريو الاستخدام سيكون كما يلي:
- باستخدام DSL ، نصف الامتداد.
- نكتب تنفيذ الوظائف على K / N.
- وفقًا للوصف ، نقوم بإنشاء
extension.c
و config.m4
. extencion.c
الشفرة في extencion.c
مع extencion.c
الوظائف. - وفقًا للوصف ، نقوم بإنشاء
constants.kt
، مما يسمح لنا باستخدام الثوابت المحددة في وظائفنا على K / N. - نقوم بتجميع كود K / N في مكتبة ثابتة.
- ضع كل ذلك معًا وقم بتجميعه في مكتبة ملحق.
دعنا نذهب!
لتنفيذ خطتنا ، نحتاج إلى الحصول على شيء مثل هذا الهيكل:
(, ) 1 2 ... 1(, ) 1 2 ... 1 ...
أعتقد أنه لن يكون من الصعب على أي شخص يعمل مع Kotlin كتابة DSL المناسب. بالنسبة للبقية ، هناك عدد كبير من المقالات المتخصصة حيث يتم تغطية هذا الموضوع بتفصيل أكبر بكثير مما لو حاولت القيام بذلك كجزء من هذه المقالة.
الخطوة التالية هي تحويل DSL إلى التحف الضرورية. للقيام بذلك ، سنكتب مولدًا على نفس K / N ، ونجمع ملفًا قابلاً للتنفيذ منه ومن DSL الخاص بنا ونقوم بتشغيله - voila! الحل ليس الأكثر أناقة ، ولكن لم يحدث لي شيء أكثر بساطة وموثوقية حتى الآن.
حسنًا ، كل شيء بسيط - نقوم بتجميع المكتبة بوظائف وجمع الامتداد بطريقة منتظمة ، بما في ذلك هناك.
لسهولة الاستخدام ، يتم إخفاء كل السحر مع الترجمة في برنامج نصي.ما الذي جاء منها
مثال على الوصف والرمز الذي تم إنشاؤه للملحق البسيط الموضح في DSL (
لفهم أفضل ، يتم تقديم جميع الوسائط في شكل مسمى ).
konfigure.kt - ملحقات DSL import php.extension.dsl.* val dsl = extension(name = "example", version = "0.1") { constant(name = "HELLO_EN", value = "Hello") constant(name = "HELLO_ES", value = "Hola") constant(name = "HELLO_RU", value = "") function(name = "hello", returnType = ArgumentType.STRING) { arg(type = ArgumentType.STRING, name = "name") arg(type = ArgumentType.STRING, name = "lang", optional = true) } } fun main(args: Array<String>) = dsl.make()
example.kt - تنفيذ الوظائف fun hello(name: String, lang: String?) = "${if (lang ?: "" == "") HELLO_EN else lang} $name!!!\n"
لاحظ الخوارزمية الغريبة لتحديد قيمة `lang`. هذا بسبب وجود خطأ في الإصدار الحالي من K / N ، والذي لا يسمح بتمرير متغير غير مهيأ من النوع `char *` كوسيطة من C. - يجب عليك تمرير سلسلة فارغة.
config.m4 - ملف تم إنشاؤه PHP_ARG_ENABLE(example, whether to enable example support,[ --enable-example Enable hello support]) if test "$PHP_EXAMPLE" != "no"; then PHP_ADD_INCLUDE(.) PHP_ADD_LIBRARY_WITH_PATH(example_kt, ., EXAMPLE_SHARED_LIBADD) PHP_SUBST(EXAMPLE_SHARED_LIBADD) PHP_NEW_EXTENSION(example, example.c, $ext_shared) fi
ملف example_generated_constants.kt - تم إنشاؤه مع ثوابت Kotlin const val HELLO_EN = "Hello" const val HELLO_ES = "Hola" const val HELLO_RU = ""
إنشاء ملف example.c برمز C #include "php.h" #include "example_kt_api.h" PHP_FUNCTION(hello); static zend_function_entry example_functions[] = { PHP_FE(hello, NULL) {NULL,NULL,NULL} }; PHP_MINIT_FUNCTION(example); zend_module_entry example_module_entry = { #if ZEND_MODULE_API_NO >= 20010901 STANDARD_MODULE_HEADER, #endif "example", example_functions, PHP_MINIT(example), NULL, NULL, NULL, NULL, #if ZEND_MODULE_API_NO >= 20010901 "0.1", #endif STANDARD_MODULE_PROPERTIES }; ZEND_GET_MODULE(example) PHP_MINIT_FUNCTION(example) { REGISTER_STRING_CONSTANT("HELLO_EN", "Hello", CONST_CS|CONST_PERSISTENT); REGISTER_STRING_CONSTANT("HELLO_ES", "Hola", CONST_CS|CONST_PERSISTENT); REGISTER_STRING_CONSTANT("HELLO_RU", "", CONST_CS|CONST_PERSISTENT); return SUCCESS; } PHP_FUNCTION(hello){
حول لماذا أنواع بسيطة فقط
لأنهم من واحد إلى واحد تم تعيينهم لأنواع Kotlin الأصلية. حتى الآن ، ينفذ المشروع ، في الواقع ، يتداخل في اتجاه واحد فقط ، أي استدعاء وظائف K / N من C. لمعالجة الأنواع المعقدة ، مثل
zend_class_entry
أو
zend_class_entry
أو
zend_fcall_info
، تحتاج إلى استيراد الهياكل المقابلة في مشروع K / N وكتابة الأغلفة المناسبة للعمل معها ، وهناك أيضًا جميع وحدات الماكرو ، وما إلى ذلك.
مرطبان مع قطران. يتم إرفاق ملعقة.
- توثيق Kotlin الأصلي. يبدو أن هناك ، ولكن ... حتى الآن ، أكثر الوسائل الموثوقة للدراسة هي قراءة المصدر.
- حجم الامتداد الناتج ليس صغيرًا. للحصول على المثال أعلاه ، تم الحصول على مكتبة حوالي 500 كيلو بايت.
- ليس عليك حتى أن تأمل في أن تنتهي الامتدادات المكتوبة بلغة K / N في مكتبة ملحقات PHP. يتم الحصول على المنتج ، إذا جاز التعبير ، للاستخدام الداخلي فقط.
ما هي الخطوة التالية
نفذ كل ما هو موصوف في قسم "حول لماذا الأنواع البسيطة فقط".
مرة أخرى ،
رابط إلى المستودع .
شكرا لكم على اهتمامكم ، أتمنى لي حظا سعيدا :)