Post on 28-Nov-2014
description
Desenvolvendo Extensões PECL
PHP Conference Brasil 201026 de novembro
Pedro Padron – ppadron@w3p.com.br - @ppadron
Hello World
- php, pear, pecl, linux, plesk, puppet...
- pecl.php.net/augeas
- spamassassin php api
- api api api api api api api api
- wordpress
Por que você está aqui?(nesta sala, não no universo)
pecl
- php extension community library
- dizem que se lê “pickle”
- ou no brasil: “pê” “ê” (ou “é”) “cê” “éle”
Motivos para criar uma extensão
- performance
- “esconder o código” do cliente
- acessar funções de uma biblioteca C
- modificar o comportamento do PHP
acessar funções de uma biblioteca em C
"It is a glue language that glues the web server to all the interesting backend libraries available out there." *
Rasmus Lerdorf
* http://www.urgig.com/int/0107_rl_a_int.html
modificar o comportamento do PHP
funcall
zend extensions
- trabalham na camada da ZendEngine
- bytecode compiler
- opcode handlers
- exemplos: xdebug, test_handlers, ZendGuardLoader
zend extensions
php extensions
não faz nada do que uma zend extension faz
montando o ambiente de trabalho
montando o ambiente - linux
- debian/ubuntu:
apt-get install php5-dev (headers & phpize)
apt-get build-dep php5 (libs necessárias)
apt-get install php5-dbg (debug symbols)
- redhat/fedora:
yum install php5-dev
yum install yum-utils
yum-builddep php5
montando o ambiente - windows
- sim, é possível; sim, é mais chato que no linux
- visual c++ 2008 (express edition é de graça)
- microsoft platform sdk
- várias bibliotecas necessárias pelo PHP
- variáveis de ambiente no console do visual studio
- processo completo: http://wiki.php.net/internals/windows/stepbystepbuild
código fonte do PHP
- php.net/downloads
- 5.3.3
- extraia p/ algum diretório
- em windows, não use caminhos que contenham espaços
- cd php-src/ext
compilando a extensão - linux
$ cd ext/minhaextensao$ phpize$ ./configure$ make$ (sudo) make install
“extension=minhaextensao.so” no php.ini
compilando a extensão - windows
- siga todos os passos da etapa de setup do ambiente
- garanta que a extensão está no diretório ext/
- abra o Visual Studio Command Prompt
> vcvars32.bat> buildconf> configure –disable-all –enable-minhaextensao=shared –enable-cli> nmake
- dentro de Release_TS estará php_minhaextensao.dll
gerando o esqueleto de uma extensão
gerando o esqueleto de uma extensão
- é tão chato que foi preciso criar um script pra isso
- php-src/ext/ext_skel
- php-src/ext/ext_skel_win32.php* precisa de CygWin instalado* gera o arquivo .dsp do VisualStudio
gerando o esqueleto de uma extensão
./ext_skel –extname=minhaextensao
.cvsignore (renomeie para .gitignore =P)
config.m4 (config script linux)
config.w32 (config script windows)
CREDITS (seu nome e seu e-mail)
EXPERIMENTAL (not for use in production)
minhaextensao.c (código da extensão)
minhaextensao.php (script de teste)
php_minhaextensao.h (headers)
tests/001.phpt (primeiro teste)
minhaextensao.c – module entry
essa estrutura vai armazenar todas as informações sobre sua extensão
minhaextensao.c - functions
Sim, a última linha tem sempre que ser {NULL, NULL, NULL}, isso indica para a Zend Engine que a lista de funções acabou.
Internamente, confirm_minha_extensao_compiled será chamada de zif_confirm_minhaextensao_compiled. (zif = zend internal function)
minhaextensao.c - functions
E essa é a função!
php_minhaextensao.h
extern zend_module_entry minhaextensao_module_entry;
PHP_MINIT_FUNCTION(minhaextensao);PHP_MSHUTDOWN_FUNCTION(minhaextensao);PHP_RINIT_FUNCTION(minhaextensao);PHP_RSHUTDOWN_FUNCTION(minhaextensao);PHP_MINFO_FUNCTION(minhaextensao);
PHP_FUNCTION(confirm_minhaextensao_compiled);
Declarando as funções e a definição do módulo
php_minhaextensao.h
- ZTS = Zend Thread Safety
- TSRM = Thread Safe Resource Manager
#ifdef ZTS#include "TSRM.h"#endif
config.m4
Se a sua extensão não usa nenhuma biblioteca externa:
config.m4Se a sua extensão usa alguma biblioteca externa
config.m4
Testando a biblioteca pela presença de algum símbolo
config.m4
símbolos de uma biblioteca são todos os elementos visíveis ao seu usuário, podem ser classes, funções,
estruturas de dados, etc...
bibliotecas externas – embutir ou linkar?
- em windows é preferível embutir a biblioteca externa, pois o usuário final só precisa instalar sua dll (php_minhaextensao.dll)
- em linux, verifique se as distribuições possuem pacotes para a biblioteca em questão;
- se for embutir, verifique se a licença da biblioteca permite isso
phpinfo(); - PHP_MINFO_FUNCTION
phpinfo(); - PHP_MINFO_FUNCTION
phpinfo(); - PHP_MINFO_FUNCTION
não abuse da criatividade
void php_info_print_table_start(void)void php_info_print_table_end(void)void php_info_print_table_header(int cols, ...)void php_info_print_table_colspan_header(int cols, char *header)void php_info_print_table_row(int cols, ...)void php_info_print_table_row_ex(int cols, char *class, ...)void php_info_print_box_start(int flag)void php_info_print_box_end()void php_info_print_hr(void)...
PHP_MINIT_FUNCTION
- executado uma vez para cada processo
- cli/cgi/multithread sapi => executa apenas uma vez (apache2-worker)
- sempre que houver fork(); inicia novamente o ambiente (mod_php no apache2-prefork)
- registrar classes, constantes, configurações php.ini...
PHP_MINIT_FUNCTION
ppadron@delorean:$ php -r 'echo MINHAEXTENSAO_HELLO;'Hello World
PHP_RINIT_FUNCTION
- executado a cada requisição feita ao script
- evite inicializar muita coisa aqui, economize memória
.ini settings
- primeiro você declara as configs
- inicializa em PHP_MINIT_FUNCTION
- destrói em PHP_MINIT_SHUTDOWN
- exibe em PHP_MINFO_FUNCTION
.ini settings – declara, inicializa, destrói
.ini settings – exibindo no phpinfo();
.ini settings – acessando os valores
/* VALORES ORIGINAIS */const char *strval = INI_ORIG_STR("minhaextensao.config");long lval = INI_ORIG_INT("minhaextensao.config_int");double dval = INI_ORIG_FLT("minhaextensao.config_float");zend_bool bval = INI_ORIG_BOOL("minhaextensao.config_bool");
/* VALORES ATUAIS */long lval = INI_INT("minhaextensao.config_int");double dval = INI_FLT("minhaextensao.config_float");zend_bool bval = INI_BOOL("minhaextensao.config_bool");
chega de enrolação, vamos trabalhar
quantos tipos de dados existem no PHP?
por baixo dos panos, eles são representados por um só: o ZVAL
struct { union { long lval; double dval; struct { char *val; int len; } str; HashTable *ht; zend_object_value obj; } value; zend_uint refcount; zend_uchar type; zend_uchar is_ref;} zval;
prazer, ZVAL.
preciso sempre mexer direto no ZVAL?
a não ser que você saiba o que está fazendo, não.
existem diversas funções e macros para lidar com um ZVAL
arrays
PHP_FUNCTION(minhaextensao_me_da_um_array){ zval *meu_primeiro_array;
ALLOC_INIT_ZVAL(meu_primeiro_array);
array_init(meu_primeiro_array); array_init(return_value);
add_next_index_null(meu_primeiro_array); add_next_index_string(meu_primeiro_array, "ueeeba!!", 1);
/* uau */ add_next_index_zval(return_value, meu_primeiro_array);
}
arrays – minha primeira função
arrays – minha primeira função
function minhaextensao_me_da_um_array(){ $meu_primeiro_array = array(); $retorno = array();
$meu_primeiro_array[] = null; $meu_primeiro_array[] = “ueeeba!!”;
$retorno[] = $meu_primeiro_array;
return $retorno;}
arrays – minha primeira função
$ php -r 'var_dump(minhaextensao_me_da_um_array());'array(1) { [0]=> array(2) { [0]=> NULL [1]=> string(8) "ueeeba!!" }}
arrays – lista de funções
Fonte: http://devzone.zend.com/node/view/id/1022#Heading5
os outros tipos que não tem tanta graça
zval *meuzval;
ZVAL_NULL(meuzval);
ZVAL_LONG(meuzval, 1408);
/* bool usa o mesmo espaço de long */ZVAL_BOOL(meuzval, 1);
ZVAL_STRING(meuzval, “tchananan”, 0);
daqui a pouco vamos discutir sobre resource e objetos, guentaí
retornando valores em funções
- já vimos que existe um tal de return_value em algum lugar
- podemos manipular seu valor e deixar que o php o retorne
- ou podemos usar alguns atalhos
retornando valores em funções
PHP_FUNCTION(minhaextensao_bool){ RETURN_TRUE; php_error_docref(NULL TSRMLS_CC, E_WARNING, "Nunca vai chegar aqui");}
retornando os valores em funções
RETURN_NULL();RETURN_STRING(“bola”, 0);RETURN_TRUE;RETURN_FALSE;RETURN_DOUBLE(3.14);RETURN_LONG(1408);
e assim por diante...
agora que sabemos como retornar valores, vamos receber valores
recebendo valores em uma função
PHP_FUNCTION(minhaextensao_recebe_string){ char *param; int param_len;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", ¶m, ¶m_len) == FAILURE) {
return; }
php_printf("Obrigado por me passar como parametro: "); PHPWRITE(param, param_len); php_printf("\n");}
recebendo valores em uma função
minhaextensao_recebe_string("eba!");// Obrigado por me passar como parametro: eba!
minhaextensao_recebe_string();// PHP Warning: minhaextensao_recebe_string() expects exactly 1 parameter, 0 given
class bola { public function __toString() { return “bola”; }}
minhaextensao_recebe_string(new bola());// Obrigado por me passar como parametro: bola
recebendo valores opcionais
PHP_FUNCTION(minhaextensao_recebe_string_opcional){ char *str = "default"; int str_len = sizeof("default") - 1;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &str, &str_len) == FAILURE) {
RETURN_FALSE; }
php_printf("vejam: "); PHPWRITE(str, str_len); php_printf("\n");}
recebendo valores opcionais
minhaextensao_recebe_string_opcional();// vejam: default
minhaextensao_recebe_string_opcional(“bola”);// vejam: bola
zend_parse_parameters
Fonte: http://devzone.zend.com/node/view/id/1022
resources
resources
- permite lidar com estruturas mais complexas em C e passá-las de um lado para o outro;
- inicializados em PHP_MINIT_FUNCTION;
- usado em extensões procedurais;
resources – inicialização/destruição
/* isso está no topo do minhaextensao.c */static int le_minhaextensao;
static void php_minhaextensao_resource_destrutor(zend_rsrc_list_entry *rsrc TSRMLS_DC){ FILE *fp = (FILE *) rsrc->ptr; fclose(fp);}
PHP_MINIT_FUNCTION(minhaextensao){ le_minhaextensao = zend_register_list_destructors_ex( php_minhaextensao_resource_destrutor, NULL, "Resource da Minha Extensao", module_number);
return SUCCESS;}
resources – criando e retornando
PHP_FUNCTION(minhaextensao_resource){ FILE *fp; fp = fopen("/tmp/arquivo", "r");
if (!fp) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Naaaaaao"); RETURN_FALSE; }
ZEND_REGISTER_RESOURCE(return_value, fp, le_minhaextensao);}
$ php -r 'var_dump(minhaextensao_resource());'resource(4) of type (Resource da Minha Extensao)
resources – recebendo como parâmetro
PHP_FUNCTION(minhaextensao_resource_check){ FILE *fp; zval *resource;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &resource) == FAILURE) {
RETURN_NULL(); }
ZEND_FETCH_RESOURCE(fp, FILE*, &resource, -1, "Resource da Minha Extensao", le_minhaextensao);
if (!fp) { RETURN_FALSE; }
RETURN_TRUE;}
resource – recebendo como parâmetro
$resource = minhaextensao_resource();$result = minhaextensao_resource_check($resource);
var_dump($result);
// bool(true)
$resource = curl_init();$result = minhaextensao_resource_check($resource);
var_dump($result);
// PHP Warning: minhaextensao_resource_check(): supplied resource is not a valid Resource da Minha Extensao resource
orientação a objetos
estudo de caso: extensão augeas
declarando uma classe
/* {{{ zend_class_entry */zend_class_entry *augeas_ce_Augeas;/* }}} */
/* REFLECTION! *//* {{{ ZEND_BEGIN_ARG_INFO */ZEND_BEGIN_ARG_INFO_EX(arginfo_Augeas__construct, 0) ZEND_ARG_INFO(0, root) ZEND_ARG_INFO(0, loadpath) ZEND_ARG_INFO(0, flags)ZEND_END_ARG_INFO();
ZEND_BEGIN_ARG_INFO(arginfo_Augeas_get, 0) ZEND_ARG_INFO(0, path)ZEND_END_ARG_INFO();/* }}} */
declarando uma classe – lista de métodos
/* {{{ augeas_methods */static zend_function_entry augeas_methods[] = { PHP_ME(Augeas, __construct, arginfo_Augeas__construct, ZEND_ACC_PUBLIC) PHP_ME(Augeas, get, arginfo_Augeas_get, ZEND_ACC_PUBLIC) PHP_ME(Augeas, set, arginfo_Augeas_set, ZEND_ACC_PUBLIC) PHP_ME(Augeas, match, arginfo_Augeas_match, ZEND_ACC_PUBLIC) PHP_ME(Augeas, rm, arginfo_Augeas_rm, ZEND_ACC_PUBLIC) PHP_ME(Augeas, save, arginfo_Augeas_save, ZEND_ACC_PUBLIC) PHP_ME(Augeas, mv, arginfo_Augeas_mv, ZEND_ACC_PUBLIC) PHP_ME(Augeas, insert, arginfo_Augeas_insert, ZEND_ACC_PUBLIC) { NULL, NULL, NULL }};/* }}} */
ZEND_ACC_PUBLICZEND_ACC_PROTECTEDZEND_ACC_PRIVATE
ZEND_ACC_STATICZEND_ACC_ABSTRACTZEND_ACC_FINAL
declarando uma classe - inicialização
PHP_MINIT_FUNCTION(augeas){ zend_class_entry ce;
/* Register Augeas class */ INIT_CLASS_ENTRY(ce, "Augeas", augeas_methods); augeas_ce_Augeas = zend_register_internal_class(&ce TSRMLS_CC);
return SUCCESS;}
declarando uma classe – inicialização (namespace)
PHP_MINIT_FUNCTION(augeas){ zend_class_entry ce;
/* Register Augeas class */ INIT_NS_CLASS_ENTRY(ce, “Augeas”, "Augeas", augeas_methods); augeas_ce_Augeas = zend_register_internal_class(&ce TSRMLS_CC);
return SUCCESS;}
declarando uma classe - herança
/* Register AugeasException class (inherits Exception) */INIT_CLASS_ENTRY(ce, "AugeasException", NULL);
augeas_ce_AugeasException = zend_register_internal_class_ex( &ce_exception, zend_exception_get_default(TSRMLS_C), NULL TSRMLS_DC);
Declarando uma classe - propriedades
int zend_declare_property(zend_class_entry *ce, char *name, int name_length, zval *property, int access_type TSRMLS_DC);
int zend_declare_property_null(zend_class_entry *ce, char *name, int name_length, int access_type TSRMLS_DC);
int zend_declare_property_bool(zend_class_entry *ce, char *name, int name_length, long value, int access_type TSRMLS_DC);
int zend_declare_property_double(zend_class_entry *ce, char *name, int name_length, double value, int access_type TSRMLS_DC);
int zend_declare_property_string(zend_class_entry *ce, char *name, int name_length, char *value, int access_type TSRMLS_DC);
zval *zend_read_property(zend_class_entry *scope, zval *object, char *name, int name_length, zend_bool silent TSRMLS_DC);
Créditos: Erick Tedeschi
declarando uma classe - constantes
int zend_declare_class_constant(zend_class_entry *ce, char *name, size_t name_len, zval *value TSRMLS_DC);
int zend_declare_class_constant_long(zend_class_entry *ce, char *name, size_t name_len, long value TSRMLS_DC);
int zend_declare_class_constant_bool(zend_class_entry *ce, char *name, size_t name_len, zend_bool value TSRMLS_DC);
int zend_declare_class_constant_double(zend_class_entry *ce, char *name, size_t name_len, double value TSRMLS_DC);
int zend_declare_class_constant_string(zend_class_entry *ce, char *name, size_t name_len, char *value TSRMLS_DC);
Créditos: Erick Tedeschi
mas... e os métodos?
quase igual funções...
AUGEAS_FROM_OBJECT ???getThis() ???
vamos dar um tempo aqui e ir logo para os arquivos...
estamos de volta
quer aprender mais?
compre o livro da Sara Golemon“Extending and Embedding PHP”
leia o código alheio
dúvidas?
obrigado =)