
 Na 
primeira parte , são contadas coisas bastante básicas sobre a configuração de ferramentas e conceitos gerais.
A segunda parte é sobre, por assim dizer, a primeira abordagem ao projétil, idéias, planos, planos.
Neste artigo, haverá um pouco mais de explicações sobre o interope C e K / N, muitas macros, dor, desesperança e "raios de bondade". É claro que haverá um capítulo com uma história sobre conquistas (você não se elogiará ... e, como bônus, uma história sobre um fakap épico.
Isenção de responsabilidade: todos os itens a seguir são considerados no contexto da criação de uma biblioteca para PHP.
Capítulo Um Interope ingênuo
Como usar as funções K / N em C é descrito na primeira parte do ciclo. Portanto, aqui vou lhe dizer como usar as funções C em K / N.
A documentação oficial é bastante mesquinha e concisa, no entanto, para projetos simples, é suficiente.
Em resumo, você precisa criar um arquivo especial com a extensão 
.def e especificar os arquivos de cabeçalho necessários.
headers = php.h 
Em seguida, alimente-o com um programa chamado 
cinterop .
 # cinterop -def php.def -o php 
Na saída, você obterá a biblioteca libphp.klib que contém o código de bit llvm e várias meta-informações.
Em seguida, você pode usar com segurança as funções e macros descritas no arquivo de cabeçalho ( 
#define ), sem esquecer de conectar a biblioteca no estágio de compilação.
 # kotlinc -opt -produce static ${SOURCES} -l libphp.klib -o myLib 
Mas há uma nuance. E não um.
Na forma descrita acima, a biblioteca não será montada
Porque Mas porque as seguintes linhas estão presentes no php.h:
 #include "php_version.h" #include "zend.h" #include "zend_sort.h" #include "php_compat.h" #include "zend_API.h" 
Aqui deve-se notar que o llvm ainda está envolvido na compilação da biblioteca e possui a 
opção -I e o cinterop tem a 
opção -copt . Bem, você entendeu. Como resultado, esse comando é suficiente para compilar o 
php.h. # cinterop -def my.def -o myLib -I${PHP_LIB_ROOT} -copt -I${PHP_LIB_ROOT} \ -copt -I${PHP_LIB_ROOT}/main \ -copt -I${PHP_LIB_ROOT}/Zend \ -copt -I${PHP_LIB_ROOT}/TSRM 
Macros. Eu te amo e odeio! Não, eu odeio isso.
Tudo que você precisa saber sobre 
#define em termos de inter> C / K / N é
Toda macro C que se expande para uma constante é representada como propriedade Kotlin. Outras macros não são suportadas.
E então lembramos que a extensão PHP é uma macro em uma macro e aciona uma macro e tenta não chorar.
Mas nem tudo é tão ruim. Para contornar essa situação, os desenvolvedores de K / N forneceram um rolo de fita elétrica azul para anexar ao arquivo def de 
declarações personalizadas . Parece com isso (por exemplo, pegue a macro 
Z_TYPE_P )
 headers = php.h --- static inline zend_uchar __zp_get_arg_type(zval *z_value) { return Z_TYPE_P(z_value); } 
Agora, no código K / N, será possível usar a função 
__zp_get_arg_typeCapítulo Dois Configurações INI do PHP ou uma macro sub-sub.
Este é um "raio do bem" para o código fonte do PHP.
Existem 4 macros para extrair configurações:
 INI_INT(val) INI_FLT(val) INI_STR(val) INI_BOOL(val) 
Onde 
val é uma sequência com o nome da configuração.
Agora, vejamos o exemplo de 
INI_STR para ver como essa macro é definida.
 #define INI_STR(name) zend_ini_string_ex((name), sizeof(name)-1, 0, NULL) 
Já reparou na sua "falha fatal"?
Caso contrário, deixe-me dizer: esse é o 
sizeof função. Quando você usa a macro diretamente, tudo está bem:
 php_printf("The value is : %s", INI_STR("my.ini")); 
Quando você o utiliza por meio de uma função proxy de um arquivo 
.def , o carro se transforma em abóbora e 
sizeof (nome) retorna o tamanho do ponteiro. Xeque-mate Kotlin Native.
De fato, existem apenas duas opções para contornar.
- Não use macros, mas funções às quais estão anexadas.
- O invólucro de código rígido funciona para cada configuração necessária.
A primeira opção é melhor para todos que a segunda, exceto por um ponto - ninguém garante que a macro declaração não será alterada. Portanto, para o meu projeto, eu, com um sentimento de profunda insatisfação, escolhi a segunda opção.
CAPÍTULO TRÊS Depurar? Que debag?
Ato 1 - Interoperabilidade.
Em um ponto, depois de anexar a fita azul ao arquivo def de 20 funções de proxy consecutivas, recebi um erro maravilhoso.
 Exception in thread "main" java.lang.Error: /tmp/tmp399964332777824085.c:103:38: error: too many arguments to function call, expected 2, have 3 at org.jetbrains.kotlin.native.interop.indexer.UtilsKt.ensureNoCompileErrors(Utils.kt:137) at org.jetbrains.kotlin.native.interop.indexer.IndexerKt.indexDeclarations(Indexer.kt:902) at org.jetbrains.kotlin.native.interop.indexer.IndexerKt.buildNativeIndexImpl(Indexer.kt:892) at org.jetbrains.kotlin.native.interop.indexer.NativeIndexKt.buildNativeIndex(NativeIndex.kt:56) at org.jetbrains.kotlin.native.interop.gen.jvm.MainKt.processCLib(main.kt:283) at org.jetbrains.kotlin.native.interop.gen.jvm.MainKt.interop(main.kt:38) at org.jetbrains.kotlin.cli.utilities.InteropCompilerKt.invokeInterop(InteropCompiler.kt:100) at org.jetbrains.kotlin.cli.utilities.MainKt.main(main.kt:29) 

Comente sobre metade, recompile, se comentar sobre a metade do resto for repetido, coletamos ... E dado que o processo de compilação dos cabeçalhos é longo o suficiente ... (sim, parecia mais rápido do que subir uma dúzia de arquivos de origem e meticulosamente, com uma lupa, reconciliar).
O segundo "raio do bem" vai para o JetBrains.
Ato 2 - tempo de execução.
Eu recebo uma 
falha de segmentação em tempo de execução. Bem, isso acontece. Estou subindo no depurador. Ummm ... STA?
 Program received signal SIGSEGV, Segmentation fault. kfun:kotlinx.cinterop.toKString@kotlinx.cinterop.CPointer<kotlinx.cinterop.ByteVarOf<kotlin.Byte>>.()kotlin.String () at /opt/buildAgent/work/4d622a065c544371/Interop/Runtime/src/main/kotlin/kotlinx/cinterop/Utils.kt:402 402 /opt/buildAgent/work/4d622a065c544371/Interop/Runtime/src/main/kotlin/kotlinx/cinterop/Utils.kt: No such file or directory. 
Capítulo Quatro Coloquei chá no seu chá para que você possa tomar chá enquanto bebe.
Aqui é necessário contar como funciona essa porcaria que eu faço.
Você escreve uma DSL descrevendo a futura extensão PHP, escreve código K / N com a implementação de funções, classes e métodos, executa o 
make e, milagrosamente, obtém uma biblioteca pronta que pode ser conectada ao PHP.
A montagem pode ser dividida em 4 etapas:
- Criando uma camada entre C e K / N (a mesma cinterop)
- Geração de código da extensão C
- Compilando uma Biblioteca com Lógica
- Compilando a biblioteca de destino
O objetivo é adicionar a capacidade de criar instâncias de uma classe PHP no código K / N. Por exemplo, para que a classe possa definir o método 
getInstance() . E eu quero fazer com que seja conveniente de usar.
Em C, esse problema é resolvido uma ou duas vezes.
 zval *obj = malloc(sizeof(zval)); object_init_ex(obj, myClass); 
Parece simples - pegue-o e transfira-o para K / N, mas aqui está o 
myClass ...
Mas 
myClass é uma variável global do tipo 
zend_class_entry* , declarada no código C do projeto e com um nome desconhecido com antecedência.
Assista suas mãos. Você precisa compilar a biblioteca a partir do código K / N, no qual haverá uma função que precisa ter acesso ao 
myClass , definido no código C gerado, mas não compilado, a partir do qual essa função será chamada.
Por fim, a implementação dessa funcionalidade levou à adição de dois novos artefatos: 
.h e 
.kt no estágio de geração de código, complicação no estágio cinterop e no épico phakap, sobre o qual falarei no final.
Capítulo Cinco O que está em meu nome?
O Conto do Porquê:
 enum class ArgumentType { PHP_STRING, PHP_LONG, PHP_DOUBLE, PHP_NULL, ... } 
melhor que:
 enum class ArgumentType { STRING, LONG, DOUBLE, NULL, ... } 
Sim, nem é necessário explicar. É isso que 
ArgumentType.NULL se transforma no arquivo de cabeçalho da biblioteca Kotlin:
 struct { extension_kt_kref_php_extension_dsl_ArgumentType (*get)();  } NULL; 
E é assim que o gcc reage a isso.
 /root/simpleExtension/phpmodule/extension_kt_api.h:113:17: error: expected identifier or '(' before 'void' } NULL; ^ 
A cortina! Cuidado com os nomes.
O penúltimo capítulo. Você não se louvará - ninguém louvará.
De modo geral, alcancei meus objetivos. Imerso no assunto, a "estrutura" para escrever extensões PHP no Kotlin Native, em geral, está pronta. Resta acrescentar algumas funcionalidades, e não as mais críticas, e polimento.
O projeto em si e, espero, uma boa documentação, podem ser vistos 
no github .
O que posso dizer sobre K / N? Apenas bom. Escrever sobre isso é um prazer, e pequenos cardumes e rugosidade podem ser atribuídos ao fato de que ele nem saiu do berço :)
O último capítulo. Raios de bom, sem aspas.
E agora, absolutamente sério e com profundo respeito, quero agradecer aos caras do JetBrains e aos residentes do canal folgado do Kotlin Native. Você é super!
E um agradecimento especial a 
Nicholas Igotti .
Bônus Epic Fakap.
O contexto é descrito no quarto capítulo.
Na verdade, quando tudo foi adicionado a um estado em que foi compilado sem erros, surgiu um problema - durante o teste, o PHP me abriu de um lado completamente desconhecido.
 # php -dextension=./phpmodule/modules/extension.so -r "var_dump(ExampleClass::getInstance());" *RECURSION* # 
"Figas!" - Pensei, entrei no código-fonte PHP e encontrei um pedaço desse tipo.
 case IS_OBJECT: if (Z_IS_RECURSIVE_P(struc)) { PUTS("*RECURSION*\n"); return; } 
Adicionando depuração:
 printf("%u", Z_IS_RECURSIVE_P(struc)) 
levou a:
 undefined symbol: Z_IS_RECURSIVE_P in Unknown on line 0 
"Figas!" Pensei de novo.
Naquele momento, quando imaginei examinar o php.h (7.1.8) realmente usado no host linux, e não aquele que foi retirado do github do brunch principal (7.3.x), um dia passou. Em linha reta envergonhado.
Mas, como se viu, não era uma bobina.
O código de verificação de recursão correto, em todas as fases da vida do objeto que eu controlei, relatou que tudo deveria funcionar. E isso significa que você deve olhar cuidadosamente para aqueles lugares que eu não controlo. Havia exatamente uma dessas coisas - na qual meu objeto é retornado à função 
var_dump RETURN_OBJ( example_symbols()->kotlin.root.php.extension.proxy.objectToZval( example_symbols()->kotlin.root.exampleclass.getInstance() ) ) 
Abra a macro RETURN_OBJ até o final. Tire os monitores nervosos e grávidos!
 1) RETURN_OBJ(r) 2) { RETVAL_OBJ(r); return; } 3) { ZVAL_OBJ(return_value, r); return; } 4) { do { zval *__z = (return_value); Z_OBJ_P(__z) = (r); Z_TYPE_INFO_P(__z) = IS_OBJECT_EX; } while (0); return; } 5) { do { zval *__z = (return_value); Z_OBJ(*(__z)) = (r); Z_TYPE_INFO(*(__z)) = (IS_OBJECT | (IS_TYPE_REFCOUNTED << Z_TYPE_FLAGS_SHIFT)); } while (0); return; } 6) { do { zval *__z = (return_value); (*(__z)).value.obj = (r); (*(__z)).u1.type_info = (8 | ((1<<0) << 8)); } while (0); return; } 
Aqui, então, senti vergonha pela segunda vez. Eu, completamente no meu olho azul, empurrei 
zval* para onde 
zend_object* esperando e passei quase dois dias procurando pelo erro.
Obrigado a todos Kotlin! :)
PS. Se existe uma alma bondosa que lê meu inglês desajeitado e corrige a documentação - não haverá limite para minha gratidão.