
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_type
Capí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.