PHP 标准库 SPL

| 选择喜欢的代码风格  

PHP在魔术函数__autoload()方法出现以前,如果你要在一个程序文件中实例化100个对象,那么你必须用include或者require包含进来100个类文件,或者你把这100个类定义在同一个类文件中——相信这个文件一定会非常大。但是__autoload()方法出来了,以后就不必为此大伤脑筋了,这个类会在你实例化对象之前自动加载制定的文件。

这里重点说说 spl_autoload_register 的一些事,先说__autoload:


在了解 spl_autoload_register 函数之前先来看另一个函数:__autoload

这是一个自动加载函数,在PHP5中,当我们实例化一个未定义的类时,就会触发此函数。看下面例子:

printit.class.php

<?php 
class PRINTIT { 
 
    function doPrint() {
        echo 'hello world';
    }
}
?>

index.php

<?
function __autoload( $class ) {
    $file = $class . '.class.php';  
    if ( is_file($file) ) {  
        require_once($file);  
    }
} 
 
$obj = new PRINTIT();
$obj->doPrint();
?>

运行 index.php 后正常输出 hello world 。在 index.php 中,由于没有包含 printit.class.php ,在实例化 PRINTIT时,自动调用 __autoload 函数,参数 $class 的值即为类名 printit,此时 printit.class.php 就被引进来了。

在面向对象中这种方法经常使用,可以避免书写过多的引用文件,同时也使整个系统更加灵活。

二、spl_autoload_register()


再看 spl_autoload_register(),这个函数与 __autoload 有与曲同工之妙,看个简单的例子:

<?php
function loadprint( $class ) {
    $file = $class . '.class.php';  
    if (is_file($file)) {  
        require_once($file);  
    } 
} 
 
spl_autoload_register( 'loadprint' ); 
 
$obj = new PRINTIT();
$obj->doPrint();
?>

__autoload换成 loadprint 函数。但是 loadprint 不会像 __autoload 自动触发,这时 spl_autoload_register() 就起作用了,它告诉 PHP 碰到没有定义的类就执行 loadprint()这种机制也称为 Lazy loading (惰性加载)

spl_autoload_register() 调用静态方法

<?php
class test {
     public static function loadprint( $class ) {
        $file = $class . '.class.php';  
        if (is_file($file)) {  
            require_once($file);  
        } 
    }
} 
 
spl_autoload_register(  array('test','loadprint')  );
//另一种写法:spl_autoload_register(  "test::loadprint"  ); 
 
$obj = new PRINTIT();
$obj->doPrint();
?>

三、spl_autoload()函数及spl_autoload_register()的关系


__autoload 函数是用来处理自动加载的函数,在 PHP 找不到指定类时就会去调用自动加载类,加载所需要的类。

__autoload 只是一个抽象定义,实现(实现就是定义如何加载,加载的规则是什么,加载的文件是什么等等)是交给用户的,而 spl_autoload 则是 SPL 所定义的 __autoload 一种实现。 spl_autoload 函数所实现的加载规则就是去 include paths 中查找对于的类。 spl_autoload 遵循是是 psr-0 的载入规则,而 include paths 就是载入时被查询的路径。

其他自己实现的 __autoload 类都可以通过 spl_autoload_register 进行注册,注册之后就可以在需要类时自动调用被注册的方法进行加载了。 spl_autoload 也是 __autoload 的一种实现,按理也是需要注册的,只不过因为是内部的默认实现,所有已经自动注册在 PHP 里了。

spl_autoload 如今来看并没有太多用处,应该是因为历史问题残留在 PHP 中的,目前绝大多数程序都没有使用 spl_autoload 去做自动加载,因为它的规则已经定死,并不适合衍生一些功能。

插一句,因为 PHP 只有一个自动加载方法,所以 SPL 的 spl_autoloadspl_autoload_register 要争抢这个方法,所以在 SPL 的 C 实现中,用了好多折衷的办法。在没有使用 spl_autoload_register 注册任何自定的自动加载函数时, PHP 的自动加载方法是挂在 spl_autoload 下的,而 spl_autoload_register 注册了自动加载函数后,PHP 的自动加载方法是挂在 spl_autoload_call 这个方法下的,而 spl_autoload 也会成为一个备选项进入 spl_autoload_register 的自动加载队列。

四、autoload 效率问题及对策


使用autoload机制时,很多人的第一反应就是使用autoload会降低系统效率,甚至有人干脆提议为了效率不要使用autoload。在我们了解了autoload实现的原理后,我们知道autoload机制本身并不是影响系统效率的原因,甚至它还有可能提高系统效率,因为它不会将不需要的类加载到系统中。

那么为什么很多人都有一个使用autoload会降低系统效率的印象呢?实际上,影响autoload机制效率本身恰恰是用户设计的自动加载函数。如果它不能高效的将类名与实际的磁盘文件(注意,这里指实际的磁盘文件,而不仅仅是文件名)对应起来,系统将不得不做大量的文件是否存在(需要在每个include path中包含的路径中去寻找)的判断,而判断文件是否存在需要做磁盘I/O操作,众所周知磁盘I/O操作的效率很低,因此这才是使得autoload机制效率降低的罪魁祸首!

因此,我们在系统设计时,需要定义一套清晰的将类名与实际磁盘文件映射的机制。这个规则越简单越明确,autoload机制的效率就越高。

结论:autoload机制并不是天然的效率低下,只有滥用autoload,设计不好的自动装载函数才会导致其效率的降低。

五、Autoload 总结


总结起来,自动加载功能带来了几处优点:

  1. 使用类之前无需 include / require
  2. 使用类的时候才会 include / require 文件,实现了 lazy loading ,避免了 include / require 多余文件。
  3. 无需考虑引入 类的实际磁盘地址 ,实现了逻辑和实体文件的分离。



发表评论