标签归档:PHP源码

PHP源码阅读笔记二十七:PHP对构造方法的识别

PHP源码阅读笔记二十七:PHP对构造方法的识别
众所周知,由于历史原因,PHP之前是使用类名作为构造函数,在PHP5中引入的新的构造函数__construct。为了实现向后兼容性,如果 PHP 5 在类中找不到 __construct() 函数,它就会尝试寻找旧式的构造函数,也就是和类同名的函数。因此唯一会产生兼容性问题的情况是:类中已有一个名为 __construct() 的方法,但它却又不是构造函数。
有如下一段代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
class Foo {
 
    public function Foo() {
 
    }
 
    private function __construct() {
 
    }
}
 
new Foo();
die();

此时,输出为:
Fatal error: Call to private Foo::__construct() from invalid context
此时,PHP识别出来的构造函数是__construct,因为是private,于是在外部调用出错。

好吧,我们从PHP的C源码中查找一下原因吧。
从spl的扩展类中直接查找类的定义开始:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
spl_iterators.c 3228行 REGISTER_SPL_STD_CLASS_EX(IteratorIterator, spl_dual_it_new, spl_funcs_IteratorIterator);
///spl_functions.h 31行
#define REGISTER_SPL_STD_CLASS_EX(class_name, obj_ctor, funcs) \
	spl_register_std_class(&spl_ce_ ## class_name, # class_name, obj_ctor, funcs TSRMLS_CC);
//spl_functions.c 41行
PHPAPI void spl_register_std_class(zend_class_entry ** ppce, char * class_name, void * obj_ctor, const zend_function_entry * function_list TSRMLS_DC)
 
//spl_functions.c 2235行
ZEND_API zend_class_entry *zend_register_internal_class(zend_class_entry *orig_class_entry TSRMLS_DC) /* {{{ */
//调用do_register_internal_class函数
 
//zend_API.c 2169行
static zend_class_entry *do_register_internal_class(zend_class_entry *orig_class_entry, zend_uint ce_flags TSRMLS_DC) /* {{{ */
//调用
zend_register_functions(class_entry, class_entry->builtin_functions, &class_entry->function_table, MODULE_PERSISTENT TSRMLS_CC);
 
//zend_API.c 1795行
/* Look for ctor, dtor, clone
* If it's an old-style constructor, store it only if we don't have
* a constructor already.
*/
if ((fname_len == class_name_len) && !memcmp(lowercase_name, lc_class_name, class_name_len+1) && !ctor) {
	ctor = reg_function;
} else if ((fname_len == sizeof(ZEND_CONSTRUCTOR_FUNC_NAME)-1) && !memcmp(lowercase_name, ZEND_CONSTRUCTOR_FUNC_NAME, sizeof(ZEND_CONSTRUCTOR_FUNC_NAME))) {
	ctor = reg_function;
} 
 
scope->constructor = ctor;	//	在1961行 确认构造函数

以上代码为php5.3.0版本
从以上跟踪流程来看,程序在注册所有函数时,如果存在__construct(即ZEND_CONSTRUCTOR_FUNC_NAME)时,会覆盖class_name(类名)的构造函数,使其作为常规的成员函数存在。如下所示代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
class Foo {
 
    public function Foo() {
        echo 'Foo';
    }
 
    public function __construct() {
        echo '__construct';
    }
}
 
$foo = new Foo();
$foo->Foo();

对于在前面的示例中的报错,我们可以在
zend/zend_object_handlers.c 1057行
ZEND_API union _zend_function *zend_std_get_constructor(zval *object TSRMLS_DC)
找到出处。

–EOF-

PHP源码阅读笔记二十六:PHP快速排序源码实现的简化版本

PHP源码阅读笔记:PHP中的快速排序实现的简化版本
这段时间在复习数据结构,有看到排序及经典的快速排序
于是有了看下PHP中实现排序的方式,在Zend目录下我们可以看到zend_qsort.c文件及zend_qsort.h文件
这是PHP实现快速排序的文件所在
从代码中我们可以看到,也许是为了兼容多种数据类型,所以其在交换及比较位置比较复杂,看起来也比较纠结,于是自己将
其中的类型全部换成int类型,得到简化版本的PHP源码中的快速排序
其代码如下 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#include <stdio.h>
 
static qsort_swap(int *a, int *b)
{
	int tmp;
	tmp = *a;
	*a = *b;
	*b = tmp;
}
 
void qsort(int *base, int nmemb)
{
	int *begin_stack[10];
	int *end_stack[10];
	int *begin;
	int *end;
	int *seg1;
	int *seg2;
	int *seg2p;
	int loop;
	unsigned int offset;
 
	/* 使用栈而不是常见的递归实现 */
	begin_stack[0] = base;	//	开始元素位置栈,入栈
	end_stack[0]   = base + (nmemb - 1) ;	//	结束位置栈,入栈
 
	for (loop = 0; loop >= 0; --loop) {
		begin = begin_stack[loop];	//	开始位置出栈
		end   = end_stack[loop];	//	结束位置出栈
 
		while (begin < end) {
			offset = (end - begin) >> 1;	//	取中间位置
 
			qsort_swap(begin, begin + offset);	//	交换开始和中间的位置
 
			seg1 = begin;
			seg2 = end;
 
			while (1) {
				for (; seg1 < seg2 && *begin < *seg1 ; seg1 += 1);
 
				for (; seg2 >= seg1 && *seg2 > *begin; seg2 -= 1);
 
				if (seg1 >= seg2)
					break;
 
				qsort_swap(seg1, seg2);
			}
 
			qsort_swap(begin, seg2);
 
			seg2p = seg2;
 
			if ((seg2p - begin) <= (end - seg2p)) {
				if (seg2p < end) {	//	右侧入栈
					begin_stack[loop] = seg2p + 1;
					end_stack[loop++] = end;
				}
				end = seg2p;
			} else {
				if (seg2p > begin) {	// 左侧入栈
					begin_stack[loop] = begin;
					end_stack[loop++] = seg2p - 1;
				}	//	end if
				begin = seg2p;
			}	//	end if
		}	//	end while
	}	//	end for
 
}
int main(int argc, char *argv[])
{
	int a[10] = {14, 5, 7, 8, 2, 4, 55, 3};
	int i;
	qsort(a, 8);
	for (i = 0; i < 8;i++) 
	{
		printf("%d ", a[i]);
	}
	return 0;
}

看完后,有一个感受:强大的指针,受益非浅!

PHP 源码阅读笔记二十五:next,current,key函数

PHP 源码阅读笔记二十五:next,current,key函数
key — 从关联数组中取得键名
mixed key ( array &array )
key() 返回数组中当前单元的键名。

此函数通过调用zend_hash.c中的zend_hash_get_current_key_ex实现key值的返回
在zend_hash_get_current_key_ex函数中根据nKeyLength属性判断key为字符串或者数字,然后返回

current — 返回数组中的当前单元
mixed current ( array &array )
每个数组中都有一个内部的指针指向它“当前的”单元,初始指向插入到数组中的第一个单元。
current() 函数返回当前被内部指针指向的数组单元的值,并不移动指针。如果内部指针指向超出了单元列表的末端,current() 返回 FALSE。

此函数通过最终是调用zend_hash_get_current_data_ex函数实现value的返回
zend_hash_get_current_data_ex函数直接返回数组元素中存储的值:
*pData = p->pData;
如果数组中存在false元素,则返回值和没有找到的返回值是一样的,这是一个比较纠结的地方

next — 将数组中的内部指针向前移动一位
mixed next ( array &array )
返回数组内部指针指向的下一个单元的值,或当没有更多单元时返回 FALSE。

next() 和 current() 操作十分类似,只有一点区别,在返回值之前将内部指针向前移动一位。即调用了zend_hash_move_forward(target_hash);
这意味着它返回的是下一个数组单元的值并将数组指针向前移动了一位。如果移动指针的结果是超出了数组单元的末端,则 next() 返回 FALSE。
并且和current()一样,当数组元素中存在false时,next()的返回值也会是false

EOF