You are on page 1of 89

Desenvolvendo Extenses PECL

PHP Conference Brasil 2010 26 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, no no universo)

pecl
- php extension community library - dizem que se l pickle - ou no brasil: p (ou ) c le

Motivos para criar uma extenso

- performance - esconder o cdigo do cliente - acessar funes de uma biblioteca C - modificar o comportamento do PHP

acessar funes 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

no 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 necessrias) 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, possvel; sim, mais chato que no linux - visual c++ 2008 (express edition de graa) - microsoft platform sdk - vrias bibliotecas necessrias pelo PHP - variveis de ambiente no console do visual studio - processo completo:
http://wiki.php.net/internals/windows/stepbystepbuild

cdigo fonte do PHP

- php.net/downloads - 5.3.3 - extraia p/ algum diretrio - em windows, no use caminhos que contenham espaos - cd php-src/ext

compilando a extenso - linux

$ $ $ $ $

cd ext/minhaextensao phpize ./configure make (sudo) make install

extension=minhaextensao.so no php.ini

compilando a extenso - windows


- siga todos os passos da etapa de setup do ambiente - garanta que a extenso est no diretrio 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 extenso

gerando o esqueleto de uma extenso

- to 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 extenso


./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 (cdigo da extenso) minhaextensao.php (script de teste) php_minhaextensao.h (headers) tests/001.phpt (primeiro teste)

minhaextensao.c module entry essa estrutura vai armazenar todas as informaes sobre sua extenso

minhaextensao.c - functions

Sim, a ltima linha tem sempre que ser {NULL, NULL, NULL}, isso indica para a Zend Engine que a lista de funes acabou. Internamente, confirm_minha_extensao_compiled ser chamada de zif_confirm_minhaextensao_compiled. (zif = zend internal function)

minhaextensao.c - functions

E essa a funo!

php_minhaextensao.h Declarando as funes e a definio do mdulo


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);

php_minhaextensao.h
#ifdef ZTS #include "TSRM.h" #endif

- ZTS = Zend Thread Safety - TSRM = Thread Safe Resource Manager

config.m4

Se a sua extenso no usa nenhuma biblioteca externa:

config.m4
Se a sua extenso usa alguma biblioteca externa

config.m4

Testando a biblioteca pela presena de algum smbolo

config.m4
smbolos de uma biblioteca so todos os elementos visveis ao seu usurio, podem ser classes, funes, estruturas de dados, etc...

bibliotecas externas embutir ou linkar?


- em windows prefervel embutir a biblioteca externa, pois o usurio final s precisa instalar sua dll (php_minhaextensao.dll) - em linux, verifique se as distribuies possuem pacotes para a biblioteca em questo; - se for embutir, verifique se a licena da biblioteca permite isso

phpinfo(); - PHP_MINFO_FUNCTION

phpinfo(); - PHP_MINFO_FUNCTION

phpinfo(); - PHP_MINFO_FUNCTION

void void void void void void void void void ...

php_info_print_table_start(void) php_info_print_table_end(void) php_info_print_table_header(int cols, ...) php_info_print_table_colspan_header(int cols, char *header) php_info_print_table_row(int cols, ...) php_info_print_table_row_ex(int cols, char *class, ...) php_info_print_box_start(int flag) php_info_print_box_end() php_info_print_hr(void)

no abuse da criatividade

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, configuraes php.ini...

PHP_MINIT_FUNCTION

ppadron@delorean:$ php -r 'echo MINHAEXTENSAO_HELLO;' Hello World

PHP_RINIT_FUNCTION

- executado a cada requisio feita ao script - evite inicializar muita coisa aqui, economize memria

.ini settings

- primeiro voc declara as configs - inicializa em PHP_MINIT_FUNCTION - destri em PHP_MINIT_SHUTDOWN - exibe em PHP_MINFO_FUNCTION

.ini settings declara, inicializa, destri

.ini settings exibindo no phpinfo();

.ini settings acessando os valores

/* VALORES ORIGINAIS const char *strval = long lval = double dval = zend_bool bval =

*/ INI_ORIG_STR("minhaextensao.config"); INI_ORIG_INT("minhaextensao.config_int"); INI_ORIG_FLT("minhaextensao.config_float"); 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 enrolao, vamos trabalhar

quantos tipos de dados existem no PHP?

por baixo dos panos, eles so representados por um s: o ZVAL

prazer, 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;

preciso sempre mexer direto no ZVAL?

a no ser que voc saiba o que est fazendo, no.

existem diversas funes e macros para lidar com um ZVAL

arrays

arrays minha primeira funo


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 funo

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 funo

$ php -r 'var_dump(minhaextensao_me_da_um_array());' array(1) { [0]=> array(2) { [0]=> NULL [1]=> string(8) "ueeeba!!" } }

arrays lista de funes

Fonte: http://devzone.zend.com/node/view/id/1022#Heading5

os outros tipos que no tem tanta graa

zval *meuzval; ZVAL_NULL(meuzval); ZVAL_LONG(meuzval, 1408); /* bool usa o mesmo espao de long */ ZVAL_BOOL(meuzval, 1); ZVAL_STRING(meuzval, tchananan, 0);

daqui a pouco vamos discutir sobre resource e objetos, guenta

retornando valores em funes

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

PHP_FUNCTION(minhaextensao_bool) { RETURN_TRUE; php_error_docref(NULL TSRMLS_CC, E_WARNING, "Nunca vai chegar aqui"); }

retornando os valores em funes

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 funo


PHP_FUNCTION(minhaextensao_recebe_string) { char *param; int param_len; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &param, &param_len) == FAILURE) { return; } php_printf("Obrigado por me passar como parametro: "); PHPWRITE(param, param_len); php_printf("\n");

recebendo valores em uma funo


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 extenses procedurais;

resources inicializao/destruio
/* 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 parmetro


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 parmetro


$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

orientao a objetos

estudo de caso: extenso 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 mtodos


/* {{{ augeas_methods */ static zend_function_entry augeas_methods[] = { PHP_ME(Augeas, __construct, arginfo_Augeas__construct, PHP_ME(Augeas, get, arginfo_Augeas_get, PHP_ME(Augeas, set, arginfo_Augeas_set, PHP_ME(Augeas, match, arginfo_Augeas_match, PHP_ME(Augeas, rm, arginfo_Augeas_rm, PHP_ME(Augeas, save, arginfo_Augeas_save, PHP_ME(Augeas, mv, arginfo_Augeas_mv, PHP_ME(Augeas, insert, arginfo_Augeas_insert, { NULL, NULL, NULL } }; /* }}} */

ZEND_ACC_PUBLIC) ZEND_ACC_PUBLIC) ZEND_ACC_PUBLIC) ZEND_ACC_PUBLIC) ZEND_ACC_PUBLIC) ZEND_ACC_PUBLIC) ZEND_ACC_PUBLIC) ZEND_ACC_PUBLIC)

ZEND_ACC_PUBLIC ZEND_ACC_PROTECTED ZEND_ACC_PRIVATE

ZEND_ACC_STATIC ZEND_ACC_ABSTRACT ZEND_ACC_FINAL

declarando uma classe - inicializao

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 inicializao (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 - herana

/* 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);

Crditos: 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);

Crditos: Erick Tedeschi

mas... e os mtodos?

quase igual funes...

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 cdigo alheio

http://lxr.php.net/ php cross-referenced source code

dvidas?

obrigado =)

You might also like