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