Criando funções na sua extensão PHP - Parte 04

Disponibilizar novas funções e classes para o usuário no phpland é a principal função de uma nova extensão. Adicionar essas novas funções ao core do PHP seria um trabalho gigantesco e complexo. Mesmo criando módulos, sem o Zend Engine, esse trabalho ainda seria complexo. Nesse post quero mostrar a parte divertida da coisa, e começar a fazer as coisas funcionarem, além de mostrar, o quão simples é criar uma nova função no PHP.


Disponibilizar novas funções e classes para o usuário no phpland é a principal função de uma nova extensão. Adicionar essas novas funções ao core do PHP seria um trabalho gigantesco e complexo. Mesmo criando módulos, sem o Zend Engine, esse trabalho ainda seria complexo. Nesse post quero mostrar a parte divertida da coisa, e começar a fazer as coisas funcionarem, alem de mostrar, o quão simples é criar uma nova função no PHP.

Toda função precisa ser registrada, e isso é feito na estrutura
zend_function_entry
, que já foi apresentada na parte 2 desse tutorial. Ela é uma estrutura de complexidade baixa, descrita como:
typedef struct _zend_function_entry {
 const char *fname;
 void (*handler)(INTERNAL_FUNCTION_PARAMETERS);
 const struct _zend_internal_arg_info *arg_info;
 uint32_t num_args;
 uint32_t flags;
} zend_function_entry;
O primeiro campo, o
fname
, é formado pelo nome da sua função.

Em seguida é passado o
handler
.  Aqui é passado o ponteiro para a função C que representa sua função no phpland.

Logo depois é passado os parâmetros,
arg_info
sendo um vetor de argumentos, e
num_args
sendo o contador/quantidade de posições do vetor (acostumado com PHP né? em C tudo quanto é função que passa um vetor, precisa passar o tamanho também)

Em seguida as
flags
, se indicará como static, public, final, protected, etc. Falaremos mais delas em um outro post.

"Caramba, e você falou que era uma estrutura simples, como que vou registrar uma função ai?" Calma, lembra que eu falei da Zend Engine? ela é maravilhosa, e nos permite registrar funções através de macros.

Então vamos criar uma função
myext_hello
e
myext_test
. Vou manter o prefixo
myext_
para diferenciar das outras funções do PHP, já que minha extensão chama myext ok?
// Cria a função myext_hello
PHP_FUNCTION(myext_hello)
{
 // Meu código C  
}
  
// Cria a função myext_test
PHP_FUNCTION(myext_test)
{
 // Meu código C
}
  
// Registra as funções para o phpland (mundo php)
static const zend_function_entry ext_functions[] = {
 PHP_FE(myext_hello, NULL) // Registra a função myext_hello sem parametro
 PHP_FE(myext_test, NULL) // Registra a função myext_test sem parametro
 ZEND_FE_END
};

/**
 * Configuração da nossa extensão
 */
extern zend_module_entry myext_module_entry;
zend_module_entry myext_module_entry = {
 STANDARD_MODULE_HEADER,                  // Campos size, zend_api, zend_debug, zts, ini_entry e deps
 "myext",                                // Nome da extensão
 ext_functions, // <---- Funções da nossa extensão
 PHP_MINIT(myext),                        // Função executada ao iniciar a extensão
 PHP_MSHUTDOWN(myext),                    // Função executada ao finalizar a extensão
 PHP_RINIT(myext),                        // Função executada ao iniciar o processamento
 PHP_RSHUTDOWN(myext),                    // Função executada ao finalizar o processamento
 PHP_MINFO(myext),                        // Função executada quando alguem pede informações da extensão

 "0.0.1",                                // Versão da extensão

 PHP_MODULE_GLOBALS(myext),              // Macro para o tamanho da estrutura globais
 PHP_GINIT(myext),                        // Inicializa os globais
 PHP_GSHUTDOWN(myext),                    // Finaliza os globais,

 NULL,                                    // Função executada após-finalização da finalização do request
 STANDARD_MODULE_PROPERTIES_EX            // Campos globais e de controle interno
};
O que fizemos foi criar uma função utilizando a macro
PHP_FUNCTION
. Ela pegará o parâmetro e criará toda a estrutura interna necessária para colocar esse parâmetro como uma função no phpland (lado do usuário no php). Então com temos
PHP_FUNCTION(myext_hello)
, no php o usuário terá a função
myext_hello();
.

A macro faz todo o trabalho para nós, mas para você conhecer a estrutura criada pela macro, o código ficaria algo assim:
void zif_myext_hello(zend_execute_data *execute_data, zval *return_value)
{
}

void zif_myext_test(zend_execute_data *execute_data, zval *return_value)
{
}

static const zend_function_entry pib_functions[] =
{
 {
 "myext_hello",
 zif_myext_hello,
 ((void *) 0),
 (uint32_t) (sizeof(((void *)0))/sizeof(struct _zend_internal_arg_info)-1),
 0
 },
 {
 "myext_test",
 zif_myext_test,
 ((void *) 0),
 (uint32_t) (sizeof(((void *)0))/sizeof(struct _zend_internal_arg_info)-1),
 0
 },
}
Complicado né? Note que tudo passado pelo parâmetro do
PHP_FUNCTION
, como por exemplo *myext_hello*, começa com o prefixo
zif_
. Esse é o prefixo para Zend Internal Function. Tudo internamente possui zif.
zif_printf
,
zif_var_dump
, etc

Você até pode compilar sua extensão já, e verá que temos 2 novas funções. Basta entrar no diretório e executar
make
novamente, não precisa fazer o
phpize
,
./configure
e tudo mais não, só o
make
por enquanto ja compila novamente. Se você não se lembra de como fazer isso e quer lembrar, só precisa acessar o primeiro post dessa serie.

Tudo compilado, vamos criar um arquivo test.php com o seguinte conteúdo:
<?php

 myext_hello();
 myext_test();
e executar:
$ php -d extension=./modules/myext.so test.php
Nossas funções não fazem nada, então tudo certo. Mas como tudo tem um "mas ...", se você está executando no PHP 8 verá 2
warnings
:
Warning: Missing arginfo for myext_hello() in Unknown on line 0
Warning: Missing arginfo for myext_test() in Unknown on line 0
E aqui começa algumas mudanças do PHP 8. Agora a declaração do arginfo é requisito quase que obrigatório. Quase por que ele compila, mas ele se perde e não consegue tratar os argumentos, mesmo que esse argumento não exista. Então vamos falar mais sobre argumentos de função no próximo post.

Pessoal, gostaria de deixar um apelo ai para darem uma força. compartilhem, comentem, deixe seu comentário no post. quem escreve ou faz vídeo, muitas vezes nem faz isso por dinheiro, mas sim para engajar a comunidade de alguma forma. não gostou da forma que eu escrevo? comenta na boa (nem eu gosto também), mas comenta algo, manda pro brodi, bora crescer e centralizar um conteúdo de qualidade

Referencias: http://www.phpinternalsbook.com/php7/extensions_design/php_functions.html, https://github.com/php/php-src/blob/master/Zend/zend_API.h