PHP自动加载

   我们在PHP的项目开发与学习中,如果要引入外部的class文件,一般我们会使用require,include,require_once,include_once来手动加载到相应的目录。当我们进行小项目开发,或者学习阶段这种方法没有一点问题。但是如果进行大项目开发或者写一个框架那么再用这种方法就不行了,例如要加载1000个类就要include 1000次,如果是框架,有些类是根据用户的需要动态的加载的,所以这种方法不可取,并且太多的include操作会降低性能,而且不利于维护。

    PHP5在自动加载PHP类时,如果类不存在,或者没有使用iclude包含进来,PHP都会出错,Zend引擎会自动调用系统内置的魔术函数__autoload()函数,但是需要自己写这个函数,PHP5.2以及更高的版本后,可以使用spl_autoload_register函数自定义自动加载处理函数。当没有调用此函数,默认情况下会使用SPL自定义的spl_autoload函数。
例如在TP中的自动加载应用:
 
 
注意:在CLI模式下运行PHP脚本的话__autoload()无效;
 
要理解自动加载叫首先要了解这几个函数
1 spl_autoload() ;
2 spl_autoload_call() ;
3 spl_autoload_register() ;
说之前之前还是有必要了解SPL :
    spl 是Standard PHP Library(标准PHP库)的缩写。是PHP5引入的一个扩展库,主要功能包括autoload机制的实现及包括各种Iterator接口或类。 SPL autoload机制的实现是通过将函数指针autoload_func指向自己实现的具有自动装载功能的函数来实现的。SPL有两个不同的函数 spl_autoload, spl_autoload_call,通过将autoload_func指向这两个不同的函数地址来实现不同的自动加载机制。
 
1:spl_autoload() 

    spl_autoload 是SPL实现的默认的自动加载函数,功能比较简单。有两个参数,第一个是$class_name,表示类名,第二个参 数$file_extensions是可选的,表示类文件扩展名,可以在$file_extensions中指定多个扩展名,之间用分号隔开。不指定将使用默认的扩展名.inc或.php。让spl_autoload自动起作用是将autoload_func指向spl_autoload,方法是使用 spl_autoload_register函数。在PHP脚本中第一次调用spl_autoload_register()时不使用任何参数,就可以将 autoload_func指向spl_autoload。

2:spl_autoload_call()

   spl_autoload() 知道spl_autoload的功能比较简单,它是在SPL扩展中实现的,无法扩充它的功能。如果想实现自己的更灵活的自动加载机制就需要使用spl_autoload_call函数。在SPL模块内部,有一个全局变量autoload_functions,本质上是 一个HashTable,可以看作一个链表,每个元素都是一个函数指针,指向一个具有自动加载类功能的函数。 spl_autoload_call会按顺序执行这个链表中每个函数,在每个函数执行完成后都判断一次需要的类是否已经加载, 如果加载成功就直接返回,不再继续执行链表中的其它函数。如果这个链表中所有的函数都执行完成后类还没有加载,spl_autoload_call会直接 退出,并不向用户报告错误。使用了autoload机制,不能保证类就一定能正确的自动加载。在php5中的标准库方法spl_autoload相当于实现自己的__autoload

3:spl_autoload_register() 注册__autoload()函数 
    spl_autoload_register()函数是主流框架使用最多的也是非常核心的函数之一,可实现自动注册函数和类,实现类似__autoload() 函数功能,简化了类的调用与加载,提高了工作的效率
注意:如果加载的文件包含函数,使用则一定需要实例化里面的类,否则就产生异常情况 Call to undefined function错误。
函数原型:
1 bool spl_autoload_register ([ callback $autoload_function ] ) 
 
    将函数注册到SPL __autoload函数栈中。如果该栈中的函数尚未激活,则激活它们。 如果在你的程序中已经实现了__autoload函数,它必须显式注册到__autoload栈中。因为 spl_autoload_register()函数会将Zend Engine中的__autoload函数取代为spl_autoload() spl_autoload_call()。 参数  autoload_function 欲注册的自动装载函数。如果没有提供任何参数,则自动注册autoload的默认实现函数 spl_autoload()。 
    如果需要多条 autoload 函数,spl_autoload_register() 满足了此类需求。 它实际上创建了 autoload 函数的队列,按定义时的顺序逐个执行。相比之下, __autoload() 只可以定义一次。
 
总结:

    PHP在实例化一个 对象时(实际上在实现接口,使用类常数或类中的静态变量等),先会在系统中查找该类(或接口)是否存在,如果不存在的话就 尝试使用autoload机制来加载该类。

autoload机制的主要执行过程为:

1检查执行器全局变量函数指针autoload_func是否为null。
2:如果autoload_func==null, 则查找系统中是否定义有__autoload()函数,如果没有,则报告错误并退出。
3如果定义了__autoload()函数,则执行__autoload()尝试加载类,并返回加载结果。
4:如果autoload_func不为null,则直接执行autoload_func指针指向的函数用来加载类。此时并不检查__autoload()函数是否定义。

总结如下:

  自动加载方便了面向对象和代码复用,但是多个类库不同的__autoload又会导致混乱。可以用spl_autoload解决,将不同开发者的拦截器函数都注册到自动加载函数的hashtable中。spl实现自动加载的机制是维护一个hashtable,里面存储有具有自动加载功能的各个函数。当触发自动加载机制时,zend会在遍历执行这个hashtable里面的函数,直到成功加载类或加载失败后返回。当需要使用自动加载功能时,使用函数spl_autoload_register()或spl_autoload_register('autoloadfuncitonname')无参的spl_autoload_register()会默认加载spl_autoload()函数,该函数功能有限,只能在inlcude_path中搜索指定扩展名的类库。有参的spl_autoload_register()默认不再加载spl_autoload()函数。可以通过spl_autoload_functions()查看当前自动加载hashtable中的函数,该函数返回一个数组

注意:使用spl_autoload时,系统会忽略拦截器__autoload,除非显式地使用spl_autoload_register('__autoload')将其加入hashtable

 

PHP提供了两种方法来实现自动自动加载,

一种是使用用户定义的__autoload()函数;

1 <?php
2   $file = str_replace("\\", '/', $classname);  //可以开转换字符
3   function __autoload($className) {               
4       if (file_exists($file_url  .  '.php')) { //先判断是否存在文件 
5          include_once($file_url .  '.php');               
6       }      
7   }        
8   $a = new Acls();
9 ?>        

另外一种就是设计一个函数,将autoload_func指针指向它,使用C语言在PHP扩展中实现,即:使用spl自动加载 spl_autoload_register()

 1 <?php
 2     spl_autoload_register(function($classname){
 3          if($classname){
 4               $file = '../';
 5               $file .= str_replace("\\", '/', $classname);
 6               $file .= '.php';
 7               if(file_exists($file)){
 8                    include $file;
 9               }
10         }
11     });
12 
13 ?>

或者:

 1 <?php
 2     class Loader { 
 3         public static function autoload($class) { 
 4             $path = ''; 
 5             $path = str_replace('_', '/', $class) . '.php'; 
 6             include_once($path); 
 7         } 
 8     } 
 9     spl_autoload_register(array('Loader', 'autoload')); 
10 ?>

 


 
 
posted @ 2015-05-31 18:58  赵亚飞  阅读(1210)  评论(0编辑  收藏  举报