
- Instalação e configuração de ferramentas.
- Escrevendo a função
helloWorld()
no Kotlin Native e compilando-a em uma biblioteca compartilhada. - Acesse esta função a partir do código C da extensão PHP.
Neste artigo, falarei sobre a criação de ferramentas para escrever uma extensão PHP sem precisar tocar em C, exclusivamente no K / N.
Quem se importa - bem-vindo ao gato.
Quem lê não está interessado, mas apenas quer vê-lo - bem-vindo ao
githubDesde o início, quero agradecer a Nikolai Igotti por respostas rápidas e de alta qualidade às minhas perguntas, às vezes tolas e ingênuas, no canal de folga do Kotlin Native.Faça imediatamente uma reserva que não pretendo criar uma estrutura completa (talvez mais tarde); portanto, limitaremos a funcionalidade desta maneira:
- Criando funções que podem ser chamadas a partir do código PHP.
- Definição de constantes.
- Operamos apenas com tipos simples de PHP:
string
, boolean
, int
, float
(e null
). Sem matrizes, objetos, recursos, transferências por referência, etc. - Vou lhe dizer por que abaixo.
As especificidades do desenvolvimento de extensões PHP são que quase todo o código do utilitário e a comunicação com o
zend engine
escritos em macros. Por um lado, facilita muito a escrita de extensões em C e, por outro lado, torna muito difícil fazer o mesmo em todas as outras linguagens de programação.
Com essa introdução, a solução mais óbvia foi usar o coderinarium. E, dado que o Kotlin oferece possibilidades muito amplas para a criação de DSLs, o processo de descrição da estrutura de extensão pode ser simplificado e intuitivo.
Para construir a biblioteca de extensões de maneira clássica (phpize, configure, make), são necessários pelo menos dois artefatos - o código de extensão em C e o arquivo
config.m4
.
O cenário de uso será assim:
- Usando DSL, descrevemos a extensão.
- Escrevemos a implementação de funções em K / N.
- De acordo com a descrição, geramos
extension.c
e config.m4
. O código em extencion.c
tratará da proxy banal de chamadas de função. - De acordo com a descrição, geramos
constants.kt
, o que nos permite usar as constantes dadas em nossas funções em K / N. - Nós compilamos o código K / N em uma biblioteca estática.
- Reunindo tudo e compilando-o em uma biblioteca de extensão.
Vamos lá!
Para implementar nosso plano, precisamos obter algo como esta estrutura:
(, ) 1 2 ... 1(, ) 1 2 ... 1 ...
Eu acho que não seria difícil para quem trabalha com Kotlin escrever a DSL apropriada. Quanto ao resto, há um grande número de artigos especializados nos quais esse tópico é abordado com muito mais detalhes do que se eu tentasse fazer isso como parte deste artigo.
A próxima etapa é transformar esse DSL nos artefatos necessários. Para fazer isso, escreveremos um gerador no mesmo K / N, compilaremos um arquivo executável a partir dele e de nossa DSL e executá-lo - voila! A solução não é a mais elegante, mas nada mais simples e confiável ainda me ocorreu.
Bem, então tudo é simples - compilamos a biblioteca com funções e coletamos a extensão regularmente, incluindo-a lá.
Para facilitar o uso, toda a mágica da compilação está oculta em um script de shell.O que veio disso
Um exemplo da descrição e do código gerado para a extensão simples descrita nesta DSL (
para melhor compreensão, todos os argumentos são fornecidos em um formulário nomeado ).
konfigure.kt - extensões 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 funções fun hello(name: String, lang: String?) = "${if (lang ?: "" == "") HELLO_EN else lang} $name!!!\n"
Observe o algoritmo estranho para determinar o valor para `lang`. Isso ocorre devido a um erro na versão atual do K / N, que não permite passar uma variável não inicializada do tipo `char *` como argumento de C. - você precisa passar uma string vazia.
config.m4 - arquivo gerado 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 - arquivo gerado com constantes Kotlin const val HELLO_EN = "Hello" const val HELLO_ES = "Hola" const val HELLO_RU = ""
example.c - arquivo gerado com 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){
Sobre por que apenas tipos simples
Porque eles são um a um mapeados para os tipos nativos do Kotlin. Até o momento, o projeto implementa, de fato, a interoperabilidade apenas em uma direção, ou seja, chamando funções K / N de C. Para processar tipos complexos, como
zend_value
,
zend_class_entry
ou
zend_fcall_info
, você precisa importar as estruturas correspondentes no projeto K / N e gravar os wrappers apropriados para trabalhar com eles, além de todas as macros, etc.
Jarra com alcatrão. Uma colher está presa.
- Documentação nativa Kotlin. Parece estar lá, mas ... Até agora, o meio mais confiável de estudar é ler a fonte.
- O tamanho da extensão resultante não é tão pequeno. Para o exemplo acima, é obtida uma biblioteca de aproximadamente 500 KB.
- Você nem precisa esperar que as extensões escritas em K / N acabem na biblioteca de extensões PHP. O produto é obtido, por assim dizer, apenas para uso interno.
O que vem a seguir
Implemente tudo o que está descrito na seção "Sobre por que apenas tipos simples".
Mais uma vez, um
link para o repositório .
Obrigado pela atenção, me deseje boa sorte :)