Extension PHP et Kotlin Native. Première partie, naïve

Cet article décrit l'approche la plus naïve et la plus simple pour créer une extension PHP à l'aide de Kotlin Native. J'attire votre attention sur le fait que non pas avec , mais avec usage .

Il s'agit plutôt d'une sorte de tutoriel avec une description des problèmes survenus lors du croisement d'un hérisson avec un hérisson et des moyens de les résoudre. Il n'y aura pas de révélations, mais peut-être que quelqu'un vous sera utile.

Donc, si vous êtes intéressé, alors bienvenue au chat.

Le but est d'écrire une extension avec une fonction `hello ($ name)` qui prend une chaîne et affiche `Hello, $ name!`.

Nous déciderons naïvement, comme son nom l'indique.

  1. Écrivons une fonction sur Kotlin
  2. Compiler dans la bibliothèque partagée
  3. De manière classique (en C), nous écrivons une extension qui redirigera un appel de fonction vers cette bibliothèque

Cela semble simple, mais un râteau s'est déjà caché dans l'herbe dense:

  1. Il y a un certain nombre d'exemples d'utilisation de bibliothèques C dans Kotlin, mais je n'ai rien trouvé de suffisant pour utiliser les fonctions de la bibliothèque Kotlin en C (enfin, peut-être que je regardais mal, quoi vraiment)
  2. Le compilateur kotlinc charge les dépendances au premier démarrage. Mais la malchance est ma machine virtuelle Linux dans le cloud d'entreprise. Sans, même théorique, la possibilité d'accéder à Internet.
  3. Ce râteau est plus probablement dû à un manque d'expérience en C, mais je vais vous dire comment l'éditeur de liens m'a particulièrement séduit.

Tout s'est passé sur la version 7.5 de Red Hat Enterprise Linux Server.

C'est parti!


Pour commencer, installez ... Non, pas le compilateur Kotlin. Tout d'abord, installez le JDK. Eh bien comme ça.
Installez ensuite le compilateur Kotlin. Téléchargez simplement l' archive à partir du github et décompressez-la, par exemple, dans le répertoire personnel.

Dans un monde idéal, au premier démarrage, il télécharge lui-même toutes les dépendances, mais, en l'absence d'Internet, nous procédons comme suit (une connaissance secrète est obtenue dans un jeu auprès des employés de JetBrains):

  • Nous créons n'importe quel script Kotlin simple pour qu'il y ait quelque chose à supprimer sur le compilateur à l'étape suivante
  • Nous commençons $ KOTLIN_HOME / bin / kotlinc SimpleScript.kt, attendons un peu et appuyez sur CTRL + C - c'est pour créer une structure de dossiers dans le répertoire personnel
  • Nous regardons dans le fichier $ KOTLIN_HOME / konan / konan.properties, dans la section avec notre architecture, et recherchons l'article:

dependencies.linux_x64 = \ clang-llvm-5.0.0-linux-x86-64 \ target-gcc-toolchain-3-linux-x86-64 \ libffi-3.2.1-2-linux-x86-64 

  • Nous allons dans un référentiel spécial et téléchargeons tout ce qui précède.
  • Nous ajoutons tout cela à ~ / .konan / cache (je vous rappelle que le répertoire personnel Linux est un tilde)

Maintenant, au premier démarrage, le compilateur utilisera ces distributions et ne sera pas accessible sur Internet.

Veuillez noter que les dépendances ne sont pas de très petite taille et, après les avoir installées, mon répertoire personnel est devenu 3,4 Go plus lourd. Pour ceux qui ont un volume séparé sous leurs devoirs, cela peut être critique.

Ensuite, avec le gestionnaire de paquets standard, installez php et le php-devel correspondant.
Sur ce point, les mesures préparatoires sont terminées.

Comme cela ne vaut pas la peine d'écrire des extensions PHP, nous nous en sortirons avec le code le plus court possible.

Commençons par Kotlin


hellokt.kt

 fun kt_print(string:String){ println("Hello, $string!!!") } 

Dans divers manuels et tutoriels, kotlinc ou konanc sont utilisés comme compilateurs, ce qui est quelque peu déroutant. Donc - c'est la même chose. En voici la preuve:


Compilation

 # $KOTLINC_HOME/kotlinc -opt ./hellokt.kt -o hellokt -produce dynamic 

Avec le commutateur -opt , la bibliothèque est plus petite. -produce dynamic indique au compilateur de créer une bibliothèque partagée pour la plate-forme actuelle.

Après exécution, nous aurons deux fichiers: libhellokt.so et hellokt_api.h. Bibliothèque et fichier d'en-tête pour cela. Veuillez noter que les préfixes et suffixes sont générés automatiquement et nous ne pouvons pas les influencer (probablement).

Dans notre cas, nous obtenons un tel fichier d'en-tête.

 #ifndef KONAN_HELLOKT_H #define KONAN_HELLOKT_H #ifdef __cplusplus extern "C" { #endif #ifdef __cplusplus typedef bool hellokt_KBoolean; #else typedef _Bool hellokt_KBoolean; #endif typedef char hellokt_KByte; typedef unsigned short hellokt_KChar; typedef short hellokt_KShort; typedef int hellokt_KInt; typedef long long hellokt_KLong; typedef float hellokt_KFloat; typedef double hellokt_KDouble; typedef void* hellokt_KNativePtr; struct hellokt_KType; typedef struct hellokt_KType hellokt_KType; typedef struct { /* Service functions. */ void (*DisposeStablePointer)(hellokt_KNativePtr ptr); void (*DisposeString)(const char* string); hellokt_KBoolean (*IsInstance)(hellokt_KNativePtr ref, const hellokt_KType* type); /* User functions. */ struct { struct { void (*kt_print)(const char* string); } root; } kotlin; } hellokt_ExportedSymbols; extern hellokt_ExportedSymbols* hellokt_symbols(void); #ifdef __cplusplus } /* extern "C" */ #endif #endif /* KONAN_HELLOKT_H */ 

L'accès à notre fonction kt_print se fera de cette façon.

 hellokt_symbols()->kotlin.root.kt_print(char *); 

Je vais vous parler des classes et des packages ci-dessous - il y a des nuances.

La bibliothèque est prête, allez en C


config.m4 ( comment le créer )

 PHP_ARG_ENABLE(hello, whether to enable hello support,[ --enable-hello Enable hello support]) if test "$PHP_HELLO" != "no"; then PHP_ADD_LIBRARY_WITH_PATH(hellokt, /path/to/compiled/library, HELLO_SHARED_LIBADD) PHP_NEW_EXTENSION(hello, hello.c, $ext_shared) PHP_SUBST(HELLO_SHARED_LIBADD) fi 

La première ligne est obligatoire!
La toute première chose vue dans l'exemple config.m4 ci-dessus, à part quelques commentaires, sont trois lignes utilisant PHP_ARG_WITH () et PHP_ARG_ENABLE ().
...
Chaque extension doit fournir au moins l'un ou l'autre avec le nom de l'extension, afin que les utilisateurs puissent choisir de construire l'extension ou non en PHP.

Notez que dans PHP_ADD_LIBRARY_WITH_PATH, le premier argument est le nom de la bibliothèque. Pas libhellokt, à savoir hellokt. J'ai tué deux heures avant de découvrir pourquoi ld ne peut pas trouver la bibliothèque. (ici, il est l'histoire promise de l'intimidation des linkers).

Maintenant, en fait, le code d'extension lui-même

bonjour.c

 #include "php.h" //    #include "hellokt_api.h" #define PHP_MY_EXTENSION_VERSION "1.0" #define PHP_MY_EXTENSION_EXTNAME "hello" PHP_FUNCTION(hello); static zend_function_entry hello_functions[] = { PHP_FE(hello, NULL) }; zend_module_entry hello_module_entry = { #if ZEND_MODULE_API_NO >= 20010901 STANDARD_MODULE_HEADER, #endif PHP_MY_EXTENSION_EXTNAME, hello_functions, NULL, NULL, NULL, NULL, NULL, #if ZEND_MODULE_API_NO >= 20010901 PHP_MY_EXTENSION_VERSION, #endif STANDARD_MODULE_PROPERTIES }; ZEND_GET_MODULE(hello) PHP_FUNCTION(hello) { char * name; size_t name_len; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &name_len) == FAILURE) { RETURN_NULL(); } hellokt_symbols()->kotlin.root.kt_print(name); //   efree(name); } 

Il y a pas mal d'articles sur l'écriture d'extensions PHP, et la documentation est présente, donc je me limiterai à un lien pour utiliser zend_parse_parameters - il sera en place.

Eh bien, alors tout est sur la bonne voie:

 # $PHP_PATH/phpize # ./configure --with-php-config=$PHP_PATH/php-config # make # $PHP_PATH/php -dextension=./modules/hello.so -r "echo hello('World');" Hello, World!!! 

Bonus, sur les cours et les packages


Supposons que nous voulions faire du feng shui et aveugler ce code.

 package hello.kt; public class HelloKt { fun kt_print(string: String) { println("Hello, $string!!!") } } 

Il n'y a aucun moyen de faire un appel de méthode habituel ici - vous devrez d'abord créer une classe et lui passer le premier argument à la fonction appelée.

 hellokt_kref_hello_kt_HelloKt helloKt = { 0 }; if(!helloKt.pinned){ helloKt = hellokt_symbols()->kotlin.root.hello.kt.HelloKt.HelloKt(); } hellokt_symbols()->kotlin.root.hello.kt.HelloKt.kt_print(helloKt, name); 

Et ensuite? Le rejet de la bibliothèque partagée, la minimisation de l'utilisation de C et, ce qui n'est pas une blague, un mini framework est une telle chose. Souhaitez-moi bonne chance! :)

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


All Articles