果冻想
认真玩技术的地方

PHP基础学习之SPL Autoload机制

前言

上一篇文章总结了PHP中的__autoload机制。这一篇是上一篇的姊妹篇。关于__autoload现在基本都被抛弃了,使用的越来越少了,但是语言的发展都是向着开发友好化的。由于__autoload机制的缺点非常明显,为了解决这些痛点,就有了这篇需要总结的SPL Autoload。

什么是SPL Autoload

SPL是Standard PHP Library(标准PHP库)的缩写。它是PHP5引入的一个扩展库,其主要功能包括autoload机制的实现及包括各种Iterator接口或类。SPL autoload机制的实现是通过将函数指针autoload_func指向自己实现的具有自动装载功能的函数来实现的。

SPL有两个不同的函数spl_autoload, spl_autoload_call,通过将autoload_func指向这两个不同的函数地址来实现不同的自动加载机制。

spl_autoload实现机制

spl_autoload是SPL实现的默认的自动加载函数,它的功能比较简单。它的函数声明如下:

void spl_autoload ( string $class_name [, string $file_extensions ] )

第一个参数是类名字,第二个参数是文件扩展名;在默认情况下,该函数先将类名转换成小写,再在小写的类名后加上.inc.php的扩展名作为文件名,然后在所有的包含路径(include paths)中检查是否存在该文件。

现在我们有一个A.class.php、B.class.php和Main.cpp,其中A.class.php代码如下:

<?php
class A {
    function __construct () 
    { 
        var_dump('construct A');
    } 
}

B.class.php内容如下:

<?php
class B {
    function __construct () 
    { 
        var_dump('construct B');
    } 
}

Main.php代码如下:

<?php
set_include_path(get_include_path().PATH_SEPARATOR.'C:\Users\Jelly\Desktop\autoload');
spl_autoload_extensions('.class.php');
spl_autoload_register();

new A();
new B();

输出内容如下:

string(11) "construct A" string(11) "construct B"

通过调用spl_autoload_extensions函数,设置spl_autoload加载的文件的扩展名。然后通过使用spl_autoload_register函数,在PHP脚本中第一次调用spl_autoload_register()时不使用任何参数,就可以将 autoload_func指向spl_autoload,从而使用spl_autoload作为默认的加载函数。

spl_autoload_call实现机制

通过上面的说明我们知道,spl_autoload的功能比较简单,和__autoload的功能区别不大,而且它是在SPL扩展中实现的,我们无法扩充它的功能。如果想实现自己的更灵活的自动加载机制怎么办呢?此时,我们就需要用到spl_autoload_call了。

我们先来简单的说说spl_autoload_call的内部机制。在SPL模块内部,有一个全局变量autoload_functions,它本质上是一个HashTable,不过我们可以将其简单的看作一个链表,链表中的每一个元素都是一个函数指针,指向一个具有自动加载类功能的函数。spl_autoload_call本身的实现很简单,只是简单的按顺序执行这个链表中每个函数,在每个函数执行完成后都判断一次需要的类是否已经加载,如果加载成功就直接返回,不再继续执行链表中的其它函数。如果这个链表中所有的函数都执行完成后类还没有加载,spl_autoload_call就直接 退出,并不向用户报告错误。因此,使用了autoload机制,并不能保证类就一定能正确的自动加载,关键还是要看你的自动加载函数如何实现。

说白了,spl_autoload_call彻底的解决了__autoload()是全局函数只能定义一次,不够灵活的缺陷,我们可以自行定义每个模块的autoload机制,然后将我们定义的加载机制通过spl_autoload_register函数注册到SPL模块内部维护的调用堆栈中即可。我们可以注册多个加载策略,反正SPL内部会依次执行我们注册的加载策略,从而加载找不到的类。基于此,我们还是通过代码来说明。

现在有以下目录结构:

project/
    ├── ModuleA/
    │  ├── A.class.php
    │  ├── autoload.php
    ├── ModuleB/
    │  ├── B.class.php
    │  ├── autoload.php
    ├── Main.php

ModuleA/autoload.php代码如下:

<?php
function autoloadA($className) {
    var_dump('autoloadA called');
    $fileName = __DIR__ . '/' . $className . '.class.php';

    if (file_exists($fileName)) {  
        require_once($fileName);  
    }
}

spl_autoload_register('autoloadA');

ModuleB/autoload.php代码如下:

<?php
function autoloadB($className) {
    var_dump('autoloadB called');
    $fileName = __DIR__ . '/' . $className . '.class.php';

    if (file_exists($fileName)) {    
        require_once($fileName);     
    }
}

spl_autoload_register('autoloadB');

Main.php代码如下:

<?php
require __DIR__.'/ModuleA/autoload.php';
require __DIR__.'/ModuleB/autoload.php';

new A();
new B();

这样就通过spl_autoload_register函数实现了多模块自定义加载。当然了,上面的代码只是用于演示,实际框架中,当和composer结合时,又会有变化,但是万变不离其宗。了解了我这里总结的,再去理解其它的都不会有太大问题的。

总结

到此,关于PHP中的SPL Autoload机制就总结完毕了。该系列我还会一直总结下去。希望大家喜欢,也希望我的总结能够真正的帮助大家。期待大家和我一起沟通,交流。

果冻想-一个原创技术文章分享网站。

2018年2月7日 于呼和浩特。

未经允许不得转载:果冻想 » PHP基础学习之SPL Autoload机制
网站维护离不开您的支持,您可以赞助本站,谢谢支持
×

感谢您的支持,我们会一直保持!

扫码支持
请土豪扫码随意打赏

打开支付宝扫一扫,即可进行扫码打赏哦

分享从这里开始,精彩与您同在

赞助本站
关注微信公众号和果冻一起分享你的疑惑与心得。
关注微信公众号
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!

 

玩技术,我们是认真的

联系我们关于果冻