Nessa segunda parte, gostaria de explicar melhor a parte técnica das extensões PHP, e precisamos descrever, ao menos a diferença, nas 2 formas diferentes de se fazer isso: modulo PHP e extensão Zend. Apesar de se chamar PHP Module, comumente utilizamos extensão PHP sem problema algum, e não se preocupe, você saberá quando é uma extensão Zend, pois elas são mais complexas, e te dão a possibilidade de criar ganchos internos dentro do PHP e da sua maquina virtual. Então, ao menos que você realmente precise, extensões Zend são algo muito avançado, que atingem situações muito especificas, como XDebug por exemplo. O skel cria uma extensão PHP, então vamos focar nisso por enquanto.

No primeiro post, falei um pouco sobre como criar uma extensão, basicamente usando skel, como compilar, e fazer um modelo funcional testado para iniciarmos nosso tutorial.

Nessa segunda parte, gostaria de explicar melhor a parte técnica das extensões PHP, e precisamos descrever, ao menos a diferença, nas 2 formas diferentes de se fazer isso: modulo PHP e extensão Zend. Apesar de se chamar PHP Module, comumente utilizamos extensão PHP sem problema algum, e não se preocupe, você saberá quando é uma extensão Zend, pois elas são mais complexas, e te dão a possibilidade de criar ganchos internos dentro do PHP e da sua maquina virtual. Então, ao menos que você realmente precise, extensões Zend são algo muito avançado, que atingem situações muito especificas, como XDebug por exemplo. O skel cria uma extensão PHP, então vamos focar nisso por enquanto.

Para uma extensão ser carregada, o core busca pelo símbolo getmodule dentro da sua extensão. Por mais que a macro tenha um ZEND, não confunda com uma extensão Zend ok?

ZEND_GET_MODULE(myext);

Então pense no processo do core carregando sua lib/extensão, encontrou o símbolo, chamou ele, e agora começa a configuração da sua extensão, e para isso usamos uma estrutura zend_module_entry e sua estrutura é essa:

struct _zend_module_entry {
    unsigned short size;
    unsigned int zend_api;
    unsigned char zend_debug;
    unsigned char zts;
    const struct _zend_ini_entry *ini_entry;
    const struct _zend_module_dep *deps;
    const char *name;
    const struct _zend_function_entry *functions;
    zend_result (*module_startup_func)(INIT_FUNC_ARGS);
    zend_result (*module_shutdown_func)(SHUTDOWN_FUNC_ARGS);
    zend_result (*request_startup_func)(INIT_FUNC_ARGS);
    zend_result (*request_shutdown_func)(SHUTDOWN_FUNC_ARGS);
    void (*info_func)(ZEND_MODULE_INFO_FUNC_ARGS);
    const char *version;
    size_t globals_size;
#ifdef ZTS
    ts_rsrc_id* globals_id_ptr;
#else
    void* globals_ptr;
#endif
    void (*globals_ctor)(void *global);
    void (*globals_dtor)(void *global);
    zend_result (*post_deactivate_func)(void);
    int module_started;
    unsigned char type;
    void *handle;
    int module_number;
    const char *build_id;
};

Não se assuste, postei isso por que é isso que ela é, mas existem algumas macros que vão te ajudar a preencher essas informações corretamente.

Os quatro primeiros campos, são preenchidos pela macro STANDARD_MODULE_HEADER, que define o tamanho no campo size, versão da api em zend_api, debug no campo zend_debug e tread safe no campo zts, e é definida como:

#define STANDARD_MODULE_HEADER_EX sizeof (zend_module_entry), ZEND_MODULE_API_NO, ZEND_DEBUG, USING_ZTS 
#define STANDARD_MODULE_HEADER STANDARD_MODULE_HEADER_EX, NULL, NULL

Na sequencia temos o campo ini_entry, e ele não é usado aqui, pois existem macros para facilitar as entradas de configuração dentro do arquivo INI.

Depois o campo de dependências do tipo _zend_module_dep. Aqui você define se alguma extensão precisa ser carregada antes da sua, ou se ela é conflitante com alguma outra extensão.

Note a definição STANDARD_MODULE_HEADER. Falamos 4 primeiros campos, porem tem 2 NULL na frente, que se não passado, assumira esse valor. Então, caso não se passe os 6 campos, o STANDARD_MODULE_HEADER assumirá todos eles.

Agora tempo o campo name. Nele é onde vai o nome da nossa extensão.

O próximo campo deixa as coisas um pouco mais interessantes. O campo functions é um ponteiro para a estrutura zend_function_entry, que registra as funções da sua extensão. Se sua extensão cria uma função no PHP, essa função será registrada aqui.

A seguir temos 5 campos primordiais para o ciclo de inicialização, vida e descarregamento de uma extensão. São eles: module_startup_func, module_shutdown_func, request_startup_func, request_shutdown_func e info_func. Aqui os detalhes são vários, então para resumir, vamos pensar que cada um desses é uma função e cada uma delas será chamada num determinado ponto do processamento do script:

  • Quando o módulo for iniciado/criado, a função module_startuprequest_shutdown_func_func é chamada;
  • Quando uma solicitação de processamento é chamado, a função request_startup_func é chamada;
  • Quando o processamento é finalizado, a função request_shutdown_func é chamada;
  • E finalmente quando o modulo é descarregado, a função module_shutdown_func é chamada;
  • Quando alguem/algo pede uma informação da extensão, como por exemplo o phpinfo(), a função info_func é chamada;

Isso daqui vai precisar de um post só pra ele, pois são nesses pontos que você deve registrar, desligar e fazer todo gerenciamento de memória da sua extensão. Por isso entender muito bem cada gancho desse é extremamente importante. Mas vamos em frente.

O próximo campo é o version, uma string com a versão da sua extensão.

Na sequencia temos os campos para gerenciamento global e de controle do PHP. Estes últimos são definidos em STANDARD_MODULE_PROPERTIES, e você não precisa se preocupar com eles, pois o core tratará de gerar o module_number e build_id. Da mesma forma anterior, caso não precise trabalhar com os globais agora, o STANDARD_MODULE_PROPERTIES fará o tratamento para você.

Juntando tudo agora, você percebeu que não precisamos saber e preencher todos estes campos do zend_module_entry? Para configurar nossa extensão, precisamos inicialmente de algo próximo disso:

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
    NULL,                       // Função executada ao iniciar a extensão
    NULL,                       // Função executada ao finalizar a extensão
    PHP_RINIT(myext),           // Função executada ao iniciar o processamento
    NULL,                       // 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
    STANDARD_MODULE_PROPERTIES  // Campos globais e de controle interno
};

Acredite ou não, uma extensão limpinha, que não faz nada, mas que funciona, é só isso:

/**
 * Inclui as libs necessárias
 */
#ifdef HAVE_CONFIG_H
    #include "config.h"
#endif
#include "php.h"
#include "ext/standard/info.h"


// Registra as funções para o phpland (mundo php)
static const zend_function_entry ext_functions[] = {
    ZEND_FE_END
};

/**
 * Função chamada quando o processamento é iniciado
 */
PHP_RINIT_FUNCTION(myext)
{
    return SUCCESS;
}

/**
 * Função chamada quando alguem pede alguma informação da extensão
 */
PHP_MINFO_FUNCTION(myext)
{
    php_info_print_table_start();
    php_info_print_table_header(2, "myext support", "enabled");
    php_info_print_table_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
    NULL,                       // Função executada ao iniciar a extensão
    NULL,                       // Função executada ao finalizar a extensão
    PHP_RINIT(myext),           // Função executada ao iniciar o processamento
    NULL,                       // 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
    STANDARD_MODULE_PROPERTIES  // Campos globais e de controle interno
};

/**
 * Simbolo chamado pelo core
 */
ZEND_GET_MODULE(myext)

Então agora podemos remover php_myext.h e myext_arginfo.h. Mais para frente trabalharemos melhor estes arquivos.

Espero que esta postagem tenha ficado simples, pois é difícil escrever de forma simples algo complexo. Mas tentei explicar de 3 formas diferentes: descrevendo cada coisa, comentando blocos e aplicando comentários. Espero que dessa forma atinja o maior numero de gente possível. Próxima postagem, quero começar a criar algumas funções, e trabalhar com parâmetros e retornos.

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: https://www.zend.com/resources/writing-php-extensions, http://www.phpinternalsbook.com/index.html