函数的注册
调用顺序为:
1
2
3
4
5
6
7
| - main
-- php_cli_startup
--- php_module_startup
---- zend_startup
----- zend_startup_builtin_function
------ zend_register_module_ex(*module)
------- zend_register_functions(module->functions)
|
sapi/cli/php_cli.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| int main(int argc, char *argv[])
{
// ...
sapi_module_struct *sapi_module = &cli_sapi_module;
// ...
/* startup after we get the above ini override se we get things right */
if (sapi_module->startup(sapi_module) == FAILURE) {
// ...
}
// ...
exit_status = do_cli(argc, argv);
// ...
}
|
main/SAPI.h
1
2
3
4
5
6
7
8
9
| typedef struct _sapi_module_struct sapi_module_struct;
struct _sapi_module_struct {
char *name;
char *pretty_name;
int (*startup)(struct _sapi_module_struct *sapi_module);
// ...
}
|
sapi/cli/php_cli.c
1
2
3
4
5
6
7
8
9
10
11
12
| static sapi_module_struct cli_sapi_module = {
"cli", /* name */
"Command Line Interface", /* pretty name */
php_cli_startup, /* startup */
// ...
const zend_function_entry *additional_functions;
// ...
}
|
sapi/cli/php_cli.c
1
2
3
4
5
6
7
| static int php_cli_startup(sapi_module_struct *sapi_module)
{
if (php_module_startup(sapi_module, NULL, 0)==FAILURE) {
return FAILURE;
}
return SUCCESS;
}
|
main/main.c
1
2
3
4
5
6
7
8
| int php_module_startup(sapi_module_struct *sf, zend_module_entry *additional_modules, uint32_t num_additional_modules)
{
// ...
zend_startup(&zuf);
// ...
}
|
Zend/zend.c
1
2
3
4
5
6
7
8
| int zend_startup(zend_utility_functions *utility_functions) /* {{{ */
{
// ...
zend_startup_builtin_functions();
// ...
}
|
Zend/zend_builtin_functions.c
1
2
3
4
5
6
| int zend_startup_builtin_functions(void) /* {{{ */
{
zend_builtin_module.module_number = 0;
zend_builtin_module.type = MODULE_PERSISTENT;
return (EG(current_module) = zend_register_module_ex(&zend_builtin_module)) == NULL ? FAILURE : SUCCESS;
}
|
Zend/zend_builtin_functions.c
1
2
3
4
5
6
7
8
9
10
11
12
| zend_module_entry zend_builtin_module = { /* {{{ */
STANDARD_MODULE_HEADER,
"Core",
builtin_functions,
ZEND_MINIT(core),
NULL,
NULL,
NULL,
NULL,
ZEND_VERSION,
STANDARD_MODULE_PROPERTIES
};
|
Zend/zend_builtin_functions.c
1
2
3
4
5
6
7
8
9
10
11
| static const zend_function_entry builtin_functions[] = { /* {{{ */
ZEND_FE(zend_version, arginfo_zend__void)
ZEND_FE(func_num_args, arginfo_zend__void)
ZEND_FE(func_get_arg, arginfo_func_get_arg)
ZEND_FE(func_get_args, arginfo_zend__void)
ZEND_FE(strlen, arginfo_strlen)
// ...
ZEND_FE(function_exists, arginfo_function_exists)
// ...
}
|
Zend/zend_API.c
1
2
3
4
5
6
7
8
| ZEND_API zend_module_entry* zend_register_module_ex(zend_module_entry *module)
{
// ...
if (module->functions && zend_register_functions(NULL, module->functions, NULL, module->type)==FAILURE) {
// ...
}
// ...
}
|
Zend/zend_API.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| /* registers all functions in *library_functions in the function hash */
ZEND_API int zend_register_functions(zend_class_entry *scope, const zend_function_entry *functions, HashTable *function_table, int type)
{
// ...
if (!target_function_table) {
target_function_table = CG(function_table);
}
// ...
while (ptr->fname) {
// ...
memcpy(reg_function, &function, sizeof(zend_internal_function));
if (zend_hash_add_ptr(target_function_table, lowercase_name, reg_function) == NULL) {
// ...
}
// ...
}
}
|
基本函数的注册到这里就基本结束了。
但是像 array_column()
这种函数不属于内置函数,它属于 standard 扩展的函数。那么它在哪里被注册呢?
main/main.c
1
2
3
4
5
6
7
8
9
10
11
12
| int php_module_startup(sapi_module_struct *sf, zend_module_entry *additional_modules, uint32_t num_additional_modules)
{
// ...
/* startup extensions statically compiled in */
if (php_register_internal_extensions_func() == FAILURE) {
php_printf("Unable to start builtin modules\n");
return FAILURE;
}
// ...
}
|
看到这里就基本结束了。其他细节有兴趣可以继续研究。
结构体:
Zend/zend_modules.h
1
2
3
4
5
6
| typedef struct _zend_module_entry zend_module_entry;
struct _zend_module_entry {
// ...
const struct _zend_function_entry *functions;
// ...
}
|
ext/standard/basic_functions.c
1
2
3
4
5
6
| zend_module_entry basic_functions_module = {
// ...
"standard", /* extension name */
basic_functions, /* function list */
// ...
};
|
ext/standard/basic_functions.h
1
| extern zend_module_entry basic_functions_module;
|
后记
最开始写这篇文章的时候,是靠分析和 VS Code 的查找功能来理清楚调用顺序的。结果在 php_module_startup
这一层里面找的时候,没注意 zend_startup
就跳过这一行找下面了。导致绕了个弯。最后决定编译 PHP 源码,用调试的方式看源码,就像当初学习 Laravel 那样。这样看起源码来容易多了,而且以后看源码也能减少很多负担。
在开启调试后,回头又补充了前面关于函数调用的解释。最开始使用 array_key_exist()
来查看调用栈,没想到 PHP 并没有调用 ext/standard/array.c
里面 array_key_exist
的代码,而是直接使用 Zend/zend_execute.c
里的 zend_array_key_exists_fast
,让我多花了一些时间。