内置函数
前面主要从用户定义函数的角度,介绍了 PHP 7 中函数机制的实现。本节将焦点转移到内置函数,并说明内置函数与用户定义函数的不同之处。
内置函数的注册
内置函数定义、注册后,CG(function_table)
会将之载入,而无须经过用户定义函数必需的词法、语法解析等编译过程,效率自然更高。PHP 7 内核开发者提供了很多核心内置函数。同时,数量庞大的社区开发者也在 PHP 核心能力之上,开发了众多功能丰富的扩展,供开发者选择。
通过方法 get_extension_funcs
可以获得安装成功的扩展模块相关的内置函数列表。例如,获取 xml 模块相关函数:
<?php
print_r(get_extension_funcs("xml"));
那么内置函数如何被开发者直接调用呢?
在第7章中,我们介绍过 php_module_startup
的过程。PHP 内置函数的注册在 php_register_internal_extensions
中完成:
#define EXTCOUNT (sizeof(php_builtin_extensions)/sizeof(zend_module_entry *))
PHPAPI int php_register_internal_extensions(void) {
return php_register_extensions(php_builtin_extensions, EXTCOUNT);
}
php_builtin_extensions 包含了需要初始化的所有内部扩展。内部函数的注册最终在 zend_API.c 文件的 zend_register_functions 方法中完成。整体注册流程如图13-4所示。

注册过程会处理函数类型、作用域等诸多细节,如果忽略细节,函数注册的精简过程是将每一个内置函数存储进 CG(function_table)。保存内部函数信息的结构体是 zend_internal_function,想必读者对这个名字不会陌生(在13.2节讲解用户定义函数的实现机制时已经介绍过,它与结构体 op_array 和 zend_function 拥有共同的成员 common,这样便可以通过通用接口快速访问成员。
内置函数的执行
在用户定义函数的执行部分,我们介绍过用户定义函数的调用会编译成 ZEND_DO_UCALL 类型 opcode;而内置函数对应的 opcode 为 ZEND_DO_ICALL。在 ZEND_DO_UCALL 和 ZEND_DO_ICALL 对应的 handler 中,再完成具体操作。
内置函数还有一种情况,即不生成 ZEND_DO_ICALL,直接执行函数对应的 opcode。如 PHP 的内置函数 strlen:
<?php
$a = 'hi php';
strlen($a);
生成的 opcodes 如下:
ZEND_ASSIGN
ZEND_STRLEN
ZEND_FREE
ZEND_RETURN
如果代码为
strlen('123456');
则生成的 opcodes 为
ZEND_RETURN
可以看到,在第一种情况中,参数为变量,对应的 opcode 为 ZEND_STRLEN;而在第二种情况中,同样为 strlen 函数,不过参数变成了常量,对应的 opcode 只有 ZEND_RETURN。
用户自定义函数的执行通过引擎执行函数的 opcode 集合来获得结果;而内置函数的执行,通过 internal_function.handler 的调用完成,并将返回结果赋值给 opline 的 result.var。其他地方与用户定义函数类似,这里不再赘述。