PHP __autoload机制原理

__autoload机制是PHP5引入的一种自动执行的机制,当new一个不存在的对象时,PHP便会执行__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机制是一个不错的选择。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值