Erweiterung PHP und Kotlin Native. Teil eins, naiv

Dieser Artikel beschreibt den naivsten und einfachsten Ansatz zum Erstellen einer PHP-Erweiterung mit Kotlin Native. Ich mache Sie darauf aufmerksam, dass nicht mit , sondern mit Gebrauch .

Es ist eher eine Art Tutorial mit einer Beschreibung der Probleme, die beim Überqueren eines Igels mit einem Igel aufgetreten sind, und Möglichkeiten, sie zu lösen. Es wird keine Enthüllungen geben, aber vielleicht wird sich jemand als nützlich erweisen.

Also, wenn Sie interessiert sind, dann willkommen bei cat.

Das Ziel ist es, eine Erweiterung mit einer Funktion "Hallo ($ name)" zu schreiben, die eine Zeichenfolge verwendet und "Hallo, $ name!" Druckt.

Wir werden naiv entscheiden, wie der Name schon sagt.

  1. Schreiben wir eine Funktion auf Kotlin
  2. Kompilieren Sie in der gemeinsam genutzten Bibliothek
  3. Auf klassische Weise (in C) schreiben wir eine Erweiterung, die einen Funktionsaufruf an diese Bibliothek umleitet

Es scheint einfach, aber im dichten Gras hat bereits ein Rechen gelauert:

  1. Es gibt eine Reihe von Beispielen für die Verwendung von C-Bibliotheken in Kotlin, aber ich konnte nichts finden, was für die Verwendung der Funktionen der Kotlin-Bibliothek in C angemessen wäre (naja, vielleicht habe ich schlecht ausgesehen, was wirklich).
  2. Der Kotlinc-Compiler lädt beim ersten Start Abhängigkeiten. Aber das Pech ist meine virtuelle Linux-Maschine in der Unternehmens-Cloud. Ohne theoretische Fähigkeit, das Internet zu erreichen.
  3. Dieser Rake ist eher auf mangelnde Erfahrung in C zurückzuführen, aber ich werde Ihnen sagen, wie der Linker mich besonders getrollt hat.

Alles geschah unter Red Hat Enterprise Linux Server Version 7.5.

Lass uns gehen!


Installieren Sie zunächst ... Nein, nicht den Kotlin-Compiler. Installieren Sie zuerst das JDK. Gut so.
Installieren Sie dann den Kotlin-Compiler. Laden Sie einfach das Archiv vom Github herunter und entpacken Sie es beispielsweise in das Home-Verzeichnis.

In einer idealen Welt lädt er beim ersten Start alle Abhängigkeiten selbst herunter, aber in Abwesenheit des Internets gehen wir wie folgt vor (geheimes Wissen wird von JetBrains-Mitarbeitern in Ruhe erhalten):

  • Wir erstellen jedes einfache Kotlin-Skript, damit im nächsten Schritt etwas auf dem Compiler angezeigt werden kann
  • Wir starten $ KOTLIN_HOME / bin / kotlinc SimpleScript.kt, warten ein wenig und drücken STRG + C - dies dient zum Erstellen einer Ordnerstruktur im Home-Verzeichnis
  • Wir suchen in der Datei $ KOTLIN_HOME / konan / konan.properties im Abschnitt mit unserer Architektur nach dem Element:

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 

  • Wir gehen zu einem speziellen Repository und laden alle oben genannten herunter.
  • Wir fügen all dies zu ~ / .konan / cache hinzu (ich erinnere Sie daran, dass das Linux-Home-Verzeichnis eine Tilde ist)

Jetzt verwendet der Compiler beim ersten Start diese Distributionen und gelangt nicht ins Internet.

Bitte beachten Sie, dass die Abhängigkeiten nicht sehr klein sind und mein Home-Verzeichnis nach der Installation 3,4 GB schwerer wurde. Für diejenigen, die ein separates Volumen unter ihren Hausaufgaben haben, kann dies kritisch sein.

Installieren Sie anschließend mit dem Standard-Paketmanager PHP und die entsprechende PHP-Entwicklung.
Damit sind die vorbereitenden Maßnahmen beendet.

Da es sich nicht lohnt, über das Schreiben von PHP-Erweiterungen zu sprechen, werden wir mit dem kürzestmöglichen Code auskommen.

Beginnen wir mit Kotlin


hellokt.kt

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

In verschiedenen Handbüchern und Tutorials werden entweder Kotlinc oder Konanc als Compiler verwendet, was etwas verwirrend ist. Also - es ist das gleiche. Hier ist der Beweis:


Kompilieren

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

Mit dem Schalter -opt ist die Bibliothek kleiner. -produce dynamic weist den Compiler an, eine gemeinsam genutzte Bibliothek für die aktuelle Plattform zu erstellen.

Nach der Ausführung haben wir zwei Dateien: libhellokt.so und hellokt_api.h. Bibliothek und Header-Datei dafür. Bitte beachten Sie, dass Präfixe und Suffixe automatisch generiert werden und wir sie (wahrscheinlich) nicht beeinflussen können.

In unserem Fall erhalten wir eine solche Header-Datei.

 #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 */ 

Der Zugriff auf unsere Funktion kt_print erfolgt auf diese Weise.

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

Ich werde Ihnen unten über Klassen und Pakete erzählen - es gibt Nuancen.

Die Bibliothek ist fertig, gehen Sie zu C.


config.m4 ( wie man es erstellt )

 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 

Die erste Zeile ist erforderlich!
Das allererste, was im obigen Beispiel config.m4 zu sehen ist, sind, abgesehen von einigen Kommentaren, drei Zeilen mit PHP_ARG_WITH () und PHP_ARG_ENABLE ().
...
Jede Erweiterung sollte mindestens die eine oder andere mit dem Erweiterungsnamen versehen, damit Benutzer auswählen können, ob die Erweiterung in PHP integriert werden soll oder nicht.

Beachten Sie, dass in PHP_ADD_LIBRARY_WITH_PATH das erste Argument der Name der Bibliothek ist. Nicht libhellokt, nämlich hallokt. Ich habe zwei Stunden getötet, bis ich herausgefunden habe, warum ich die Bibliothek nicht finden kann. (Hier ist er die versprochene Geschichte über Linker-Mobbing).

Nun tatsächlich der Erweiterungscode selbst

hallo.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); } 

Es gibt einige Artikel über das Schreiben von PHP-Erweiterungen, und die Dokumentation ist vorhanden. Daher beschränke ich mich auf einen Link zur Verwendung von zend_parse_parameters - er wird vorhanden sein.

Nun, dann ist alles auf dem gerollten Weg:

 # $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, über Klassen und Pakete


Angenommen, wir wollten Feng Shui machen und diesen Code blind machen.

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

Es gibt hier keine Möglichkeit, einen normalen Methodenaufruf durchzuführen. Sie müssen zuerst eine Klasse erstellen und das erste Argument an die aufgerufene Funktion übergeben.

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

Was weiter? Ablehnung der gemeinsam genutzten Bibliothek, Minimierung der Verwendung von C und, was zum Teufel nicht scherzt, eines Mini-Frameworks - solche Dinge. Wünsch mir viel Glück! :) :)

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


All Articles