前面介绍了使用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函数呢?