Desenvolvendo Extensões PECL

2,544 views

Published on

Palestra apresentada no PHP Conference Brasil 2010 sobre desenvolvimento de extensões PECL.

Published in: Technology
0 Comments
3 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
2,544
On SlideShare
0
From Embeds
0
Number of Embeds
142
Actions
Shares
0
Downloads
33
Comments
0
Likes
3
Embeds 0
No embeds

No notes for slide

Desenvolvendo Extensões PECL

  1. 1. Desenvolvendo Extensões PECL PHP Conference Brasil 2010 26 de novembro Pedro Padron – ppadron@w3p.com.br - @ppadron
  2. 2. Hello World - php, pear, pecl, linux, plesk, puppet... - pecl.php.net/augeas - spamassassin php api - api api api api api api api api - wordpress
  3. 3. Por que você está aqui? (nesta sala, não no universo)
  4. 4. pecl - php extension community library - dizem que se lê “pickle” - ou no brasil: “pê” “ê” (ou “é”) “cê” “éle”
  5. 5. 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
  6. 6. 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
  7. 7. modificar o comportamento do PHP funcall
  8. 8. zend extensions - trabalham na camada da ZendEngine - bytecode compiler - opcode handlers - exemplos: xdebug, test_handlers, ZendGuardLoader
  9. 9. zend extensions
  10. 10. php extensions não faz nada do que uma zend extension faz
  11. 11. montando o ambiente de trabalho
  12. 12. 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
  13. 13. 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
  14. 14. 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
  15. 15. compilando a extensão - linux $ cd ext/minhaextensao $ phpize $ ./configure $ make $ (sudo) make install “extension=minhaextensao.so” no php.ini
  16. 16. 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
  17. 17. gerando o esqueleto de uma extensão
  18. 18. 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
  19. 19. 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)
  20. 20. minhaextensao.c – module entry essa estrutura vai armazenar todas as informações sobre sua extensão
  21. 21. 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)
  22. 22. minhaextensao.c - functions E essa é a função!
  23. 23. 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
  24. 24. php_minhaextensao.h - ZTS = Zend Thread Safety - TSRM = Thread Safe Resource Manager #ifdef ZTS #include "TSRM.h" #endif
  25. 25. config.m4 Se a sua extensão não usa nenhuma biblioteca externa:
  26. 26. config.m4 Se a sua extensão usa alguma biblioteca externa
  27. 27. config.m4 Testando a biblioteca pela presença de algum símbolo
  28. 28. 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...
  29. 29. 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
  30. 30. phpinfo(); - PHP_MINFO_FUNCTION
  31. 31. phpinfo(); - PHP_MINFO_FUNCTION
  32. 32. 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) ...
  33. 33. 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...
  34. 34. PHP_MINIT_FUNCTION ppadron@delorean:$ php -r 'echo MINHAEXTENSAO_HELLO;' Hello World
  35. 35. PHP_RINIT_FUNCTION - executado a cada requisição feita ao script - evite inicializar muita coisa aqui, economize memória
  36. 36. .ini settings - primeiro você declara as configs - inicializa em PHP_MINIT_FUNCTION - destrói em PHP_MINIT_SHUTDOWN - exibe em PHP_MINFO_FUNCTION
  37. 37. .ini settings – declara, inicializa, destrói
  38. 38. .ini settings – exibindo no phpinfo();
  39. 39. .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");
  40. 40. chega de enrolação, vamos trabalhar
  41. 41. quantos tipos de dados existem no PHP?
  42. 42. por baixo dos panos, eles são representados por um só: o ZVAL
  43. 43. 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.
  44. 44. preciso sempre mexer direto no ZVAL?
  45. 45. a não ser que você saiba o que está fazendo, não.
  46. 46. existem diversas funções e macros para lidar com um ZVAL
  47. 47. arrays
  48. 48. 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
  49. 49. 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; }
  50. 50. 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!!" } }
  51. 51. arrays – lista de funções Fonte: http://devzone.zend.com/node/view/id/1022#Heading5
  52. 52. 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);
  53. 53. daqui a pouco vamos discutir sobre resource e objetos, guentaí
  54. 54. 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
  55. 55. retornando valores em funções PHP_FUNCTION(minhaextensao_bool) { RETURN_TRUE; php_error_docref(NULL TSRMLS_CC, E_WARNING, "Nunca vai chegar aqui"); }
  56. 56. 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...
  57. 57. agora que sabemos como retornar valores, vamos receber valores
  58. 58. 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", &param, &param_len) == FAILURE) { return; } php_printf("Obrigado por me passar como parametro: "); PHPWRITE(param, param_len); php_printf("n"); }
  59. 59. 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
  60. 60. 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"); }
  61. 61. recebendo valores opcionais minhaextensao_recebe_string_opcional(); // vejam: default minhaextensao_recebe_string_opcional(“bola”); // vejam: bola
  62. 62. zend_parse_parameters Fonte: http://devzone.zend.com/node/view/id/1022
  63. 63. resources
  64. 64. 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;
  65. 65. 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; }
  66. 66. 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)
  67. 67. 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; }
  68. 68. 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
  69. 69. orientação a objetos
  70. 70. estudo de caso: extensão augeas
  71. 71. 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(); /* }}} */
  72. 72. 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_PUBLIC ZEND_ACC_PROTECTED ZEND_ACC_PRIVATE ZEND_ACC_STATIC ZEND_ACC_ABSTRACT ZEND_ACC_FINAL
  73. 73. 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; }
  74. 74. 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; }
  75. 75. 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);
  76. 76. 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
  77. 77. 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
  78. 78. mas... e os métodos?
  79. 79. quase igual funções...
  80. 80. AUGEAS_FROM_OBJECT ??? getThis() ???
  81. 81. vamos dar um tempo aqui e ir logo para os arquivos...
  82. 82. estamos de volta
  83. 83. quer aprender mais?
  84. 84. compre o livro da Sara Golemon “Extending and Embedding PHP”
  85. 85. leia o código alheio
  86. 86. http://lxr.php.net/ php cross-referenced source code
  87. 87. dúvidas?
  88. 88. obrigado =)

×