Entendendo a estrutura de uma extensão PHP - Parte 02

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
get_module
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