-
Notifications
You must be signed in to change notification settings - Fork 0
PHP Extension Develop and Test
yourlovemyall edited this page Oct 25, 2017
·
7 revisions
$ /$path/phpize
$ ./configure + php-cofnig
$ make && make install
$ 添加生成的 ext.so 到 php.ini
- config.m4为配置定义文件,用来读取编译命令。
- myext.so为编译后生成的二进制文件。
- 代码在myext.c中,编译完成并且加入到php.ini后。
- 运行 $ php test.php
- config.m4
- php_{extname}.h
- {extname}.c
dnl $Id$
dnl config.m4 for extension myext
dnl Comments in this file start with the string 'dnl'.
dnl Remove where necessary. This file will not work
dnl without editing.
dnl If your extension references something external, use with:
dnl PHP_ARG_WITH(myext, for myext support,
dnl Make sure that the comment is aligned:
dnl [ --with-myext Include myext support])
dnl Otherwise use enable:
PHP_ARG_ENABLE(myext, whether to enable myext support,
Make sure that the comment is aligned:
[ --enable-myext Enable myext support])
if test "$PHP_MYEXT" != "no"; then
dnl Write more examples of tests here...
dnl # --with-myext -> check with-path
dnl SEARCH_PATH="/usr/local /usr" # you might want to change this
dnl SEARCH_FOR="/include/myext.h" # you most likely want to change this
dnl if test -r $PHP_MYEXT/$SEARCH_FOR; then # path given as parameter
dnl MYEXT_DIR=$PHP_MYEXT
dnl else # search default path list
dnl AC_MSG_CHECKING([for myext files in default path])
dnl for i in $SEARCH_PATH ; do
dnl if test -r $i/$SEARCH_FOR; then
dnl MYEXT_DIR=$i
dnl AC_MSG_RESULT(found in $i)
dnl fi
dnl done
dnl fi
dnl
dnl if test -z "$MYEXT_DIR"; then
dnl AC_MSG_RESULT([not found])
dnl AC_MSG_ERROR([Please reinstall the myext distribution])
dnl fi
dnl # --with-myext -> add include path
dnl PHP_ADD_INCLUDE($MYEXT_DIR/include)
dnl # --with-myext -> check for lib and symbol presence
dnl LIBNAME=myext # you may want to change this
dnl LIBSYMBOL=myext # you most likely want to change this
dnl PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL,
dnl [
dnl PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $MYEXT_DIR/lib, MYEXT_SHARED_LIBADD)
dnl AC_DEFINE(HAVE_MYEXTLIB,1,[ ])
dnl ],[
dnl AC_MSG_ERROR([wrong myext lib version or lib not found])
dnl ],[
dnl -L$MYEXT_DIR/lib -lm
dnl ])
dnl
dnl PHP_SUBST(MYEXT_SHARED_LIBADD)
PHP_NEW_EXTENSION(myext, myext.c, $ext_shared)
fi
AC_MSG_CHECKING(message)
在执行 configure 命令时输出“checking <message>”等信息。
AC_MSG_RESULT(value)
取得 AC_MSG_CHECKING 的执行结果,一般情况下 value 应为 yes 或 no。
AC_MSG_ERROR(message)
在执行 configure 命令时输出一条错误消息 message 并中止脚本的执行。
AC_DEFINE(name,value,description)
向 php_config.h 添加一行定义:#define name value // description (这对模块的条件编译很有用。)
AC_ADD_INCLUDE(path)
添加一条编译器的包含路径,比如用于模块需要为头文件添加搜索路径。
AC_ADD_LIBRARY_WITH_PATH(libraryname,librarypath)
指定一个库的连接路径。
AC_ARG_WITH(modulename,description,unconditionaltest,conditionaltest)
这是一款比较强大的宏,用于将模块的描述 description 添加到“configure –help”命令的输出里面。PHP 会检查当前执行的 configure 脚本里面有没有–with-<modulename> 这个选项。 如果有则执行 unconditionaltest 语句(比如 –with-myext=yes 等), 此时,选项的值会被包含在 $withval 变量里面。否则就执行 conditionaltest 语句。
PHP_NEW_EXTENSION(myredis, myredis.c library.c, $ext_shared)
zend_module_entry myext_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
STANDARD_MODULE_HEADER,
#endif
"myext",
NULL, /*const struct _zend_function_entry *functions*/
PHP_MINIT(myext),
PHP_MSHUTDOWN(myext),
PHP_RINIT(myext), /* Replace with NULL if there's nothing to do at request start */
PHP_RSHUTDOWN(myext), /* Replace with NULL if there's nothing to do at request end */
PHP_MINFO(myext),
#if ZEND_MODULE_API_NO >= 20010901
PHP_MYEXT_VERSION,
#endif
STANDARD_MODULE_PROPERTIES
};
extern zend_module_entry myext_module_entry;
typedef struct _zend_module_entry zend_module_entry;
struct _zend_module_entry {
unsigned short size;
unsigned int zend_api;
unsigned char zend_debug;
unsigned char zts;
const struct _zend_ini_entry *ini_entry;
const struct _zend_module_dep *deps;
const char *name;
const struct _zend_function_entry *functions;
int (*module_startup_func)(INIT_FUNC_ARGS);
int (*module_shutdown_func)(SHUTDOWN_FUNC_ARGS);
int (*request_startup_func)(INIT_FUNC_ARGS);
int (*request_shutdown_func)(SHUTDOWN_FUNC_ARGS);
void (*info_func)(ZEND_MODULE_INFO_FUNC_ARGS);
const char *version;
size_t globals_size;
#ifdef ZTS
ts_rsrc_id* globals_id_ptr;
#else
void* globals_ptr;
#endif
void (*globals_ctor)(void *global TSRMLS_DC);
void (*globals_dtor)(void *global TSRMLS_DC);
int (*post_deactivate_func)(void);
int module_started;
unsigned char type;
void *handle;
int module_number;
char *build_id;
};
上面生成的结构体中和已上源码结构体是匹配上的。其中有几个指针函数(module_startup_func,module_shutdown_func,request_startup_func,request_shutdown_func),这四个函数会在相应时机被调用,分别是“扩展模块加载时”、“扩展模块卸载时”、“每个请求开始时”和“每个请求结束时”。这四个函数可以看成是一种拦截机制,主要用于相应时机的资源分配、释放等相关操作。 (后面再说)
ZEND_FUNCTION(say_hello)
{
php_printf("Hello World!");
}
static zend_function_entry say_hello_functions[] = {
ZEND_FE(say_hello, NULL)
{
NULL,
NULL,
NULL
}
};
zend_module_entry gglinux_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
STANDARD_MODULE_HEADER,
#endif
"say_hello",
say_hello_functions, /* Functions */
NULL, /* MINIT */
NULL, /* MSHUTDOWN */
NULL, /* RINIT */
NULL, /* RSHUTDOWN */
NULL, /* MINFO */
#if ZEND_MODULE_API_NO >= 20010901
"2.1",
#endif
STANDARD_MODULE_PROPERTIES
};
#define INTERNAL_FUNCTION_PARAMETERS int ht, zval *return_value, zval **return_value_ptr, zval *this_ptr, int return_value_used TSRMLS_DC
int ht
zval *return_value,我们在函数内部修改这个指针,函数执行完成后,内核将把这个指针指向的zval返回给用户端的函数调用者。
zval **return_value_ptr,
zval *this_ptr,如果此函数是一个类的方法,那么这个指针的含义和PHP语言中$this变量差不多。
int return_value_used,代表用户端在调用此函数时有没有使用到它的返回值。
ZVAL_LONG(return_value, 42);
Z_TYPE_P(return_value) = IS_LONG;
Z_LVAL_P(return_value) = 42;
//更彻底的讲,应该是这样的:
return_value->type = IS_LONG;
return_value->value.lval = 42;
return_value如此重要,内核肯定早已经为它准备了大量的宏,来简化我们的操作,提高程序的质量。 在前几章我们接触的宏大多都是以ZVAL_开头的,而接下来我们要介绍的宏的名字是:RETVAL。 再回到上面的那个例子,我们用RETVAL来重写一下:
RETVAL_LONG(42);
// 下面是其他的例子
/*
//这些宏都定义在Zend/zend_API.h文件里
#define RETVAL_RESOURCE(l) ZVAL_RESOURCE(return_value, l)
#define RETVAL_BOOL(b) ZVAL_BOOL(return_value, b)
#define RETVAL_NULL() ZVAL_NULL(return_value)
#define RETVAL_LONG(l) ZVAL_LONG(return_value, l)
#define RETVAL_DOUBLE(d) ZVAL_DOUBLE(return_value, d)
#define RETVAL_STRING(s, duplicate) ZVAL_STRING(return_value, s, duplicate)
#define RETVAL_STRINGL(s, l, duplicate) ZVAL_STRINGL(return_value, s, l, duplicate)
#define RETVAL_EMPTY_STRING() ZVAL_EMPTY_STRING(return_value)
#define RETVAL_ZVAL(zv, copy, dtor) ZVAL_ZVAL(return_value, zv, copy, dtor)
#define RETVAL_FALSE ZVAL_BOOL(return_value, 0)
#define RETVAL_TRUE ZVAL_BOOL(return_value, 1)
#define RETURN_RESOURCE(l) { RETVAL_RESOURCE(l); return; }
#define RETURN_BOOL(b) { RETVAL_BOOL(b); return; }
#define RETURN_NULL() { RETVAL_NULL(); return;}
#define RETURN_LONG(l) { RETVAL_LONG(l); return; }
#define RETURN_DOUBLE(d) { RETVAL_DOUBLE(d); return; }
#define RETURN_STRING(s, duplicate) { RETVAL_STRING(s, duplicate); return; }
#define RETURN_STRINGL(s, l, duplicate) { RETVAL_STRINGL(s, l, duplicate); return; }
#define RETURN_EMPTY_STRING() { RETVAL_EMPTY_STRING(); return; }
#define RETURN_ZVAL(zv, copy, dtor) { RETVAL_ZVAL(zv, copy, dtor); return; }
#define RETURN_FALSE { RETVAL_FALSE; return; }
#define RETURN_TRUE { RETVAL_TRUE; return; }
*/
<?php
function sample_array_range() {
$ret = array();
for($i = 0; $i < 1000; $i++) {
$ret[] = $i;
}
return $ret;
}
sample_array_range();
ZEND_FUNCTION(sample_array_range)
{
if (return_value_used) {
int i;
//把返回值初始化成一个PHP语言中的数组
array_init(return_value);
for(i = 0; i < 1000; i++)
{
//向retrun_value里不断的添加新元素,值为i
add_next_index_long(return_value, i);
}
return;
}
else
{
//抛出一个E_NOTICE级错误
php_error_docref(NULL TSRMLS_CC, E_NOTICE,"猫了个咪的,我就知道你没用我的劳动成果!");
RETURN_NULL();
}
}
ZEND_FUNCTION(sample_getlong) {
long foo;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,"l", &foo) == FAILURE)
{
RETURN_NULL();
}
php_printf("The integer value of the parameter is: %ld\n", foo);
RETURN_TRUE;
}