


Summary of PHP extension development, PHP extension summary_PHP tutorial
Summary of PHP extension development, PHP extension summary
1. Thread-safe macro definition
There is the following definition in the TSRM/TSRM.h file
#define TSRMLS_FETCH() void ***tsrm_ls = (void ***) ts_resource_ex(0, NULL) #define TSRMLS_FETCH_FROM_CTX(ctx) void ***tsrm_ls = (void ***) ctx #define TSRMLS_SET_CTX(ctx) ctx = (void ***) tsrm_ls #define TSRMG(id, type, element) (((type) (*((void ***) tsrm_ls))[TSRM_UNSHUFFLE_RSRC_ID(id)])->element) #define TSRMLS_D void ***tsrm_ls #define TSRMLS_DC , TSRMLS_D #define TSRMLS_C tsrm_ls #define TSRMLS_CC , TSRMLS_C
There is such a passage in ext/xsl/php_xsl.h
/* In every utility function you add that needs to use variables. in php_xsl_globals, call TSRM_FETCH(); after declaring other. variables used by that function, or better yet, pass in TSRMLS_CC after the last function argument and declare your utility function with TSRMLS_DC after the last declared argument. Always refer to the globals in your function as XSL_G(variable). You are. encouraged to rename these macros something shorter, see examples in any other php module directory. */
1. Add TSRMLS_D (use this if the method has no parameters) or TSRMLS_DC (with more than 1 parameter) when defining the method
2. Use TSRMLS_C (use this if the method has no parameters) or when calling the method TSRMLS_CC (has more than 1 parameter)
It should be understood this way
The first suffix letter D means definition, that is, D=Define, the first suffix letter C means call, that is, C=Call, and does the second suffix letter C mean a comma? C=Comma (comma)
TSRMLS_D就是定义了,所以是 void ***tsrm_ls TSRMLS_DC是带逗号的定义,所以是 , void ***tsrm_ls TSRMLS_C是调用,即tsrm_ls TSRMLS_CC是调用并带逗号,即 ,tsrm_ls
So one is a formal parameter and the other is an actual parameter
You can use it like this
int php_myext_action(int action_id, char *message TSRMLS_DC); php_myext_action(42, "The meaning of life" TSRMLS_CC);
It is generally recommended to use the tsrm_ls pointer definition method to ensure thread safety
TSRMLS_FETCH call requires a certain amount of processing time. This isn't noticeable in a single iteration, but as your thread count increases, and as the points at which you call TSRMLS_FETCH() increase, your scaling will reveal this bottleneck. Therefore, please use it with caution. Note: For compatibility with c compilers, make sure to place TSRMLS_FETCH() and all variable definitions at the top of the given block scope (before any other statements). Because the TSRMLS_FETCH() macro itself can be parsed in many different ways, it is best to use it as the last line of the variable definition
2. PHP life cycle
The two most common operating modes of PHP are WEB mode and CLI mode. No matter which mode, PHP works on the same principle, running as a SAPI.
1. When we type the php command in the terminal, it uses the CLI.
It is like a web server to support PHP to complete the request. After the request is completed, control is returned to the terminal.
2. When using Apache as the host, when a request comes, PHP will support the completion of the request
PHP_MINIT_FUNCTION 初始化module时运行 PHP_MSHUTDOWN_FUNCTION 当module被卸载时运行 PHP_RINIT_FUNCTION 当一个REQUEST请求初始化时运行 PHP_RSHUTDOWN_FUNCTION 当一个REQUEST请求结束时运行 PHP_MINFO_FUNCTION 这个是设置phpinfo中这个模块的信息 PHP_GINIT_FUNCTION 初始化全局变量时 PHP_GSHUTDOWN_FUNCTION 释放全局变量时
For example PHP_GINIT_FUNCTION
比如PHP_GINIT_FUNCTION PHP_GINIT_FUNCTION(test) { /** 初始化全局变量 */ } //对应的C代码 void zm_globals_ctor_test (zend_test_globals *test_globals TSRMLS_DC) { /** 初始化全局变量 */ } //在线程退出时,需要将之前自己申请的资源释放时,可以使用 PHP_GSHUTDOWN_FUNCTION来注册析构函数。 PHP_GSHUTDOWN_FUNCTION(test) { /** 清除全局变量 */ } //对应的C代码 void zm_globals_dtor_test (zend_test_globals *test_globals TSRMLS_DC) { /** 清除全局变量 */ }
Here is a piece of code that you can test
int minit_time; PHP_MINIT_FUNCTION(test) { minit_time = time(NULL); return SUCCESS; } PHP_MSHUTDOWN_FUNCTION(test) { FILE *fp=fopen("mshutdown.txt","a+"); fprintf(fp,"%ld\n",time(NULL)); fclose(fp); return SUCCESS; } int rinit_time; PHP_RINIT_FUNCTION(test) { rinit_time = time(NULL); return SUCCESS; } PHP_RSHUTDOWN_FUNCTION(test) { FILE *fp=fopen("rshutdown.txt","a+"); fprintf(fp,"%ld\n",time(NULL)); fclose(fp); return SUCCESS; } PHP_MINFO_FUNCTION(test) { php_info_print_table_start(); php_info_print_table_header(2, "module info", "enabled"); php_info_print_table_end(); /* Remove comments if you have entries in php.ini DISPLAY_INI_ENTRIES(); */ } PHP_FUNCTION(test) { php_printf("%d",time_of_minit); php_printf("%d",time_of_rinit); return; }
3. Segmentation fault debugging
C programs under Linux often cause segment faults due to memory access errors and other reasons. At this time, if the system core dump function is turned on, the memory image will be dumped to the hard disk, and then gdb can be used Analyze the core file and restore the stack situation when the system segmentation fault occurred. This is very helpful for us to find program bugs.
Use ulimit -a to view the size limit of the system core file; use ulimit -c [kbytes] to set the size of the core file that the system allows to generate.
ulimit -c 0 does not generate core files
ulimit -c 100 sets the maximum core file size to 100k
ulimit -c unlimited does not limit the core file size
Steps:
1. When a segmentation fault occurs, we check ulimit -a (core file size (blocks, -c) 0) and there is no file.
2. Setting: ulimit -c unlimited does not limit the core file size
3. Run the program. When a segmentation fault occurs, it will be automatically recorded in the core (php -f WorkWithArray.php)
4. ls -al core.* in that file (-rw------- 1 leconte leconte 139264 01-06 22:3 1 core.2065)
5. Use gdb to run the program and record the file with segmentation fault. (gdb ./test core.2065)
6. Will mention which line is wrong.
The default core file size of many systems is 0. We can specify it by adding the ulimit -c command to the shell startup script /etc/bashrc or ~/.bashrc. The size of the core file to ensure that the core file can be generated.
In addition, you can also set the file name template of the core file in /proc/sys/kernel/core_pattern. For details, please see the official man manual of core.
4. Common variable operation macros
CG -> Complier Global 编译时信息,包括函数表等(zend_globals_macros.h:32) EG -> Executor Global 执行时信息(zend_globals_macros.h:43) PG -> PHP Core Global 主要存储php.ini中的信息 SG -> SAPI Global SAPI信息
1. SG for SAPI information in the main/SAPI.h file
typedef struct _sapi_globals_struct { void *server_context; sapi_request_info request_info; sapi_headers_struct sapi_headers; int read_post_bytes; unsigned char headers_sent; struct stat global_stat; char *default_mimetype; char *default_charset; HashTable *rfc1867_uploaded_files; long post_max_size; int options; zend_bool sapi_started; double global_request_time; HashTable known_post_content_types; zval *callback_func; zend_fcall_info_cache fci_cache; zend_bool callback_run; } sapi_globals_struct;
Look at the definition of SG
BEGIN_EXTERN_C() #ifdef ZTS # define SG(v) TSRMG(sapi_globals_id, sapi_globals_struct *, v) SAPI_API extern int sapi_globals_id; #else # define SG(v) (sapi_globals.v) extern SAPI_API sapi_globals_struct sapi_globals; #endif SAPI_API void sapi_startup(sapi_module_struct *sf); SAPI_API void sapi_shutdown(void); SAPI_API void sapi_activate(TSRMLS_D); SAPI_API void sapi_deactivate(TSRMLS_D); SAPI_API void sapi_initialize_empty_request(TSRMLS_D); END_EXTERN_C()
The members are all here in sapi_globals_struct
Then can we call it like this
SG(default_mimetype) SG(request_info).request_uri
You can feel this piece of code
static int sapi_cgi_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC) { char buf[SAPI_CGI_MAX_HEADER_LENGTH]; sapi_header_struct *h; zend_llist_position pos; long rfc2616_headers = 0; if(SG(request_info).no_headers == 1) { return SAPI_HEADER_SENT_SUCCESSFULLY; } if (SG(sapi_headers).http_response_code != 200) { int len; len = sprintf(buf, "Status: %d\r\n", SG(sapi_headers).http_response_code); PHPWRITE_H(buf, len); } if (SG(sapi_headers).send_default_content_type) { char *hd; hd = sapi_get_default_content_type(TSRMLS_C); PHPWRITE_H("Content-type:", sizeof("Content-type: ")-1); PHPWRITE_H(hd, strlen(hd)); PHPWRITE_H("\r\n", 2); efree(hd); } h = zend_llist_get_first_ex(&sapi_headers->headers, &pos); while (h) { PHPWRITE_H(h->header, h->header_len); PHPWRITE_H("\r\n", 2); h = zend_llist_get_next_ex(&sapi_headers->headers, &pos); } PHPWRITE_H("\r\n", 2); return SAPI_HEADER_SENT_SUCCESSFULLY; }
2. EG Executor Globals
EG obtains the data in the struct _zend_execution_globals
structure
struct _zend_execution_globals { ... HashTable symbol_table; /* 全局作用域,如果没有进入函数内部,全局=活动 */ HashTable *active_symbol_table; /* 活动作用域,当前作用域 */ ... }
Usually, use EG(symbol_table)
to get the symbol table in the global scope, use EG(active_symbol_table)
to get the symbol table in the current scope
For example, to define $foo = 'bar'
zval *fooval; MAKE_STD_ZVAL(fooval); ZVAL_STRING(fooval, "bar", 1); ZEND_SET_SYMBOL(EG(active_symbol_table), "foo", fooval);
or find $foo
from the symbol tablezval **fooval; if(zend_hash_find(&EG(symbol_table), "foo", sizeof("foo"), (void **)&fooval) == SUCCESS) { RETURN_STRINGL(Z_STRVAL_PP(fooval), Z_STRLEN_PP(fooval)); } else { RETURN_FALSE; }
In the above code, EG(active_symbol_table) == &EG(symbol_table)
3. CG() is used to access core global variables. (zend/zend_globals_macros.h)
4. PG() PHP global variable. We know that php.ini will map one or more PHP global structures. (main/php_globals.h)
5. FG() file global variable. Most data streams for file I/O or related global variables are plugged into the standard extension exit structure. (ext/standard/file.h)
5. Get the type and value of the variable
#define Z_TYPE(zval) (zval).type #define Z_TYPE_P(zval_p) Z_TYPE(*zval_p) #define Z_TYPE_PP(zval_pp) Z_TYPE(**zval_pp)
For example, getting the type of a variable
void describe_zval(zval *foo) { if ( Z_TYPE_P(foo) == IS_NULL ) { php_printf("这个变量的数据类型是: NULL"); } else { php_printf("这个变量的数据类型不是NULL,这种数据类型对应的数字是: %d", Z_TYPE_P(foo)); } }
There are several types
#define IS_NULL 0 #define IS_LONG 1 #define IS_DOUBLE 2 #define IS_BOOL 3 #define IS_ARRAY 4 #define IS_OBJECT 5 #define IS_STRING 6 #define IS_RESOURCE 7 #define IS_CONSTANT 8 #define IS_CONSTANT_ARRAY 9 #define IS_CALLABLE 10
php_printf()函数是内核对printf()函数的一层封装,我们可以像使用printf()函数那样使用它,以一个P结尾的宏的参数大多是*zval型变量。 此外获取变量类型的宏还有两个,分别是Z_TYPE和Z_TYPE_PP,前者的参数是zval型,而后者的参数则是**zval
比如gettype函数的实现
//开始定义php语言中的函数gettype PHP_FUNCTION(gettype) { //arg间接指向调用gettype函数时所传递的参数。是一个zval**结构 //所以我们要对他使用__PP后缀的宏。 zval **arg; //这个if的操作主要是让arg指向参数~ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &arg) == FAILURE) { return; } //调用Z_TYPE_PP宏来获取arg指向zval的类型。 //然后是一个switch结构,RETVAL_STRING宏代表这gettype函数返回的字符串类型的值 switch (Z_TYPE_PP(arg)) { case IS_NULL: RETVAL_STRING("NULL", 1); break; case IS_BOOL: RETVAL_STRING("boolean", 1); break; case IS_LONG: RETVAL_STRING("integer", 1); break; case IS_DOUBLE: RETVAL_STRING("double", 1); break; case IS_STRING: RETVAL_STRING("string", 1); break; case IS_ARRAY: RETVAL_STRING("array", 1); break; case IS_OBJECT: RETVAL_STRING("object", 1); break; case IS_RESOURCE: { char *type_name; type_name = zend_rsrc_list_get_rsrc_type(Z_LVAL_PP(arg) TSRMLS_CC); if (type_name) { RETVAL_STRING("resource", 1); break; } } default: RETVAL_STRING("unknown type", 1); } }
获取变量的值,有这么多宏来获取
Long |
Boolean |
Double |
String value |
String length |
---|---|---|---|---|
<pre class="code">Z_LVAL( ) Copy after login |
<pre class="code">Z_BVAL( ) Copy after login |
<pre class="code">Z_DVAL( ) Copy after login |
<pre class="code">Z_STRVAL( ) Copy after login |
<pre class="code">Z_STRLEN( ) Copy after login |
<pre class="code">Z_LVAL_P( ) Copy after login |
<pre class="code">Z_BVAL_P( ) Copy after login |
<pre class="code">Z_DVAL_P( ) Copy after login |
<pre class="code">Z_STRVAL_P( ) Copy after login |
<pre class="code">Z_STRLEN_P( ) Copy after login |
<pre class="code">Z_LVAL_PP( ) Copy after login |
<pre class="code">Z_BVAL_PP( ) Copy after login |
<pre class="code">Z_DVAL_PP( ) Copy after login |
<pre class="code">Z_STRVAL_PP( ) Copy after login |
<pre class="code">Z_STRLEN_PP( ) Copy after login |
HashTable |
Object |
Object properties |
Object class entry |
Resource value |
<pre class="code">Z_ARRVAL( ) Copy after login |
<pre class="code">Z_OBJ( ) Copy after login |
<pre class="code">Z_OBJPROP( ) Copy after login |
<pre class="code">Z_OBJCE( ) Copy after login |
<pre class="code">Z_RESVAL( ) Copy after login |
<pre class="code">Z_ARRVAL_P( ) Copy after login |
<pre class="code">Z_OBJ_P( ) Copy after login |
<pre class="code">Z_OBJPROP_P( ) Copy after login |
<pre class="code">Z_OBJCE_P( ) Copy after login |
<pre class="code">Z_RESVAL_P( ) Copy after login |
<pre class="code">Z_ARRVAL_PP( ) Copy after login |
<pre class="code">Z_OBJ_PP( ) Copy after login |
<pre class="code">Z_OBJPROP_PP( ) Copy after login |
<pre class="code">Z_OBJCE_PP( ) Copy after login |
<pre class="code">Z_RESVAL_PP( ) Copy after login |
rot13函数的实现
PHP_FUNCTION(rot13) { zval **arg; char *ch, cap; int i; if (ZEND_NUM_ARGS( ) != 1 || zend_get_parameters_ex(1, &arg) == FAILURE) { WRONG_PARAM_COUNT; } *return_value = **arg; zval_copy_ctor(return_value); convert_to_string(return_value); for(i=0, ch=return_value->value.str.val; i<return_value->value.str.len; i++, ch++) { cap = *ch & 32; *ch &= ~cap; *ch = ((*ch>='A') && (*ch<='Z') ? ((*ch-'A'+13) % 26 + 'A') : *ch) | cap; } }
要获取变量的值,也应该使用Zend定义的宏进行访问。对于简单的标量数据类型、Boolean,long,double, 使用Z_BVAL
, Z_LVAL
, Z_DVAL
void display_values(zval boolzv, zval *longpzv, zval **doubleppzv) { if (Z_TYPE(boolzv) == IS_BOOL) { php_printf("The value of the boolean is : %s\n", Z_BVAL(boolzv) ? "true" : "false"); } if(Z_TYPE_P(longpzv) == IS_LONG) { php_printf("The value of the long is: %ld\n", Z_LVAL_P(longpzv)); } if(Z_TYPE_PP(doubleppzv) == IS_DOUBLE) { php_printf("The value of the double is : %f\n", Z_DVAL_PP(doubleppzv)); } }
对于字符串类型,因为它含有两个字段char *
(Z_STRVAL) 和 int
(Z_STRLEN),因此需要用两个宏来进行取值,因为需要二进制安全的输出这个字符串
void display_string(zval *zstr) { if (Z_TYPE_P(zstr) != IS_STRING) { php_printf("The wronng datatype was passed!\n"); return ; } PHPWRITE(Z_STRVAL_P(zstr), Z_STRLEN_P(zstr)); }
因为数组在zval中是以HashTable形式存在的,因此使用Z_ARRVAL()
进行访问
void display_zval(zval *value) { switch (Z_TYPE_P(value)) { case IS_NULL: /* 如果是NULL,则不输出任何东西 */ break; case IS_BOOL: /* 如果是bool类型,并且true,则输出1,否则什么也不干 */ if (Z_BVAL_P(value)) { php_printf("1"); } break; case IS_LONG: /* 如果是long整型,则输出数字形式 */ php_printf("%ld", Z_LVAL_P(value)); break; case IS_DOUBLE: /* 如果是double型,则输出浮点数 */ php_printf("%f", Z_DVAL_P(value)); break; case IS_STRING: /* 如果是string型,则二进制安全的输出这个字符串 */ PHPWRITE(Z_STRVAL_P(value), Z_STRLEN_P(value)); break; case IS_RESOURCE: /* 如果是资源,则输出Resource #10 格式的东东 */ php_printf("Resource #%ld", Z_RESVAL_P(value)); break; case IS_ARRAY: /* 如果是Array,则输出Array5个字母! */ php_printf("Array"); break; case IS_OBJECT: php_printf("Object"); break; default: /* Should never happen in practice, * but it's dangerous to make assumptions */ php_printf("Unknown"); break; } }
一些类型转换函数
ZEND_API void convert_to_long(zval *op); ZEND_API void convert_to_double(zval *op); ZEND_API void convert_to_null(zval *op); ZEND_API void convert_to_boolean(zval *op); ZEND_API void convert_to_array(zval *op); ZEND_API void convert_to_object(zval *op); ZEND_API void _convert_to_string(zval *op ZEND_FILE_LINE_DC);
6、常量的实例化
我们可以这样实例化
PHP_MINIT_FUNCTION(consts) //模块初始化时定义常量 { REGISTER_LONG_CONSTANT("CONSTS_MEANING_OF_LIFE", 42, CONST_CS | CONST_PERSISTENT); REGISTER_DOUBLE_CONSTANT("CONSTS_PI", 3.1415926, CONST_PERSISTENT); REGISTER_STRING_CONSTANT("CONSTS_NAME", "leon", CONST_CS|CONST_PERSISTENT); } PHP_RINIT_FUNCTION(consts) //每次请求时定义常量 { char buffer[40]; srand((int)time(NULL)); snprintf(buffer, sizeof(buffer), "%d", rand()); REGISTER_STRING_CONSTANT("CONSTS_RAND", estrdup(buffer), CONST_CS); return SUCCESS; }
常见的宏
/*注册LONG类型常量*/ #define REGISTER_LONG_CONSTANT(name, lval, flags) zend_register_long_constant((name), sizeof(name), (lval), (flags), module_number TSRMLS_CC) /*注册double类型常量*/ #define REGISTER_DOUBLE_CONSTANT(name, dval, flags) zend_register_double_constant((name), sizeof(name), (dval), (flags), module_number TSRMLS_CC) /*注册STRING类型常量*/ #define REGISTER_STRING_CONSTANT(name, str, flags) zend_register_string_constant((name), sizeof(name), (str), (flags), module_number TSRMLS_CC) /*注册STRING类型常量*/ #define REGISTER_STRINGL_CONSTANT(name, str, len, flags) zend_register_stringl_constant((name), sizeof(name), (str), (len), (flags), module_number TSRMLS_CC)
7、全局变量
#php-fpm 生成 POST|GET|COOKIE|SERVER|ENV|REQUEST|FILES全局变量的流程 php_cgi_startup() -> php_module_startup() -> php_startup_auto_globals() -> 保存变量到symbol_table符号表 php_cgi_startup()在 fpm/fpm/fpm_main.c中定义 php_module_startup() 在main/main.c中定义 php_startup_auto_globals() 在main/php_variables.h中定义 zend_hash_update(&EG(symbol_table), "_GET", sizeof("_GET") + 1, &vars, sizeof(zval *), NULL); /* 读取$_SERVER变量 */ static PHP_FUNCTION(print_server_vars) { zval **val; if (zend_hash_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER"), (void **)&val) == SUCCESS) { RETURN_ZVAL(*val, 1, 0); }else{ RETURN_FALSE; } } /* 读取$_SERVER[$name] */ ZEND_BEGIN_ARG_INFO(print_server_var_arginfo, 0) ZEND_ARG_INFO(0, "name") ZEND_END_ARG_INFO() static PHP_FUNCTION(print_server_var) { char *name; int name_len; zval **val; HashTable *ht_vars = NULL; HashPosition pos; zval **ret_val; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!", &name, &name_len) == FAILURE) { RETURN_NULL(); } if (zend_hash_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER"), (void **)&val) == SUCCESS) { ht_vars = Z_ARRVAL_PP(val); //此处需传入大于name长度+1的值,因为字符串值后面需要'\0' if (zend_hash_find(ht_vars, name, name_len+1, (void **)&ret_val) == SUCCESS) { RETURN_STRING(Z_STRVAL_PP(ret_val), 0); }else{ RETURN_NULL(); } }else{ RETURN_NULL(); } }
8、包装第三方库
配置(config.m4)
SEARCH_PATH="/usr/local /usr" #lib搜索的目录 SEARCH_FOR="/include/curl/curl.h" #lib头文件的路径 if test -r $PHP_LIBS/$SEARCH_FOR; then LIBS_DIR=$PHP_LIBS else # search default path list AC_MSG_CHECKING([for libs files in default path]) for i in $SEARCH_PATH ; do if test -r $i/$SEARCH_FOR; then LIBS_DIR=$i #搜索到的lib的路径 AC_MSG_RESULT(found in $i) fi done fi /*验证lib是否存在*/ if test -z "$LIBS_DIR"; then AC_MSG_RESULT([not found]) AC_MSG_ERROR([Please reinstall the libs distribution]) fi /*编译的时候添加lib的include目录, -I/usr/include*/ PHP_ADD_INCLUDE($LIBS_DIR/include) LIBNAME=curl #lib名称 LIBSYMBOL=curl_version #lib的一个函数,用来PHP_CHECK_LIBRARY验证lib /*验证lib*/ PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL, [ PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $LIBS_DIR/$PHP_LIBDIR, LIBS_SHARED_LIBADD) #编译的时候链接lib, -llibcurl AC_DEFINE(HAVE_LIBSLIB,1,[ ]) ],[ AC_MSG_ERROR([wrong libs lib version or lib not found]) ],[ -L$LIBS_DIR/$PHP_LIBDIR -lm ]) PHP_SUBST(LIBS_SHARED_LIBADD)
9、用于返回的宏
//这些宏都定义在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语言中的复合类型我们需要在函数中返回,如数组和对象,我们可以通过RETVAL_ZVAL与RETURN_ZVAL来操作它们
10、hashTable的遍历函数
//基于long key的操作函数 zval *v3; MAKE_STD_ZVAL(v3); ZVAL_STRING(v3, "value3", 1); zend_hash_index_update(names, 0, &v3, sizeof(zval *), NULL);//按数字索引键更新HashTable元素的值 zval **v4; zend_hash_index_find(names, 1, &v4); //按数字索引获取HashTable元素的值 php_printf("v4 : "); PHPWRITE(Z_STRVAL_PP(v4), Z_STRLEN_PP(v4)); php_printf("\n"); ulong idx; idx = zend_hash_index_exists(names, 10);//按数字索引查找HashTable,如果找到返回 1, 反之则返回 0 zend_hash_index_del(names, 2); //按数字索引删除HashTable元素 //hashTable的遍历函数 zend_hash_internal_pointer_reset(names); //初始化hash指针 zend_hash_internal_pointer_reset_ex(names, &pos);//初始化hash指针,并付值给pos zend_hash_get_current_data(names, (void**) &val); //获取当前hash存储值,data should be cast to void**, ie: (void**) &data zend_hash_get_current_data_ex(names, (void**) &val, &pos) == SUCCESS; //获取当前hash存储值 zend_hash_get_current_key(names, &key, &klen, &index, 0) == HASH_KEY_IS_LONG zend_hash_get_current_key_ex(names, &key, &klen, &index, 0, &pos) == HASH_KEY_IS_LONG; //读取hashtable当前的KEY,返回值会有两种 HASH_KEY_IS_LONG | HASH_KEY_IS_STRING ,分别对应array("value"),array("key"=>"value")两种hashtable zend_hash_move_forward(names); zend_hash_move_forward_ex(names, &pos); //hash指针移至下一位 //HashTable长度 php_printf("%*carray(%d) {\n", depth * 2, ' ', zend_hash_num_elements(Z_ARRVAL_P(zv))
一个简单的函数
function hello_array_strings($arr) { if (!is_array($arr)) return NULL; printf("The array passed contains %d elements ", count($arr)); foreach($arr as $data) { if (is_string($data)) echo "$data "; } }
PHP内核实现
PHP_FUNCTION(hello_array_strings) { zval *arr, **data; HashTable *arr_hash; HashPosition pointer; int array_count; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &arr) == FAILURE) { RETURN_NULL(); } arr_hash = Z_ARRVAL_P(arr); array_count = zend_hash_num_elements(arr_hash); php_printf("The array passed contains %d elements ", array_count); for(zend_hash_internal_pointer_reset_ex(arr_hash, &pointer); zend_hash_get_current_data_ex(arr_hash, (void**) &data, &pointer) == SUCCESS; zend_hash_move_forward_ex(arr_hash, &pointer)) { if (Z_TYPE_PP(data) == IS_STRING) { PHPWRITE(Z_STRVAL_PP(data), Z_STRLEN_PP(data)); php_printf(" "); } } RETURN_TRUE; }
参考文章
http://php.find-info.ru/php/016/ch23lev1sec1.html
http://docstore.mik.ua/orelly/webprog/php/ch14_10.htm
http://wiki.jikexueyuan.com/project/extending-embedding-php/2.4.html
http://www.php-internals.com/book/?p=chapt02/02-03-02-opcode
http://blog.csdn.net/phpkernel/article/details/5721134
https://github.com/chenpingzhao/php-ext-trie-filter/blob/master/trie_filter.c
http://aicode.cc/tags/php%E6%89%A9%E5%B1%95/
http://www.phpboy.net/2013-12/28-php-stream-wrapper.html
http://thiniki.sinaapp.com/?p=163
http://top.jobbole.com/26400/
https://github.com/walu/phpbook/blob/master/9.1.md

Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

AI Hentai Generator
Generate AI Hentai for free.

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

Hot Topics



In PHP language development, request header errors are usually caused by some problems in HTTP requests. These issues may include invalid request headers, missing request bodies, and unrecognized encoding formats. Correctly handling these request header errors is the key to ensuring application stability and security. In this article, we will discuss some best practices for handling PHP request header errors to help you build more reliable and secure applications. Checking the request method The HTTP protocol specifies a set of available request methods (e.g. GET, POS

PHP is a very popular programming language that allows developers to create a wide variety of applications. However, sometimes when writing PHP code, we need to handle and validate characters. This is where PHP's Ctype extension comes in handy. This article will introduce how to use PHP's Ctype extension. What are Ctype extensions? The Ctype extension for PHP is a very useful tool that provides various functions to verify the character type in a string. These functions include isalnum, is

With the development of Internet technology, more and more websites and applications are developed using PHP language. However, security issues also arise. One of the common security issues is path traversal vulnerabilities. In this article, we will explore how to avoid path traversal vulnerabilities in PHP language development to ensure application security. What is a path traversal vulnerability? Path traversal vulnerability (PathTraversal) is a common web vulnerability that allows an attacker to access the web server without authorization.

In PHP programming, Behat is a very useful tool that can help programmers better understand business requirements during the development process and ensure the quality of the code. In this article, we will introduce how to use Behat in PHP programming. 1. What is Behat? Behat is a behavior-driven development (BDD) framework that couples PHP code through language description (use cases written in Gherkin language), thereby enabling code and business requirements to work together. Use Behat to do

In modern development, unit testing has become a necessary step. It can be used to ensure that your code behaves as expected and that bugs can be fixed at any time. In PHP development, Phpt is a very popular unit testing tool, which is very convenient to write and execute unit tests. In this article, we will explore how to use Phpt for unit testing. 1. What is PhptPhpt is a simple but powerful unit testing tool, which is part of PHP testing. Phpt test cases are a series of PHP source code snippets whose

PHP language supports 3 comment styles: 1. C++ style, using the "//" symbol and the syntax "//comment content"; 2. C language style, using the "/* */" symbol and the syntax "/* comment content*" /"; 3. Shell style (Perl style), using the "#" symbol and the syntax "#comment content".

In PHP language development, it is often necessary to parse JSON data for subsequent data processing and operations. However, when parsing JSON, it is easy to encounter various errors and problems. This article will introduce common errors and processing methods to help PHP developers better process JSON data. 1. JSON format error The most common error is that the JSON format is incorrect. JSON data must comply with the JSON specification, that is, the data must be a collection of key-value pairs, and use curly brackets ({}) and square brackets ([]) to contain the data.

LDAP (Lightweight Directory Access Protocol) is a common network protocol used to access and manage directory services. In PHP language development, LDAP is often used to interact with external LDAP directory services, such as identity authentication and user authorization. However, due to the nature of LDAP, it also has some security vulnerabilities, such as LDAP injection and LDAP override issues. This article will explore how to avoid LDAP-related vulnerabilities in PHP language development. Avoid LDAP injection LDAP injection is a common security vulnerability, something like
