前面介绍了使用Zephir来开发PHP扩展,将PHP代码转为扩展,以提升性能,保护代码。目前更多的扩展都是采用C/C++开发的,最近在项目开发中,需要在这些已有的PHP扩展上开发,也只能用C/C++来开发了。
首先去PHP官网下载对应版本的PHP源码,解压并进入对应的目录。
创建扩展courages:
[vagrant@vagrant-centos64 ext]$ ./ext_skel ./ext_skel --extname=module [--proto=file] [--stubs=file] [--xml[=file]] [--skel=dir] [--full-xml] [--no-help] --extname=module module is the name of your extension --proto=file file contains prototypes of functions to create --stubs=file generate only function stubs in file --xml generate xml documentation to be added to phpdoc-cvs --skel=dir path to the skeleton directory --full-xml generate xml documentation for a self-contained extension (not yet implemented) --no-help don't try to be nice and create comments in the code and helper functions to test if the module compiled [vagrant@vagrant-centos64 ext]$ ./ext_skel --extname=courages Creating directory courages Creating basic files: config.m4 config.w32 .svnignore courages.c php_courages.h CREDITS EXPERIMENTAL tests/001.phpt courages.php [done]. To use your new extension, you will have to execute the following steps: 1. $ cd .. 2. $ vi ext/courages/config.m4 3. $ ./buildconf 4. $ ./configure --[with|enable]-courages 5. $ make 6. $ ./sapi/cli/php -f ext/courages/courages.php 7. $ vi ext/courages/courages.c 8. $ make Repeat steps 3-6 until you are satisfied with ext/courages/config.m4 and step 6 confirms that your module is compiled into PHP. Then, start writing code and repeat the last two steps as often as necessary.
这里的步骤说的很清楚,但这一次,步骤3被phpize代替了。
按部就班,编辑config.m4,PHP_ARG_WITH是采用动态库方式加载(PHP_ARG_ENABLE则是编译内核中,configure是–enable-extension使用),将
dnl PHP_ARG_WITH(courages, whether to enable courages support, dnl Make sure that the comment is aligned: dnl [ --with-courages Include courages support])
更改为
PHP_ARG_WITH(courages, for courages support, [ --with-courages Include courages support])
然后,在php_courages.h增加函数声明
PHP_FUNCTION(confirm_courages_compiled); /* For testing, remove later. */ PHP_FUNCTION(courages_helloworld);
接着,编辑courages.c,在function_entry中增加函数注册
const zend_function_entry courages_functions[] = { PHP_FE(confirm_courages_compiled, NULL) /* For testing, remove later. */ PHP_FE(courages_helloworld, NULL) PHP_FE_END /* Must be the last line in courages_functions[] */ };
然后是courages_helloworld函数实现
PHP_FUNCTION(courages_helloworld) { char *arg = NULL; int arg_len, len; char *strg; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &arg, &arg_len) == FAILURE) { return; } len = spprintf(&strg, 0, "Your input string: %s/n", arg); php_printf(strg); return SUCCESS; }
最后就是编译
phpize ./configure sudo make sudo make install sudo vim /etc/php.ini
在php.ini中增加扩展courages.so
[courages] extension = courages.so
测试一下
[vagrant@vagrant-centos64 courages]$ php -m | grep 'courages' courages [vagrant@vagrant-centos64 courages]$ php courages.php Functions available in the test extension: confirm_courages_compiled courages_helloworld Your input string: hellow world Congratulations! You have successfully modified ext/courages/config.m4. Module courages is now compiled into PHP.
到这里一个扩展的开发流程就结束了。
这里分享一些小技巧。
首先是如何在PHP扩展中获取PHP全局数组$_SERVER($_POST/GET)变量中的值:
static char *get_server_var_by_name(char *str){ // This code makes sure $_SERVER has been initialized if (!zend_hash_exists(&EG(symbol_table), "_SERVER", 8)) { zend_auto_global* auto_global; if (zend_hash_find(CG(auto_globals), "_SERVER", 8, (void **)&auto_global) != FAILURE) { auto_global->armed = auto_global->auto_global_callback(auto_global->name, auto_global->name_len TSRMLS_CC); } } // This fetches $_SERVER['PHP_SELF'] zval** arr; char* script_name; if (zend_hash_find(&EG(symbol_table), "_SERVER", 8, (void**)&arr) != FAILURE) { HashTable* ht = Z_ARRVAL_P(*arr); zval** val; if (zend_hash_find(ht, str, strlen(str)+1, (void**)&val) != FAILURE) { script_name = Z_STRVAL_PP(val); } } return script_name; }
然后是如何在PHP扩展调用PHP函数:
/*调用无参数函数*/ static char *get_sapi_name(){ zval *function_name; zval *retval; char *sapi_name; MAKE_STD_ZVAL(function_name); ZVAL_STRING(function_name , "php_sapi_name", 1); if (call_user_function_ex(EG(function_table), NULL, function_name, &retval, 0, NULL, 0, EG(active_symbol_table) TSRMLS_CC) != SUCCESS) { zend_error(E_ERROR, "Function call failed"); } if (retval != NULL && Z_TYPE_P(retval) != IS_STRING) { convert_to_string(retval); sapi_name = Z_STRVAL_P(retval); } else{ sapi_name = "cli"; } return sapi_name; } /*调用有参数函数*/ static int _ck_dir(char *dir TSRMLS_DC) { zval *function_name; zval *retval; zval *str; zval **param[1]; MAKE_STD_ZVAL(function_name); ZVAL_STRING(function_name , "is_dir", 1); MAKE_STD_ZVAL(str); ZVAL_STRING(str, dir, 1); param[0] = &str; if (call_user_function_ex(EG(function_table), NULL, function_name, &retval, 1, param, 0, EG(active_symbol_table) TSRMLS_CC) != SUCCESS) { zend_error(E_ERROR, "Function call failed"); } if (retval != NULL && zval_is_true(retval)) { return SUCCESS; } return FAILURE; }
更高级的一些技巧可以参考《PHP扩展开发及内核应用》和阅读别人的扩展开发代码。
参考链接:
php扩展实战 —— 获得ip的来源地址
如何编写一个PHP的C扩展
[原创]快速开发一个PHP扩展
用C/C++扩展你的PHP
Get the name of running script from a PHP extension
Build PHP extension and use call_user_function
Programming PHP
与 UNIX 构建系统交互: config.m4
call_user_function_ex() documentation
PHP Extensions Made Eldrich: PHP Variables
Convert Zval to char*
PHP扩展编写第一步:PHP和Zend介绍
PHP扩展开发:简单类实现
自己写PHP扩展之创建一个类[原创]
如何在扩展里调用PHP函数呢?