[php-src] 理解Php内核中的函数与INI
内容均以php-5.6.14为例.
一. 函数结构
内核中定义一个php函数使用 PHP_FUNCTION 宏 包装,扩展也不例外,该宏在 ./main/php.h:343
有着一系列类似以 PHP 命名的 Zend 宏包装器,它们是:
/* PHP-named Zend macro wrappers */ /* 以PHP命名的Zend宏包装器 */ #define PHP_FN ZEND_FN #define PHP_MN ZEND_MN #define PHP_NAMED_FUNCTION ZEND_NAMED_FUNCTION #define PHP_FUNCTION ZEND_FUNCTION /* PHP_FUNCTION 就是 ZEND_FUNCTION */ #define PHP_METHOD ZEND_METHOD #define PHP_RAW_NAMED_FE ZEND_RAW_NAMED_FE #define PHP_NAMED_FE ZEND_NAMED_FE #define PHP_FE ZEND_FE #define PHP_DEP_FE ZEND_DEP_FE #define PHP_FALIAS ZEND_FALIAS #define PHP_DEP_FALIAS ZEND_DEP_FALIAS #define PHP_ME ZEND_ME #define PHP_MALIAS ZEND_MALIAS #define PHP_ABSTRACT_ME ZEND_ABSTRACT_ME #define PHP_ME_MAPPING ZEND_ME_MAPPING #define PHP_FE_END ZEND_FE_END #define PHP_MODULE_STARTUP_N ZEND_MODULE_STARTUP_N #define PHP_MODULE_SHUTDOWN_N ZEND_MODULE_SHUTDOWN_N #define PHP_MODULE_ACTIVATE_N ZEND_MODULE_ACTIVATE_N #define PHP_MODULE_DEACTIVATE_N ZEND_MODULE_DEACTIVATE_N #define PHP_MODULE_INFO_N ZEND_MODULE_INFO_N #define PHP_MODULE_STARTUP_D ZEND_MODULE_STARTUP_D #define PHP_MODULE_SHUTDOWN_D ZEND_MODULE_SHUTDOWN_D #define PHP_MODULE_ACTIVATE_D ZEND_MODULE_ACTIVATE_D #define PHP_MODULE_DEACTIVATE_D ZEND_MODULE_DEACTIVATE_D #define PHP_MODULE_INFO_D ZEND_MODULE_INFO_D /* Compatibility macros */ /* 兼容性宏 */ #define PHP_MINIT ZEND_MODULE_STARTUP_N #define PHP_MSHUTDOWN ZEND_MODULE_SHUTDOWN_N #define PHP_RINIT ZEND_MODULE_ACTIVATE_N #define PHP_RSHUTDOWN ZEND_MODULE_DEACTIVATE_N #define PHP_MINFO ZEND_MODULE_INFO_N #define PHP_GINIT ZEND_GINIT #define PHP_GSHUTDOWN ZEND_GSHUTDOWN #define PHP_MINIT_FUNCTION ZEND_MODULE_STARTUP_D /* 可用来定义模块初始时执行一些操作 */ #define PHP_MSHUTDOWN_FUNCTION ZEND_MODULE_SHUTDOWN_D /* 可用来定义模块卸载时执行一些操作 */ #define PHP_RINIT_FUNCTION ZEND_MODULE_ACTIVATE_D /* 可用来定义请求初始化时执行一些操作 */ #define PHP_RSHUTDOWN_FUNCTION ZEND_MODULE_DEACTIVATE_D /* 可用来定义请求结束时执行一些操作 */ #define PHP_MINFO_FUNCTION ZEND_MODULE_INFO_D /* 用来定义模块的信息,如phpinfo中的 */ #define PHP_GINIT_FUNCTION ZEND_GINIT_FUNCTION /* 用来初始化全局变量 */ #define PHP_GSHUTDOWN_FUNCTION ZEND_GSHUTDOWN_FUNCTION #define PHP_MODULE_GLOBALS ZEND_MODULE_GLOBALS
ZEND_FUNCTION 在 ./Zend/zend_API.h:68
#define ZEND_FN(name) zif_##name #define ZEND_MN(name) zim_##name #define ZEND_NAMED_FUNCTION(name) void name(INTERNAL_FUNCTION_PARAMETERS) #define ZEND_FUNCTION(name) ZEND_NAMED_FUNCTION(ZEND_FN(name)) #define ZEND_METHOD(classname, name) ZEND_NAMED_FUNCTION(ZEND_MN(classname##_##name))
函数参数 INTERNAL_FUNCTION_PARAMETERS 在 ./Zend/zend.h:290
#define INTERNAL_FUNCTION_PARAMETERS int ht, zval *return_value, zval **return_value_ptr, zval *this_ptr, int return_value_used TSRMLS_DC #define INTERNAL_FUNCTION_PARAM_PASSTHRU ht, return_value, return_value_ptr, this_ptr, return_value_used TSRMLS_CC
也就是说,使用 PHP_FUNCTION(abc) 定义一个abc函数,预处理结果是 zif_abc(INTERNAL_FUNCTION_PARAMETERS),下面做个试验:
/* 1.c */ #include <stdio.h> #define ZEND_FN(name) zif_##name #define ZEND_MN(name) zim_##name #define ZEND_NAMED_FUNCTION(name) void name(INTERNAL_FUNCTION_PARAMETERS) #define ZEND_FUNCTION(name) ZEND_NAMED_FUNCTION(ZEND_FN(name)) #define ZEND_METHOD(classname, name) ZEND_NAMED_FUNCTION(ZEND_MN(classname##_##name)) #define INTERNAL_FUNCTION_PARAMETERS int ht, zval *return_value, zval **return_value_ptr, zval *this_ptr, int return_value_used TSRMLS_DC int main() { ZEND_FUNCTION(abc); return 0; }
下次看到 ZEND_FN() 就可以想到是 zend function name,看到 ZEND_MN() 就能想到 zend method name.
使用 `gcc -E -o 1.i 1.c` 生成预处理文件 1.i ,`tail 1.i` 看文件最后的结果,内核中一个函数的形式如下:
int main() { void zif_abc(int ht, zval *return_value, zval **return_value_ptr, zval *this_ptr, int return_value_used TSRMLS_DC); return 0; }
PHP_MINIT_FUNCTION 和 ZEND_MINIT_FUNCTION 的效果是一样的,看下面:
./Zend/zend_API.h:126, 748
.... /* Name macros */ #define ZEND_MODULE_STARTUP_N(module) zm_startup_##module #define ZEND_MODULE_SHUTDOWN_N(module) zm_shutdown_##module #define ZEND_MODULE_ACTIVATE_N(module) zm_activate_##module #define ZEND_MODULE_DEACTIVATE_N(module) zm_deactivate_##module #define ZEND_MODULE_POST_ZEND_DEACTIVATE_N(module) zm_post_zend_deactivate_##module #define ZEND_MODULE_INFO_N(module) zm_info_##module #define ZEND_MODULE_GLOBALS_CTOR_N(module) zm_globals_ctor_##module #define ZEND_MODULE_GLOBALS_DTOR_N(module) zm_globals_dtor_##module /* Declaration macros */ #define ZEND_MODULE_STARTUP_D(module) int ZEND_MODULE_STARTUP_N(module)(INIT_FUNC_ARGS) #define ZEND_MODULE_SHUTDOWN_D(module) int ZEND_MODULE_SHUTDOWN_N(module)(SHUTDOWN_FUNC_ARGS) #define ZEND_MODULE_ACTIVATE_D(module) int ZEND_MODULE_ACTIVATE_N(module)(INIT_FUNC_ARGS) #define ZEND_MODULE_DEACTIVATE_D(module) int ZEND_MODULE_DEACTIVATE_N(module)(SHUTDOWN_FUNC_ARGS) #define ZEND_MODULE_POST_ZEND_DEACTIVATE_D(module) int ZEND_MODULE_POST_ZEND_DEACTIVATE_N(module)(void) #define ZEND_MODULE_INFO_D(module) void ZEND_MODULE_INFO_N(module)(ZEND_MODULE_INFO_FUNC_ARGS) #define ZEND_MODULE_GLOBALS_CTOR_D(module) void ZEND_MODULE_GLOBALS_CTOR_N(module)(zend_##module##_globals *module##_globals TSRMLS_DC) #define ZEND_MODULE_GLOBALS_DTOR_D(module) void ZEND_MODULE_GLOBALS_DTOR_N(module)(zend_##module##_globals *module##_globals TSRMLS_DC)
....
.... /* For compatibility */ #define ZEND_MINIT ZEND_MODULE_STARTUP_N #define ZEND_MSHUTDOWN ZEND_MODULE_SHUTDOWN_N #define ZEND_RINIT ZEND_MODULE_ACTIVATE_N #define ZEND_RSHUTDOWN ZEND_MODULE_DEACTIVATE_N #define ZEND_MINFO ZEND_MODULE_INFO_N #define ZEND_GINIT(module) ((void (*)(void* TSRMLS_DC))(ZEND_MODULE_GLOBALS_CTOR_N(module))) #define ZEND_GSHUTDOWN(module) ((void (*)(void* TSRMLS_DC))(ZEND_MODULE_GLOBALS_DTOR_N(module))) #define ZEND_MINIT_FUNCTION ZEND_MODULE_STARTUP_D #define ZEND_MSHUTDOWN_FUNCTION ZEND_MODULE_SHUTDOWN_D #define ZEND_RINIT_FUNCTION ZEND_MODULE_ACTIVATE_D #define ZEND_RSHUTDOWN_FUNCTION ZEND_MODULE_DEACTIVATE_D #define ZEND_MINFO_FUNCTION ZEND_MODULE_INFO_D #define ZEND_GINIT_FUNCTION ZEND_MODULE_GLOBALS_CTOR_D #define ZEND_GSHUTDOWN_FUNCTION ZEND_MODULE_GLOBALS_DTOR_D
....
所以如果我们要定义自己的启动函数原型,可以定义 ZEND_MINIT_FUNCTION 的兼容性宏,多文件时传入其它模块。
#define MY_STARTUP_FUNCTION(module) ZEND_MINIT_FUNCTION(my_##module)
这个 MY_STARTUP_FUNCTION 作用和 ZEND_MINIT_FUNCTION 一样,在这里是用在多文件中定义启动其它主文件模块的函数。
或者,直接定义成最终形式:
#define MY_STARTUP_FUNCTION(module) ZEND_MODULE_STARTUP_N(my_##module)(INIT_FUNC_ARGS)
这两种都是原型的定义。
自定义 启动/卸载 函数调用的宏:
#define MY_STARTUP(module) ZEND_MODULE_STARTUP_N(my_##module)(INIT_FUNC_ARGS_PASSTHRU)
#define MY_SHUTDOWN(module) ZEND_MODULE_SHUTDOWN_N(my_##module)(INIT_FUNC_ARGS_PASSTHRU)
这个 MY_STARTUP 是用在主模块的初始化函数中启动附加组件的。从参数可以看出,它是直接调用。
或者,采用高层宏的形式:
#define MY_STARTUP(module) ZEND_MINIT(my_##module)(INIT_FUNC_ARGS_PASSTHRU)
模块操作的参数能说明一些问题:
./Zend/zend_modules.h:30
.... #define INIT_FUNC_ARGS int type, int module_number TSRMLS_DC #define INIT_FUNC_ARGS_PASSTHRU type, module_number TSRMLS_CC #define SHUTDOWN_FUNC_ARGS int type, int module_number TSRMLS_DC #define SHUTDOWN_FUNC_ARGS_PASSTHRU type, module_number TSRMLS_CC #define ZEND_MODULE_INFO_FUNC_ARGS zend_module_entry *zend_module TSRMLS_DC #define ZEND_MODULE_INFO_FUNC_ARGS_PASSTHRU zend_module TSRMLS_CC ....
上面 zif_abc 函数参数中还有一个看起来奇怪的东西 TSRMLS_DC,线程安全资源管理的宏;
./TSRM/TSRM.h:166
#ifdef ZTS .......... #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 #else /* non ZTS */ #define TSRMLS_FETCH() #define TSRMLS_FETCH_FROM_CTX(ctx) #define TSRMLS_SET_CTX(ctx) #define TSRMLS_D void #define TSRMLS_DC #define TSRMLS_C #define TSRMLS_CC #endif /* ZTS */
TSRMLS_* 系列总共有四个,其展开意思是 thread safe resource manager local stroage;
在不启用zend线程安全的时候,它们并没有内容可以替换,ZTS 就是 zend thread safe;
TSRMLS_DC 和 TSRMLS_CC 对应比 TSRMLS_D 和 TSRMLS_C 多加了一个逗号,
TSRMLS_DC 就是 , TSRMLS_D,也就是 , void ***tsrm_ls ;它在函数定义中使用,D有define的意思.
TSRMLS_CC就是 , TSRMLS_C,也就是 , tsrm_ls ;它在函数调用中使用,C有call的意思.
这里有篇关于TSRM的靠谱文章:揭秘TSRM(Introspecting TSRM)
二. 参数
int ht, 用户实际传入的参数数目,ZEND_NUM_ARGS() 获取.
zval *return_value, 指向由return_value填充的PHP变量的指针,(所指向的变量值)返回给函数调用者,默认类型是 IS_NULL.
zval **return_value_ptr, 当返回PHP的引用,设置为指向变量的指针.(需要引用形式的返回值时使用)
zval *this_ptr, 如果此函数是类的方法调用,相当于 $this 对象.
int return_value_used, 用户调用此函数有没有用到函数返回值,用到就是1.
在 [php-src]窥探Php内核中的变量 中讲到了设置变量的值,ZVAL_*() 系列函数,第一个参数就是 return_value,如果我们不用 RETURN_*() 系列函数主动返回某个值的话,同样可以用 ZVAL_*() ,效果是一样的。
long t = 10;
RETURN_LONG(t); // 同 RETVAL_LONG(t);return; 也相当于 ZVAL_LONG(return_value, t);return;
内核中修改传递的参数:
{ zval *arg; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &arg) == FAILURE) { RETURN_NULL(); } // PHP5.4 参数不再支持引用方式传递,所以不需要判断 arg->is_ref__gc convert_to_string(arg); ZVAL_STRING(arg, "abc", 1); return; }
<?php $a = "AAA"; test($a); // allow_call_time_pass_reference 已从5.4中移除,所以下面的用法是非法的,只能在函数定义时使用,如:function test(&$var) // test(&$a); // PHP Fatal error: Call-time pass-by-reference has been removed // 参考:http://php.net/manual/zh/ini.core.php echo $a; // abc
知道了上面的原理,我们直接对参数值进行修改,别忘了函数原型中提醒用户会更改原值。
三. zend_API.h 中的宏函数
array_init() 在 ./Zend/zend_API.h:363
#define array_init(arg) _array_init((arg), 0 ZEND_FILE_LINE_CC) #define array_init_size(arg, size) _array_init((arg), (size) ZEND_FILE_LINE_CC) #define object_init(arg) _object_init((arg) ZEND_FILE_LINE_CC TSRMLS_CC) #define object_init_ex(arg, ce) _object_init_ex((arg), (ce) ZEND_FILE_LINE_CC TSRMLS_CC) #define object_and_properties_init(arg, ce, properties) _object_and_properties_init((arg), (ce), (properties) ZEND_FILE_LINE_CC TSRMLS_CC) ZEND_API int _array_init(zval *arg, uint size ZEND_FILE_LINE_DC);
_array_init() 的实现在 ./Zend/zend_API.c:1009
/* Argument parsing API -- andrei */ ZEND_API int _array_init(zval *arg, uint size ZEND_FILE_LINE_DC) /* {{{ */ { ALLOC_HASHTABLE_REL(Z_ARRVAL_P(arg)); _zend_hash_init(Z_ARRVAL_P(arg), size, ZVAL_PTR_DTOR, 0 ZEND_FILE_LINE_RELAY_CC); Z_TYPE_P(arg) = IS_ARRAY; return SUCCESS; } /* }}} */
ZEND_FILE_LINE_DC 在 zend.h 第217行:
#if ZEND_DEBUG #define ZEND_FILE_LINE_D const char *__zend_filename, const uint __zend_lineno #define ZEND_FILE_LINE_DC , ZEND_FILE_LINE_D #define ZEND_FILE_LINE_ORIG_D const char *__zend_orig_filename, const uint __zend_orig_lineno #define ZEND_FILE_LINE_ORIG_DC , ZEND_FILE_LINE_ORIG_D #define ZEND_FILE_LINE_RELAY_C __zend_filename, __zend_lineno #define ZEND_FILE_LINE_RELAY_CC , ZEND_FILE_LINE_RELAY_C #define ZEND_FILE_LINE_C __FILE__, __LINE__ #define ZEND_FILE_LINE_CC , ZEND_FILE_LINE_C #define ZEND_FILE_LINE_EMPTY_C NULL, 0 #define ZEND_FILE_LINE_EMPTY_CC , ZEND_FILE_LINE_EMPTY_C #define ZEND_FILE_LINE_ORIG_RELAY_C __zend_orig_filename, __zend_orig_lineno #define ZEND_FILE_LINE_ORIG_RELAY_CC , ZEND_FILE_LINE_ORIG_RELAY_C #define ZEND_ASSERT(c) assert(c) #else #define ZEND_FILE_LINE_D #define ZEND_FILE_LINE_DC #define ZEND_FILE_LINE_ORIG_D #define ZEND_FILE_LINE_ORIG_DC #define ZEND_FILE_LINE_RELAY_C #define ZEND_FILE_LINE_RELAY_CC #define ZEND_FILE_LINE_C #define ZEND_FILE_LINE_CC #define ZEND_FILE_LINE_EMPTY_C #define ZEND_FILE_LINE_EMPTY_CC #define ZEND_FILE_LINE_ORIG_RELAY_C #define ZEND_FILE_LINE_ORIG_RELAY_CC #define ZEND_ASSERT(c) #endif /* ZEND_DEBUG */
上面 __file_name 就是函数一个形参名,没特殊含义,用两个下划线有非常局部(私有)的感觉。
API函数的存在,能让我们更方便在内核中完成某项功能。
看到这里,你或许对函数原型和函数前面的 ZEND_API 感到困惑,这是什么,定义它们的文件有:
./configure.in:22
./Zend/configure.in:16
./main/php_config.h.in:8
./main/php_config.h:6
./Zend/zend_config.win32.h:78
configure.in 和 ./Zend/configure.in 由 autoconf 处理,并生成配置文件,它们都含下面这段:
AH_TOP([ #if defined(__GNUC__) && __GNUC__ >= 4 # define ZEND_API __attribute__ ((visibility("default"))) # define ZEND_DLEXPORT __attribute__ ((visibility("default"))) #else # define ZEND_API # define ZEND_DLEXPORT #endif #define ZEND_DLIMPORT #undef uint #undef ulong /* Define if you want to enable memory limit support */ #define MEMORY_LIMIT 0 ])
__attribute__ ((visibility("default"))) 用于设置可见性,default使符号在所有情况下都被输出. 参考:控制符的可见性
ps:
__GNUC__ 是gcc编译器编译代码时预定义的一个宏。需要针对gcc编写代码时, 可以使用该宏进行条件编译。
__GNUC__ 的值表示gcc的版本。需要针对gcc特定版本编写代码时,也可以使用该宏进行条件编译。
__GNUC__ 的类型是“int”,该宏被扩展后, 得到的是整数字面值。可以通过仅预处理,查看宏扩展后的文本。
./Zend/zend_config.win32.h
#ifdef LIBZEND_EXPORTS # define ZEND_API __declspec(dllexport) #else # define ZEND_API __declspec(dllimport) #endif
./configure.in ->生成 ./main/php_config.h.in ->生成 ./main/php_config.h
和 ZEND_API 效果一样的有 PHPAPI,
./main/php.h:60:# define PHPAPI __attribute__ ((visibility("default")))
四. 错误输出及运行时信息
1). 错误输出函数
#ifndef ZEND_ERRORS_H #define ZEND_ERRORS_H #define E_ERROR (1<<0L) #define E_WARNING (1<<1L) #define E_PARSE (1<<2L) #define E_NOTICE (1<<3L) #define E_CORE_ERROR (1<<4L) #define E_CORE_WARNING (1<<5L) #define E_COMPILE_ERROR (1<<6L) #define E_COMPILE_WARNING (1<<7L) #define E_USER_ERROR (1<<8L) #define E_USER_WARNING (1<<9L) #define E_USER_NOTICE (1<<10L) #define E_STRICT (1<<11L) #define E_RECOVERABLE_ERROR (1<<12L) #define E_DEPRECATED (1<<13L) #define E_USER_DEPRECATED (1<<14L) #define E_ALL (E_ERROR | E_WARNING | E_PARSE | E_NOTICE | E_CORE_ERROR | E_CORE_WARNING |
E_COMPILE_ERROR | E_COMPILE_WARNING | E_USER_ERROR | E_USER_WARNING | E_USER_NOTICE |
E_RECOVERABLE_ERROR | E_DEPRECATED | E_USER_DEPRECATED | E_STRICT) #define E_CORE (E_CORE_ERROR | E_CORE_WARNING) #endif /* ZEND_ERRORS_H */
定义错误输出函数的宏 ./main/php.h:292
#define php_error zend_error #define error_handling_t zend_error_handling_t BEGIN_EXTERN_C() static inline ZEND_ATTRIBUTE_DEPRECATED void php_set_error_handling(error_handling_t error_handling, zend_class_entry *exception_class TSRMLS_DC) { zend_replace_error_handling(error_handling, exception_class, NULL TSRMLS_CC); } static inline ZEND_ATTRIBUTE_DEPRECATED void php_std_error_handling() {} PHPAPI void php_verror(const char *docref, const char *params, int type, const char *format, va_list args TSRMLS_DC) PHP_ATTRIBUTE_FORMAT(printf, 4, 0); #ifdef ZTS #define PHP_ATTR_FMT_OFFSET 1 #else #define PHP_ATTR_FMT_OFFSET 0 #endif /* PHPAPI void php_error(int type, const char *format, ...); */ PHPAPI void php_error_docref0(const char *docref TSRMLS_DC, int type, const char *format, ...) PHP_ATTRIBUTE_FORMAT(printf, PHP_ATTR_FMT_OFFSET + 3, PHP_ATTR_FMT_OFFSET + 4); PHPAPI void php_error_docref1(const char *docref TSRMLS_DC, const char *param1, int type, const char *format, ...) PHP_ATTRIBUTE_FORMAT(printf, PHP_ATTR_FMT_OFFSET + 4, PHP_ATTR_FMT_OFFSET + 5); PHPAPI void php_error_docref2(const char *docref TSRMLS_DC, const char *param1, const char *param2, int type, const char *format, ...) PHP_ATTRIBUTE_FORMAT(printf, PHP_ATTR_FMT_OFFSET + 5, PHP_ATTR_FMT_OFFSET + 6); #ifdef PHP_WIN32 PHPAPI void php_win32_docref2_from_error(DWORD error, const char *param1, const char *param2 TSRMLS_DC); #endif END_EXTERN_C() #define php_error_docref php_error_docref0
内核中很多地方出现 BEGIN_EXTERN_C() 和 END_EXTERN_C() 将程序包起来的情况,它们实际上是下面这种东西,你一看便知:
#ifdef __cplusplus #define BEGIN_EXTERN_C() extern "C" { #define END_EXTERN_C() } #else #define BEGIN_EXTERN_C() #define END_EXTERN_C() #endif
如果定义了__cplusplus,就把C程序用 extern "C" { } 包起来当C++处理,用于指定编译和链接规则,实现C和C++的混合编程. 参考:C++项目中的extern "C" {}
zend_error 原型在 ./Zend/zend.h:711,实现在 ./Zend/zend.c:1031
ZEND_API void zend_error(int type, const char *format, ...) ZEND_ATTRIBUTE_FORMAT(printf, 2, 3);
php_error_docref0 实现在 ./main.c:947
/* {{{ php_error_docref0 */ /* See: CODING_STANDARDS for details. */ PHPAPI void php_error_docref0(const char *docref TSRMLS_DC, int type, const char *format, ...) { va_list args; va_start(args, format); php_verror(docref, "", type, format, args TSRMLS_CC); va_end(args); } /* }}} */
php_error 即 zend_error,php_error_docref 即 php_error_docref0
php_error 与 php_error_docref 的使用基本相同,参数位置不一样、效果略有差异;
由于 php_error 只是 zend_error 的别名,我们应该尽量使用 zend_error.
如:php_error_docref(NULL TSRMLS_CC, E_WARNING, "has been disabled for security reasons");
将输出:Warning: testing(): has been disabled for security reasons in /home/www/1.php on line 3
如:zend_error(E_WARNING, "has been disabled for security reasons");
将输出:Warning: has been disabled for security reasons in /home/www/1.php on line 3
2). 获得运行时函数信息的函数
get_active_function_name(TSRMLS_C) 查看当前执行的函数名.
zend_get_executed_filename(TSRMLS_C) 查看当前执行的文件名.
zend_get_executed_lineno(TSRMLS_C) 查看当前执行到哪一行代码.
./Zend/zend_execute.h:353
ZEND_API const char *get_active_function_name(TSRMLS_D);
./Zend/zend_execute_API.c:366
ZEND_API const char *get_active_function_name(TSRMLS_D) /* {{{ */ { if (!zend_is_executing(TSRMLS_C)) { return NULL; } switch (EG(current_execute_data)->function_state.function->type) { case ZEND_USER_FUNCTION: { const char *function_name = ((zend_op_array *) EG(current_execute_data)- >function_state.function)->function_name; if (function_name) { return function_name; } else { return "main"; } } break; case ZEND_INTERNAL_FUNCTION: return ((zend_internal_function *) EG(current_execute_data)->function_state.function)- >function_name; break; default: return NULL; } } /* }}} */
3). 示例
zend_error( E_WARNING, "%s() has been disabled for security reasons; current file is %s; current line is %i", get_active_function_name(TSRMLS_C), zend_get_executed_filename(TSRMLS_C), zend_get_executed_lineno(TSRMLS_C) ); zend_error(E_WARNING, "this is a warning message\n");
给内核传递函数参数信息的宏:(用 zend_parse_parameters 解析参数时,可以不使用 arginfo 指定参数类型 )
./Zend/zend_API.h:108
#define ZEND_ARG_INFO(pass_by_ref, name) { #name, sizeof(#name)-1, NULL, 0, 0, pass_by_ref, 0, 0 }, .... #define ZEND_BEGIN_ARG_INFO_EX(name, _unused, return_reference, required_num_args) \ static const zend_arg_info name[] = { \ { NULL, 0, NULL, required_num_args, 0, return_reference, 0, 0 }, #define ZEND_BEGIN_ARG_INFO(name, _unused) \ ZEND_BEGIN_ARG_INFO_EX(name, 0, ZEND_RETURN_VALUE, -1) #define ZEND_END_ARG_INFO() };
【写一个color扩展的welcome函数】
定义函数并添加为可用:
/* {{{ arginfo */ ZEND_BEGIN_ARG_INFO_EX(arginfo_welcome, 0, 0, 0) ZEND_ARG_INFO(0, arg) ZEND_END_ARG_INFO() /* }}} */ /* {{{ 自定义welcome */ PHP_FUNCTION(welcome) { php_printf("It works, Welcome!\n"); } /* }}} */ /* {{{ color_functions[] * * Every user visible function must have an entry in color_functions[]. 可用的函数都加到里面 */ const zend_function_entry color_functions[] = { PHP_FE(confirm_color_compiled, NULL) /* For testing, remove later. */ PHP_FE(welcome, arginfo_welcome) PHP_FALIAS(wel, welcome, arginfo_welcome) /* alias */ PHP_FE_END /* Must be the last line in color_functions[] */ }; /* }}} */
make && sudo make install
运行 php5.6.14 -r 'welcome();' 就会输出:It works, Welcome!
注意:如果你本机装了多个php,一不小心打成了 php -r 'welcome();',那么你得到的永远都是undefined function
为扩展增加phpinfo信息:
/* {{{ PHP_MINFO_FUNCTION */ PHP_MINFO_FUNCTION(color) { php_info_print_table_start(); // 开始表格, 无参数 // php_info_print_table_header(2, "Color Support", "enabled"); // 表格头,第一个参数是列数,后面是可变参数,第一个参数是几后面就是几个
php_info_print_table_row(2, "Color Support", "enabled");
php_info_print_table_row(2, "author", "weiChen"); // 表格行,与header头仅颜色不一样,同样第一个参数是列数,后面是列的内容
php_info_print_table_row(2, "dependencies", "No"); php_info_print_table_end(); // 结束表格, 无参数 DISPLAY_INI_ENTRIES(); // 开启此行向 phpinfo 中显示INI配置 } /* }}} */
注意模块信息的修改除了重新编译安装,还需要重启Php使之生效.
增加可供查看的 INI 配置项:
1). 模块初始阶段引入 结束阶段去除
/* {{{ PHP_MINIT_FUNCTION */ PHP_MINIT_FUNCTION(color) { REGISTER_INI_ENTRIES(); return SUCCESS; } /* }}} */ /* {{{ PHP_MSHUTDOWN_FUNCTION */ PHP_MSHUTDOWN_FUNCTION(color) { UNREGISTER_INI_ENTRIES(); return SUCCESS; } /* }}} */
2). 具体配置项
static PHP_INI_MH(onChangeTest) // 定义回调函数onChangeTest { php_printf("ini entry has changed to %s\n", new_value); return SUCCESS; } /* {{{ PHP_INI */ PHP_INI_BEGIN() // 开始
// 1.配置项名称, 2.初始值, 3.权限(PHP_INI_PERDIR、PHP_INI_SYSTEM、PHP_INI_USER、PHP_INI_ALL), 4.改变这个值时的回调函数
// PHP_INI_PERDIR | PHP_INI_SYSTEM 指令只允许在php.ini或httpd.conf等配置中修改,PHP_INI_USER 只能在用户脚本中修改 如ini_set().
// 具体配置项 PHP_INI_ENTRY("color.test", "very good", PHP_INI_ALL, onChangeTest)
//STD_PHP_INI_ENTRY("color.global_value", "42", PHP_INI_ALL, OnUpdateLong, global_value, zend_color_globals, color_globals) //STD_PHP_INI_ENTRY("color.global_string", "foobar", PHP_INI_ALL, OnUpdateString, global_string, zend_color_globals, color_globals)
PHP_INI_END() // 结束 /* }}} */
宏的转换关系都在 ./Zend/zend_ini.h 中:
PHP_INI_ENTRY -> ZEND_INI_ENTRY -> ZEND_INI_ENTRY_EX -> ZEND_INI_ENTRY3_EX
STD_PHP_INI_ENTRY -> STD_ZEND_INI_ENTRY -> ZEND_INI_ENTRY2 -> ZEND_INI_ENTRY2_EX -> ZEND_INI_ENTRY3_EX
由于 ZEND_INI_ENTRY 只需要传4个参数,到最后变成 ZEND_INI_ENTRY3_EX 时后面3个参数已经默认为 NULL .
当不需要后面的参数时用 PHP_INI_ENTRY,想传参的时候用 STD_PHP_INI_ENTRY,最后两个参数是由下面这个扩展内的函数初始化的.
/* {{{ php_color_init_globals */ /* Uncomment this function if you have INI entries static void php_color_init_globals(zend_color_globals *color_globals) { color_globals->global_value = 0; color_globals->global_string = NULL; } */ /* }}} */
./Zend/zend_ini.h:114
#define ZEND_INI_BEGIN() static const zend_ini_entry ini_entries[] = { #define ZEND_INI_END() { 0, 0, NULL, 0, NULL, NULL, NULL, NULL, NULL, 0, NULL, 0, 0, 0, NULL } };
#define ZEND_INI_ENTRY3_EX(name, default_value, modifiable, on_modify, arg1, arg2, arg3, displayer) \ { 0, modifiable, name, sizeof(name), on_modify, arg1, arg2, arg3, default_value, sizeof(default_value)-1, NULL, 0, 0, 0, displayer },
最终的 PHP_INI 区块实际编译成一个如下结构的数组:
static const zend_ini_entry ini_entries[] = { { 0, modifiable, name, sizeof(name), on_modify, arg1, arg2, arg3, default_value, sizeof(default_value)-1, NULL, 0, 0, 0, displayer }, { 0, 0, NULL, 0, NULL, NULL, NULL, NULL, NULL, 0, NULL, 0, 0, 0, NULL } }
3). 访问配置项的宏 ( Zend/zend_ini.h:157 )
#define INI_INT(name) zend_ini_long((name), sizeof(name), 0) #define INI_FLT(name) zend_ini_double((name), sizeof(name), 0) #define INI_STR(name) zend_ini_string_ex((name), sizeof(name), 0, NULL) #define INI_BOOL(name) ((zend_bool) INI_INT(name)) #define INI_ORIG_INT(name) zend_ini_long((name), sizeof(name), 1) #define INI_ORIG_FLT(name) zend_ini_double((name), sizeof(name), 1) #define INI_ORIG_STR(name) zend_ini_string((name), sizeof(name), 1) #define INI_ORIG_BOOL(name) ((zend_bool) INI_ORIG_INT(name))
头四个用来获取INI设置的当前值,后四个用来读取未经修改的INI值 ( Zend/zend_ini.h:102 ).
ZEND_API long zend_ini_long(char *name, uint name_length, int orig); ZEND_API double zend_ini_double(char *name, uint name_length, int orig); ZEND_API char *zend_ini_string(char *name, uint name_length, int orig); ZEND_API char *zend_ini_string_ex(char *name, uint name_length, int orig, zend_bool *exists);
实现在 Zend/zend_ini.c:348
/* * Data retrieval */ ZEND_API long zend_ini_long(char *name, uint name_length, int orig) /* {{{ */ { zend_ini_entry *ini_entry; TSRMLS_FETCH(); // 在需要访问全局变量的代码块开头使用TSRMLS_FETCH()来提取上下文 if (zend_hash_find(EG(ini_directives), name, name_length, (void **) &ini_entry) == SUCCESS) {
if (orig && ini_entry->modified) { return (ini_entry->orig_value ? strtol(ini_entry->orig_value, NULL, 0) : 0); } else { return (ini_entry->value ? strtol(ini_entry->value, NULL, 0) : 0); } } return 0; } /* }}} */
编译扩展、重启php生效:make && sudo make install && sudo sh /home/weiChen/reloadPhp.sh
Refer:什么是PHP内核函数
开发文档:https://github.com/farwish/php-core-hack
推荐文章:用C/C++扩展你的PHP