__autoload机制是PHP5引入的一种自动执行的机制,当new一个不存在的对象时,PHP便会执行__autoload函数的代码。而你可以在__autoload函数中引入需要的类文件,这样就不必每个同时引入一大堆的类库,只需要必要的时候才引入。例如:
如果PHP找不到MyObject类,便会执行__autoload函数,这样就可以自动引入这个类文件。
那么在PHP内部是怎么实现这个机制的呢?
当在PHP脚本new一个对象的时候,PHP内核会调用zend_lookup_class()函数,而这个函数的作用是从PHP的类表(存放类的HashTable)中查找要进行new操作的类。如果没有找到,便会调用用户自动的__autoload函数,zend_lookup_class()函数的代码如下:
从上面的代码可以知道,使用__autoload机制会调用从类表中查找两次要进行new操作的类,而且还调用了一次__autoload函数。所以性能肯定比不上直接手工引入类文件。不过这个机制的好处就是方便,所以如果不是很在意性能的问题的话,__autoload机制是一个不错的选择。
01
02
03
04
05
06
|
<?php
function
__autoload(
$classname
) {
require
(PATH_TO_CLASS.
'/'
.
$classname
.
'.class.php'
);
}
$obj
=
new
MyObject();
?>
|
如果PHP找不到MyObject类,便会执行__autoload函数,这样就可以自动引入这个类文件。
那么在PHP内部是怎么实现这个机制的呢?
当在PHP脚本new一个对象的时候,PHP内核会调用zend_lookup_class()函数,而这个函数的作用是从PHP的类表(存放类的HashTable)中查找要进行new操作的类。如果没有找到,便会调用用户自动的__autoload函数,zend_lookup_class()函数的代码如下:
01
02
03
04
05
06
07
08
09
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
|
ZEND_API int zend_lookup_class(char *name, int name_length, zend_class_entry ***ce TSRMLS_DC)
{
zval **args[1];
zval autoload_function;
zval class_name, *class_name_ptr = &class_name;
zval *retval_ptr;
int retval;
char *lc_name;
zval *exception;
char dummy = 1;
lc_name = do_alloca(name_length + 1);
/* 把类名转换成小写 */
zend_str_tolower_copy(lc_name, name, name_length);
/* 查找要进行new操作的类 */
if
(zend_hash_find(EG(class_table), lc_name, name_length+1, (void **) ce) == SUCCESS) {
/* 找到的话,返回SUCCESS */
free_alloca(lc_name);
return
SUCCESS;
}
if
(zend_is_compiling(TSRMLS_C)) {
free_alloca(lc_name);
return
FAILURE;
}
/* 找不到要进行new操作的类 */
if
(EG(in_autoload) == NULL) {
ALLOC_HASHTABLE(EG(in_autoload));
zend_hash_init(EG(in_autoload), 0, NULL, NULL, 0);
}
if
(zend_hash_add(EG(in_autoload), lc_name, name_length+1, (void**)&dummy, sizeof(char), NULL) == FAILURE) {
free_alloca(lc_name);
return
FAILURE;
}
/* 构建一个__autoload函数名变量 */
ZVAL_STRINGL(&autoload_function,
"__autoload"
, sizeof(
"__autoload"
)-1, 0);
/* 要进行new操作的类名 */
INIT_PZVAL(class_name_ptr);
ZVAL_STRINGL(class_name_ptr, name, name_length, 0);
args[0] = &class_name_ptr;
exception = EG(exception);
EG(exception) = NULL;
/* 调用用户自定义的__autoload函数 */
retval = call_user_function_ex(EG(function_table), NULL, &autoload_function, &retval_ptr, 1, args, 0, NULL TSRMLS_CC);
zend_hash_del(EG(in_autoload), lc_name, name_length+1);
if
(retval == FAILURE) {
EG(exception) = exception;
free_alloca(lc_name);
return
FAILURE;
}
if
(EG(exception)) {
free_alloca(lc_name);
zend_error(E_ERROR,
"__autoload(%s) threw an exception of type '%s'"
, name, Z_OBJCE_P(EG(exception))->name);
return
FAILURE;
}
EG(exception) = exception;
zval_ptr_dtor(&retval_ptr);
/* 再一次从类表中查找要进行new操作的类 */
retval = zend_hash_find(EG(class_table), lc_name, name_length + 1, (void **) ce);
free_alloca(lc_name);
return
retval;
}
|
从上面的代码可以知道,使用__autoload机制会调用从类表中查找两次要进行new操作的类,而且还调用了一次__autoload函数。所以性能肯定比不上直接手工引入类文件。不过这个机制的好处就是方便,所以如果不是很在意性能的问题的话,__autoload机制是一个不错的选择。