
- Instalaci贸n y configuraci贸n de herramientas.
- Escribir la funci贸n
helloWorld()
en Kotlin Native y compilarla en una biblioteca compartida. - Acceda a esta funci贸n desde el c贸digo C de la extensi贸n PHP.
En este art铆culo, hablar茅 sobre la creaci贸n de herramientas para escribir una extensi贸n PHP sin tener que tocar C, exclusivamente en K / N.
A qui茅n le importa, bienvenido a cat.
Quien lee no est谩 interesado, pero solo quiere verlo - bienvenido a
githubAl principio, quiero agradecer a Nikolai Igotti por las respuestas r谩pidas y de alta calidad a mis preguntas, a veces tontas e ingenuas, en el canal de Kotlin Native.Inmediatamente haga una reserva que no pretendo crear un marco completo (tal vez m谩s adelante), por lo tanto, limitaremos la funcionalidad de esta manera:
- Creaci贸n de funciones que se pueden invocar desde c贸digo PHP.
- Definici贸n de constantes.
- Operamos solo con tipos PHP simples:
string
, boolean
, int
, float
(y null
). Sin matrices, objetos, recursos, transferencias por referencia, etc. - Te dir茅 por qu茅 a continuaci贸n.
Lo espec铆fico del desarrollo de extensiones PHP es que casi todo el c贸digo de utilidad y la comunicaci贸n con el
zend engine
escritos en macros. Por un lado, facilita enormemente la escritura de extensiones en C y, por otro lado, hace que sea muy dif铆cil hacer lo mismo en todos los dem谩s lenguajes de programaci贸n.
Con tal introducci贸n, la soluci贸n m谩s obvia era usar el coderinario. Y, dado que Kotlin ofrece posibilidades muy amplias para crear DSL, el proceso de describir la estructura de extensi贸n puede hacerse simple e intuitivo.
Para construir la biblioteca de extensiones de una manera cl谩sica (phpize, configure, make), se necesitan al menos dos artefactos: el c贸digo de extensi贸n en C y el archivo
config.m4
.
El escenario de uso ser谩 as铆:
- Usando DSL, describimos la extensi贸n.
- Escribimos la implementaci贸n de funciones en K / N.
- Seg煤n la descripci贸n, generamos
extension.c
y config.m4
. El c贸digo en extencion.c
se ocupar谩 de la representaci贸n banal de llamadas a funciones. - De acuerdo con la descripci贸n, generamos
constants.kt
, que nos permite usar las constantes dadas en nuestras funciones en K / N. - Compilamos c贸digo K / N en una biblioteca est谩tica.
- Poner todo junto y compilarlo en una biblioteca de extensiones.
Vamos!
Para implementar nuestro plan, necesitamos obtener algo como esta estructura:
(, ) 1 2 ... 1(, ) 1 2 ... 1 ...
Creo que no ser铆a dif铆cil para cualquiera que trabaje con Kotlin escribir el DSL apropiado. Por lo dem谩s, hay una gran cantidad de art铆culos especializados en los que este tema se trata con mucho m谩s detalle que si trato de hacer esto como parte de este art铆culo.
El siguiente paso es convertir este DSL en los artefactos necesarios. Para hacer esto, escribiremos un generador en el mismo K / N, compilaremos un archivo ejecutable a partir de 茅l y nuestro DSL y lo ejecutaremos, 隆listo! La soluci贸n no es la m谩s elegante, pero a煤n no se me ha ocurrido nada m谩s simple y confiable.
Bueno, entonces todo es simple: compilamos la biblioteca con funciones y recopilamos la extensi贸n de forma regular, incluy茅ndola all铆.
Para facilitar su uso, toda la magia con la compilaci贸n est谩 oculta en un script de shell.Lo que vino de eso
Un ejemplo de la descripci贸n y el c贸digo generado para la extensi贸n simple descrita en este DSL (
para una mejor comprensi贸n, todos los argumentos se dan en una forma con nombre ).
konfigure.kt - Extensiones 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 - Implementando funciones fun hello(name: String, lang: String?) = "${if (lang ?: "" == "") HELLO_EN else lang} $name!!!\n"
Tenga en cuenta el extra帽o algoritmo para determinar el valor de `lang`. Esto se debe a un error en la versi贸n actual de K / N, que no permite pasar una variable no inicializada de tipo `char *` como argumento de C. - tiene que pasar una cadena vac铆a.
config.m4 - archivo generado 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 - archivo generado con constantes de Kotlin const val HELLO_EN = "Hello" const val HELLO_ES = "Hola" const val HELLO_RU = ""
ejemplo.c: archivo generado con c贸digo 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){
Acerca de por qu茅 solo los tipos simples
Porque est谩n asignados uno a uno a los tipos nativos de Kotlin. Hasta la fecha, el proyecto implementa, de hecho, interoperabilidad solo en una direcci贸n, es decir llamando a funciones K / N desde C. Para procesar tipos complejos, como
zend_value
,
zend_class_entry
o
zend_fcall_info
, debe importar las estructuras correspondientes en el proyecto K / N y escribir los contenedores apropiados para trabajar con ellos, y tambi茅n est谩n todas las macros, etc.
Tarro con alquitr谩n. Se adjunta una cuchara.
- Documentaci贸n nativa de Kotlin. Parece estar all铆, pero ... Hasta ahora, el medio m谩s confiable de estudiar es leer la fuente.
- El tama帽o de la extensi贸n resultante no es tan peque帽o. Para el ejemplo anterior, se obtiene una biblioteca de aproximadamente 500 KB.
- Ni siquiera tiene que esperar que las extensiones escritas en K / N terminen en la biblioteca de extensiones PHP. El producto se obtiene, por as铆 decirlo, solo para uso interno.
Que sigue
Implemente todo lo que se describe en la secci贸n "Acerca de por qu茅 solo los tipos simples".
Una vez m谩s, un
enlace al repositorio .
Gracias por su atenci贸n, des茅enme buena suerte :)