/images/avatar.png

Schaepher's Blog

【PHP 源码】PHP 函数调用

想法

我以前对于 C 语言的印象是有很强的确定性,而 PHP 在执行的时候会被翻译为 C 语言执行,所以一直很好奇 PHP 怎么调用底层函数。

换句话说就是已知函数名字的情况下如何调用 C 语言中对应名字的函数?

【PHP 源码】PHP 函数注册

函数的注册

调用顺序为:

- 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

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

PHP sort 源码

ext/standard/array.c

/* {{{ proto bool sort(array &array_arg [, int sort_flags])
   Sort an array */
PHP_FUNCTION(sort)
{
	zval *array;
	zend_long sort_type = PHP_SORT_REGULAR;
	compare_func_t cmp;

	ZEND_PARSE_PARAMETERS_START(1, 2)
		Z_PARAM_ARRAY_EX(array, 0, 1)
		Z_PARAM_OPTIONAL
		Z_PARAM_LONG(sort_type)
	ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);

	cmp = php_get_data_compare_func(sort_type, 0);  // 根据 sort_flags 选择比较函数

	if (zend_hash_sort(Z_ARRVAL_P(array), cmp, 1) == FAILURE) {
		RETURN_FALSE;
	}
	RETURN_TRUE;
}

接下来找 zend_hash_sort 。

Zend/zend_hash.h

#define zend_hash_sort(ht, compare_func, renumber) \
	zend_hash_sort_ex(ht, zend_sort, compare_func, renumber)

用于排序的算法是 zend_sort 。

阅读 PHP 源码

从数组入手。

数组相关函数是 PHP 扩展的一部分,扩展名为 standard,执行 php -m 可以看到该扩展。

该扩展的源码在 PHP 源码包的 ext/standard ,其中数组相关函数的定义在 ext/standard/array.c

PHP 对象

创建对象:

zend_objects.c

ZEND_API zend_object* ZEND_FASTCALL zend_objects_new(zend_class_entry *ce)
{
	zend_object *object = emalloc(sizeof(zend_object) + zend_object_properties_size(ce));

	_zend_object_std_init(object, ce);
	object->handlers = &std_object_handlers;
	return object;
}

zend_object_handlers.c

ZEND_API const zend_object_handlers std_object_handlers = {
	0,										/* offset */

	zend_object_std_dtor,					/* free_obj */
	zend_objects_destroy_object,			/* dtor_obj */
	zend_objects_clone_obj,					/* clone_obj */

	zend_std_read_property,					/* read_property */
	zend_std_write_property,				/* write_property */
	zend_std_read_dimension,				/* read_dimension */
	zend_std_write_dimension,				/* write_dimension */
	zend_std_get_property_ptr_ptr,			/* get_property_ptr_ptr */
	NULL,									/* get */
	NULL,									/* set */
	zend_std_has_property,					/* has_property */
	zend_std_unset_property,				/* unset_property */
	zend_std_has_dimension,					/* has_dimension */
	zend_std_unset_dimension,				/* unset_dimension */
	zend_std_get_properties,				/* get_properties */
	zend_std_get_method,					/* get_method */
	NULL,									/* call_method */
	zend_std_get_constructor,				/* get_constructor */
	zend_std_get_class_name,				/* get_class_name */
	zend_std_compare_objects,				/* compare_objects */
	zend_std_cast_object_tostring,			/* cast_object */
	NULL,									/* count_elements */
	zend_std_get_debug_info,				/* get_debug_info */
	zend_std_get_closure,					/* get_closure */
	zend_std_get_gc,						/* get_gc */
	NULL,									/* do_operation */
	NULL,									/* compare */
	NULL,									/* get_properties_for */
};

PHP 垃圾回收

  1. 每个值容器(zend_value)里面都会有引用计数结构体 zend_rrefcounted
  2. 引用计数减少到 0 时进行清除
  3. unset() 将值的引用减少 1
  4. 值容器引用在减少 1 后如果不为 0 ,则加入回收池
  5. 回收池满后,开始执行回收
    1. 第一次遍历回收池所有值容器。对所有值容器的引用减 1 。如果是嵌套对象(如数组和对象),则遍历其元素,元素值容器的引用减 1 。
      • 这是一个深度遍历的过程。
      • 这里是模拟减 1,减到 0 时不触发回收
    2. 第二次遍历回收池所有值容器。对引用计数不为 0 的值容器引用加 1 (恢复到之前的样子),并将其从回收池中移出。
    3. 回收池中引用计数为 0 的值容器被作为垃圾清除。

如: