+ -
当前位置:首页 → 问答吧 → PHP __autoload机制原理

PHP __autoload机制原理

时间:2012-09-25

来源:互联网

http://liexusong.sinaapp.com/?p=261

__autoload机制是PHP5引入的一种自动执行的机制,当new一个不存在的对象时,PHP便会执行__autoload函数的代码。而你可以在__autoload函数中引入需要的类文件,这样就不必每个同时引入一大堆的类库,只需要必要的时候才引入。例如:
<?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()函数的代码如下:
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机制是一个不错的选择。














作者: liexusong   发布时间: 2012-09-25

学习了

作者: qianxunww   发布时间: 2012-10-25

谢谢了 好东西

作者: yangtze   发布时间: 2013-03-28