Extensão PHP e Kotlin Native. Parte um, ingênua

Este artigo discute a abordagem mais ingênua e fácil de criar uma extensão PHP usando o Kotlin Native. Chamo sua atenção para o fato de que não com , mas com o uso .

É um tipo de tutorial com uma descrição dos problemas que surgiram ao cruzar um ouriço com um ouriço e maneiras de resolvê-los. Não haverá revelações, mas talvez alguém seja útil.

Então, se estiver interessado, então seja bem-vindo ao gato.

O objetivo é escrever uma extensão com uma função `hello ($ name)` que pegue uma string e imprima `Hello, $ name!`.

Nós decidiremos ingenuamente, como o nome diz.

  1. Vamos escrever uma função no Kotlin
  2. Compilar na biblioteca compartilhada
  3. Da maneira clássica (em C), escrevemos uma extensão que redirecionará uma chamada de função para esta biblioteca

Parece simples, mas um ancinho já estava à espreita na grama densa:

  1. Existem vários exemplos de uso de bibliotecas C no Kotlin, mas não encontrei nada adequado para usar as funções da biblioteca Kotlin no C (bem, talvez eu estivesse parecendo mal, o que realmente)
  2. O compilador kotlinc carrega dependências na primeira vez em que é iniciado. Mas a má sorte é minha máquina virtual Linux na nuvem corporativa. Sem, mesmo teórica, a capacidade de acessar a Internet.
  3. Esse rake é mais provável devido à falta de experiência em C, mas vou lhe contar como o vinculador me trollou notavelmente.

Tudo aconteceu no Red Hat Enterprise Linux Server versão 7.5.

Vamos lá!


Para começar, instale ... Não, não o compilador Kotlin. Primeiro, instale o JDK. Bem assim.
Em seguida, instale o compilador Kotlin. Basta baixar o arquivo no github e descompactá-lo, por exemplo, no diretório inicial.

Em um mundo ideal, no primeiro começo, ele baixa todas as dependências, mas, na ausência da Internet, procedemos da seguinte maneira (o conhecimento secreto é obtido com folga dos funcionários da JetBrains):

  • Criamos qualquer script Kotlin simples para que haja algo a ser destacado no compilador na próxima etapa
  • Começamos $ KOTLIN_HOME / bin / kotlinc SimpleScript.kt, espere um pouco e pressione CTRL + C - isto é para criar uma estrutura de pastas no diretório inicial
  • Examinamos o arquivo $ KOTLIN_HOME / konan / konan.properties, na seção com nossa arquitetura, e procuramos o item:

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 

  • Vamos a um repositório especial e baixamos todos os itens acima.
  • Adicionamos tudo isso ao ~ / .konan / cache (lembro que o diretório inicial do Linux é um til)

Agora, no primeiro início, o compilador usará essas distribuições e não entrará na Internet.

Observe que as dependências não são muito pequenas e, após instalá-las, meu diretório pessoal ficou 3,4 GB mais pesado. Para aqueles que têm um volume separado sob a lição de casa, isso pode ser crítico.

Depois, com o gerenciador de pacotes padrão, instale o php e o php-devel correspondente.
Sobre isso, as medidas preparatórias terminaram.

Como não vale a pena falar em escrever extensões PHP, seguiremos com o menor código possível.

Vamos começar com Kotlin


hellokt.kt

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

Em vários manuais e tutoriais, o kotlinc ou o konanc são usados ​​como compiladores, o que é um pouco confuso. Então - é a mesma coisa. Aqui está a prova:


Compilando

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

Com a opção -opt , a biblioteca é menor. -produce dynamic diz ao compilador para fazer uma biblioteca compartilhada para a plataforma atual.

Após a execução, teremos dois arquivos: libhellokt.so e hellokt_api.h. Biblioteca e arquivo de cabeçalho para ele. Observe que prefixos e sufixos são gerados automaticamente e não podemos influenciá-los (provavelmente).

No nosso caso, obtemos um arquivo de cabeçalho.

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

O acesso à nossa função kt_print será assim.

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

Vou falar sobre aulas e pacotes abaixo - existem nuances.

A biblioteca está pronta, vá para C


config.m4 ( como criá-lo )

 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 

A primeira linha é necessária!
A primeira coisa vista no exemplo config.m4 acima, além de alguns comentários, são três linhas usando PHP_ARG_WITH () e PHP_ARG_ENABLE ().
...
Cada extensão deve fornecer pelo menos uma ou outra com o nome da extensão, para que os usuários possam optar por criar ou não a extensão no PHP.

Observe que no PHP_ADD_LIBRARY_WITH_PATH, o primeiro argumento é o nome da biblioteca. Não libhellokt, ou seja, hellokt. Eu matei duas horas até descobrir por que ld não consegue encontrar a biblioteca. (aqui está a história prometida sobre o bullying de vinculador).

Agora, de fato, o próprio código de extensão

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

Existem alguns artigos sobre como escrever extensões PHP, e a documentação está presente, então vou me limitar a um link para usar zend_parse_parameters - ele estará no lugar.

Bem, então tudo está no caminho rolado:

 # $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!!! 

Bônus, sobre aulas e pacotes


Suponha que desejássemos fazer o feng shui e cegar esse código.

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

Não há como fazer uma chamada de método usual aqui - você primeiro terá que criar uma classe e passar o primeiro argumento para a função chamada.

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

O que vem a seguir? Rejeição da biblioteca compartilhada, minimizando o uso de C e, o que diabos não está brincando, uma mini estrutura é uma coisa dessas. Deseje-me boa sorte! :)

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


All Articles