php⾃动加载函数、⾃动加载⽅法、⾃动加载类
在PHP开发过程中,如果希望从外部引⼊⼀个class,通常会使⽤include和require⽅法,去把定义这个class的⽂件包含进来。这个在⼩规模开发的时候,没什么⼤问题。但在⼤型的开发项⽬中,这么做会产⽣⼤量的require或者include⽅法调⽤,这样不因降低效率,⽽且使得代码难以维护,况且 require_once的代价很⼤。
在PHP5之前,各个PHP框架如果要实现类的⾃动加载,⼀般都是按照某种约定⾃⼰实现⼀个遍历⽬录,⾃动加载所有符合约定规则的⽂件的类或函数。当然,PHP5之前对⾯向对象的⽀持并不是太好,类的使⽤也没有现在频繁。在PHP5后,当加载PHP类时,如果类所在⽂件没有被包含进来,或者类名出错,Zend引擎会⾃动调⽤__autoload 函数。此函数需要⽤户⾃⼰实现__autoload函数。在PHP5.1.2版本后,可以使⽤spl_autoload_register函数⾃定义⾃动加载处理函数。当没有调⽤此函数,默认情况下会使⽤SPL ⾃定义的spl_autoload函数。
spl_autoload函数的默认实现如果不使⽤任何参数调⽤ autoload_register() 函数,则以后在进⾏ __autoload() 调⽤时会⾃动使⽤此函数。
SPL有两个不同的函数 spl_autoload, spl_autoload_call,通过将autoload_func指向这两个不同的函数地址来实现不同的⾃动加载机制。
spl_autoload 是SPL实现的默认的⾃动加载函数,它的功能⽐较简单。它可以接收两个参数,第⼀个参数是$class_name,表⽰类名,第⼆个参数$file_extensions是可选的,表⽰类⽂件的扩展名" title="扩展名">扩展名,可以在$file_extensions中指定多个扩展名" title="扩展名">扩展名,护展名之间⽤分号隔开即可;如果不指定的话,它将使⽤默认的扩展名" title="扩展名">扩展名.inc或.php。spl_autoload⾸先将$class_name变为⼩写,然后在所有的 include path中搜索$class_name.inc或$class_name.php⽂件(如果不指定$file_extensions参数的话),如果到,就加载该类⽂件。你可以⼿动使⽤spl_autoload(”Person”, “.class.php”)来加载Person类。实际上,它跟require/include差不多,不同的它可以指定多个扩展名" title="扩展名">扩展名。
怎样让spl_autoload⾃动起作⽤呢,也就是将autoload_func指向spl_autoload?答案是使⽤ spl_autoload_register函数。在PHP脚本中第⼀次调⽤spl_autoload_register()时不使⽤任何参数,就可以将 autoload_func指向spl_autoload。
通过上⾯的说明我们知道,spl_autoload的功能⽐较简单,⽽且它是在SPL扩展中实现的,我们⽆法扩充它的功能。如果想实现⾃⼰的更灵活的⾃动加载机制怎么办呢?这时,spl_autoload_call函数闪亮登场了。
我们先看⼀下spl_autoload_call的实现有何奇妙之处。在SPL模块内部,有⼀个全局变量autoload_functi
ons,它本质上是⼀个HashTable,不过我们可以将其简单的看作⼀个链表,链表中的每⼀个元素都是⼀个函数指针,指向⼀个具有⾃动加载类功能的函数。
spl_autoload_call本⾝的实现很简单,只是简单的按顺序执⾏这个链表中每个函数,在每个函数执⾏完成后都判断⼀次需要的类是否已经加载,如果加载成功就直接返回,不再继续执⾏链表中的其它函数。如果这个链表中所有的函数都执⾏完成后类还没有加
载,spl_autoload_call就直接退出,并不向⽤户报告错误。因此,使⽤了autoload机制,并不能保证类就⼀定能正确的⾃动加载,关键还是要看你的⾃动加载函数如何实现。
在php5中的标准库⽅法spl_autoload相当于实现⾃⼰的__autoload
<?php
function __autoload($classname){
if(is_file($classname.'.php'){
include $classname.'.php';
} elseif(is_file($classname.'.inc'){
include $classname.'.inc';
}
}
它会在注册⽬录下⾃动寻与$classname同名的.php/.inc⽂件。当然,你也可以指定特定类型的⽂件,⽅法是注册扩展名
<?php
spl_autoload_extensions('.php,.inc,.some');
这样,它也会搜索.some⽂件。默认,php是不会启动spl_autoload的,那么怎样才能⾃动让spl_autoload⽣效呢呢?⽅法是
<?php
spl_autoload_register();
spl_autoload_register有⼀个$callback参数,如果不指定,它就会⾃动注册spl_autoload,为了能搜寻更
多的⾃动加载⽬录,可以在这些代码前⾯设置⾃动加载⽬录
<?php
set_include_path(get_include_path() . PATH_SEPARATOR . 'some/path' . DIRECTORY_SEPARATOR);
这样,当php不到指定的类时,就会在set_include_path指定的⽬录下寻。
这些⽅法常⽤在php框架中。⽐如把上⾯的介绍串连起来:
<?php
set_include_path(get_include_path() . PATH_SEPARATOR . 'some/path' . DIRECTORY_SEPARATOR);
spl_autoload_extensions('.php,.inc,.some');
spl_autoload_register();
当你要加载some/path下⾯的classA类时,它会在⽬录下寻classa.php或classa.inc或classa.some,这
样你就可以放⼼地运⽤new classA 或extends classA
<?php
ClassB extends ClassA {
// code..
}
$a = new ClassA;
$b = new ClassB;
spl_autoload
spl_autoload_register 注册⼀个⾃动加载函数函数参数⼀个当类不存在时⾃动加载的函数
spl_autoload_call尝试调⽤所有已注册的⾃动加载函数来装载请求类
spl_autoload_extensions注册并返回spl_autoload函数使⽤的默认⽂件扩展名。当不使⽤任何参数调⽤
此函数时,它返回当前的⽂件扩展名的列表,不同的扩展名⽤逗号分隔。要修改⽂件扩展名列表,⽤⼀个逗号分隔的新的扩展名列表字符串来调⽤本函数即可。中⽂注:默认的spl_autoload函数使⽤的扩展名是".inc,.php"。实例:spl_autoload_extensions(".inc, .php, .lib, .lib.php ");
spl_autoload_unregister注销已注册的⾃动加载函数函数参数注册时的函数名
spl_autoload_functions 返回所有已注册的z⾃动加载函数函数。
1、 __autoload⽰例:
function __autoload($class_name) {
echo '__autload class:', $class_name, '<br />';
}
new Demo();
以上的代码在最后会输出:__autload class:Demo。
并在此之后报错显⽰: Fatal error: Class ‘Demo’ not found
我们⼀般使⽤_autoload⾃动加载类如下:
<?php
function __autoload($class_name) {
require_once ($class_name . “class.php”);
}
$memo= new Demo();
常用的php代码实例我们可以看出_autoload⾄少要做三件事情,第⼀件事是根据类名确定类⽂件名,第⼆件事是确定类⽂件所在的磁盘路径(在我们的例⼦是最简单的情况,类与调⽤它们的PHP程序⽂件在同⼀个⽂件夹下),第三件事是将类从磁盘⽂件中加载到系统中。第三步最简单,只需要使⽤include/require即可。要实现第⼀步,第⼆步的功能,必须在开发时约定类名与磁盘⽂件的映射⽅法,只有这样我们才能根据类名到
它对应的磁盘⽂件。
因此,当有⼤量的类⽂件要包含的时候,我们只要确定相应的规则,然后在__autoload() 函数中,将类
名与实际的磁盘⽂件对应起来,就可以实现lazy loading的效果。从这⾥我们也可以看出__autoload()函数的实现中最重要的是类名与实际的磁盘⽂件映射规则的实现。
但现在问题来了,假如在⼀个系统的实现中,假如需要使⽤很多其它的类库,这些类库可能是由不同的开发⼯程师开发,其类名与实际的磁盘⽂件的映射规则不尽相同。这时假如要实现类库⽂件的⾃动加载,就必须在__autoload()函数中将所有的映射规则全部实现,因此__autoload()函数有可能会⾮常复杂,甚⾄⽆法实现。最后可能会导致__autoload()函数⼗分臃肿,这时即便能够实现,也会给将来的维护和系统效率带来很⼤的负⾯影响。在这种情况下,⼀种新的解决⽅案,即spl_autoload_register()函数。
2、此函数的功能就是把函数注册⾄SPL的__autoload函数栈中,并移除系统默认的__autoload()函数。下⾯的例⼦可以看出:
function __autoload($class_name) {
echo '__autload class:', $class_name, '<br />';
}
function classLoader($class_name) {
echo 'SPL load class:', $class_name, '<br />';
}
spl_autoload_register('classLoader');
new Test();//结果:SPL load class:Test
语法:bool spl_autoload_register ( [callback $autoload_function] ) 接受两个参数:⼀个是添加到⾃动加载栈的函数,另外⼀个是加载器不能到这个类时是否抛出异常的标志。第⼀个参数是可选的,并且默认指向 spl_autoload()函数,这个函数会⾃动在路径中查具有⼩写类名和.php扩展或者.ini扩展名,或者任何注册到 spl_autoload_extensions()函数中的其它扩展名的⽂件。
<?php
class CalssLoader
{
public static function loader($classname)
{
$class_file = strtolower($classname).".php";
if (file_exists($class_file)){
require_once($class_file);
}
}
}
// ⽅法为静态⽅法
spl_autoload_register('CalssLoader::loader');
$test = new Test();
⼀旦调⽤spl_autoload_register()函数,当调⽤未定义类时,系统会按顺序调⽤注册到spl_autoload_regi
ster()函数的所有函数,⽽不是⾃动调⽤__autoload()函数。如果要避免这种情况,需采⽤⼀种更加安全的spl_autoload_register()函数的初始化调⽤⽅法:
if(false === spl_autoload_functions()){
if(function_exists('__autoload')){
spl_autoload_registe('__autoload',false);
}
}
spl_autoload_functions()函数会返回已注册函数的⼀个数组,如果SPL⾃动加载栈还没有被初始化,它会返回布尔值false。然后,检查是否有⼀个名为
__autoload()的函数存在,如果存在,可以将它注册为⾃动加载栈中的第⼀个函数,从⽽保留它的功能。之后,可以继续注册⾃动加载函数。
还可以调⽤spl_autoload_register()函数以注册⼀个回调函数,⽽不是为函数提供⼀个字符串名称。如提供⼀个如array('class','method')这样的数组,使得可以使⽤某个对象的⽅法。
下⼀步,通过调⽤spl_autoload_call('className')函数,可以⼿动调⽤加载器,⽽不⽤尝试去使⽤那个类。这个函数可以和函数
class_exists('className',false)组合在⼀起使⽤以尝试去加载⼀个类,并且在所有的⾃动加载器都不能到那个类的情况下失败。
f(spl_autoload_call('className') && class_exists('className',false)){
} else {
}
SPL ⾃动加载功能是由spl_autoload() ,spl_autoload_register(), spl_autoload_functions() ,spl_autoload_extensions()和spl_autoload_call()函数提供的。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论