说起PHP的自动加载,很多同学可能都会想到各种框架的自动加载功能,PHP规范中的PSR0和PSR4原则,Composer的自动加载功能等等,这些都为我们的开发提供了很大的方便。

那么PHP自动加载的前因后果到底是什么?PHP的内部原理又是怎么样的呢?接下来我就根据自己的理解进行一下分析总结:

为什么会有自动加载?

在PHP面向对象(OO)编程中,为了方便管理,我们都会把一个类写在一个单独的文件中,那么如果想在A类中使用B类的功能,就需要把B类加载到A类。对于这样的需求在最原始的时候,我们是通过require 和 include 语法实现的,这2种语法结果基本一样,执行流程有一些区别,这里不解释。例如:

//文件 B.php
<?php
class B{
public function echo_info(){
echo "我是class B中的方法执行结果";
}
}
?>
//文件 A.php
<?php
require 'b.php';//include 'b.php';
class A{
public function test(){
$b_object = new B();
$b_object->echo_info();
}
}
$a_object = new A();
$a_oject->test();
?>
命令行输入:#php a.php
输出: “我是class B中的方法执行结果“
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

于是,PHP5实现了类的自动加载(Autoload)功能,这个功能最初是通过PHP的一个魔术方法__autoload()实现的。后来,PHP扩展SPL(Standard PHP Library 标准PHP类库)又实现了更强大的自动加载机制。

PHP原始自动加载

首先,先介绍下__autoload()方法。还是刚刚的例子,使用__autoload()可以做如下修改:

//文件 B.php 不做修改
//文件 A.php
<?php
class A{
public function test(){
$b_object = new B();
$b_object->echo_info();
}
}
function __autoload($classname){
require $classname.'.php';//include 'b.php';
}
$a_object = new A();
$a_oject->test();
?>
命令行输入:#php a.php
输出: “我是class B中的方法执行结果“
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

我们在A文件中加了一个函数:__autoload(),并且自己在函数中编写了相应的引入方法,运行之后同样得到了结果,没有报错。我们需要明确 __autoload()函数PHP在找不到类的时候会自动执行,但是PHP内部并没有定义这个函数,这个函数需要开发着自己定义,并且编写内部逻辑,PHP只负责在需要的时候自动调用执行。而且在调用的时候会自动传人要加载的类名作为参数。

有了__autoload()函数,可以看出,如果我们现在需要引入100个其它文件,只需要订好一个规则,编写一个函数就可以了。这比直接用require/inlude有了很大进步,但是同样也有新的问题,在一个项目中,我们只能编写一个__autoload()函数,如果项目比较大,加载每个文件都使用同样的规则显然是不现实的,那么我们可能就需要在__autoload()中编写复杂的规则逻辑来满足加载不同文件的需求。这同样会使得__autoload()函数变得复杂臃肿,难以维护管理。

于是,SPL(Standard PHP Library 标准PHP类库)的自动加载机制就应时而生了。

SPL 自动加载

首先,明确一点,PHP在实例化一个对象时(实际上在实现接口,使用类常数或类中的静态变量,调用类中的静态方法时都会如此),首先会在系统中查找该类(或接口)是否存在,如果不存在的话就尝试使用autoload机制来加载该类。而autoload机制的主要执行过程为:

  • 检查执行器全局变量函数指针autoload_func是否是NULL;
  • 如果 autoload_func==NULL ,则查找系统是否定义 __autoload() 函数,如果定义了,则执行并返回加载结果。如果没有定义,则报错并退出;
  • 如果 autoload_func 不等于NULL,则直接执行 autoload_func 指向的函数加载类,此时并不检查 __autoload() 函数是否定义。

通过对PHP自动加载流程的了解,可以看到PHP实际上提供了两种方法来实现自动装载机制:

  • 一种我们前面已经提到过,是使用用户定义的__autoload()函数,这通常在PHP源程序中来实现;
  • 另外一种就是设计一个函数,将autoload_func指针指向它,这通常使用C语言在PHP扩展中实现,即 SPL autoload机制。

如果两种方式都实现了,也就是 autoload_func 不等于NULL,程序只会执行第二种方式,__autoload() 函数是不会被执行的。

先看一个 SPL 自动加载例子:

B.php文件不变
A.php
<?php
class A{
public function test(){
$b_object = new B();
$b_object->echo_info();
}
} function __autoload($classname){
require $classname.'.php';//include 'b.php';
} function my_autoload($classname){
require $classname.'.php';//include 'b.php';
echo 'my_autoload ';
} spl_autoload_register('my_autoload');
$a_object = new A();
$a_object->test(); 结果:my_autoload 我是class B中的方法执行结果
?>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

在这个小例子,可以看到,通过 spl_autoload_register(’my_autoload’),实现了 当程序执行找不到类B时,会执行 自定义的 my_autoload()函数,加载B类。实际上 spl_autoload_register(’my_autoload’) 的作用就是 把autoload_func 指针指向 my_autoload()。现在,整个PHP 自动加载过程就明白了。

接下来我们详细分析下 SPL 自动加载的整个过程。

首先还是刚刚的小例子,假如把spl_autoload_register(’my_autoload’) 改成 spl_autoload_register()不添加任何参数,B类能被加载吗?答案是:YES。 
为什么呢?

因为SPL扩展内部自己定义了一个自动加载函数 spl_autoload(),实现了自动加载的功能,如果我们不定义自己的自动加载函数,并且程序里写了 spl_autoload_register()(如果不传参数,必须是第一次执行才会有效)或者 spl_autoload_register(’spl_autoload’),那么autoload_func 指针就会指向内部函数 spl_autoload()。程序执行的时候如果找不到相应类就会执行该自动加载函数。

那么,SPL 是怎么实现autoload_func 指针指向不同的函数呢?

原来,在SPL内部定义了 一个函数 spl_autoload_call() 和 一个全局变量autoload_functions。autoload_functions本质上是一个HashTable,不过我们可以将其简单的看作一个链表,链表中的每一个元素都是一个函数指针,指向一个具有自动加载类功能的函数。

spl_autoload_call()的作用就是按顺序遍历 autoload_functions,使得autoload_func指向每个自动加载函数,如果加载成功就停止,如果不成功就继续遍历下个自动加载函数,直到加载成功或者遍历完所有的函数。

那么,autoload_functions 这个列表是谁来维护的呢?就是 spl_autoload_register() 这个函数。我们说的自动加载函数的注册,其实就是通过spl_autoload_register()把自动加载函数加入到 autoload_functions 列表。

到此为止,整个自动加载的流程就是分析结束了。

  相关SPL自动加载函数:
spl_autoload_functions() //打印autoload_functions列表
spl_autoload_unregister() //注销自动加载函数
  • 1
  • 2
  • 3
  • 4

参考资源: 
http://www.jb51.net/article/31279.htm

PHP-自动加载原理分析的更多相关文章

  1. 深入解析 composer 的自动加载原理 (转)

    深入解析 composer 的自动加载原理 转自:https://segmentfault.com/a/1190000014948542 前言 PHP 自5.3的版本之后,已经重焕新生,命名空间.性状 ...

  2. composer 实现自动加载原理

    简介 一般在框架中都会用到composer工具,用它来管理依赖.其中composer有类的自动加载机制,可以加载composer下载的库中的所有的类文件.那么composer的自动加载机制是怎么实现的 ...

  3. 深入解析 composer 的自动加载原理

    PHP 自5.3的版本之后,已经重焕新生,命名空间.性状(trait).闭包.接口.PSR 规范.以及 composer 的出现已经让 PHP 变成了一门现代化的脚本语言.PHP 的生态系统也一直在演 ...

  4. php自动加载类文件探讨,spl_autoload_register自动加载原理

    spl_autoload_register函数是实现自动加载未定义类功能的的重要方法,所谓的自动加载意思就是 我们的new 一个类的时候必须先include或者require的类文件,如果没有incl ...

  5. Firmware 加载原理分析【转】

    转自:http://blog.csdn.net/dxdxsmy/article/details/8669840 [-] 原理分析 实现机制 总结   前言 前段时间移植 wifi 驱动到 Androi ...

  6. composer 自动加载原理

    核心当然是php5加入来的_autoload函数,当实例化一个不存在的类时,在报错之前,如果定义了_autoload函数,会进行调用此函数,此函数就可以执行相关的include操作. <?php ...

  7. log4j自动加载原理

    java虚拟机加载log4j的类(LogManager.class)后,执行静态代码块,这个类中的静态代码块,会load log4j的配置文件,依次加载log4j.xml,log4j.properti ...

  8. laravel框架源码分析(一)自动加载

    一.前言 使用php已有好几年,laravel的使用也是有好长时间,但是一直对于框架源码的理解不深,原因很多,归根到底还是php基础不扎实,所以源码看起来也比较吃力.最近有时间,所以开启第5.6遍的框 ...

  9. 自动加载以及Composer的实现

    类的自动加载 两个函数 __autoload()魔术方法.spl_autoload_register 当php文件中使用了new关键字实例化一个对象时,如果该类没有在本php文件中被定义,将会触发__ ...

随机推荐

  1. 使用Docker搭建Tomcat运行环境

    1 准备宿主系统 准备一个 CentOS 7操作系统,具体要求如下: 必须是 64 位操作系统 建议内核在 3.8 以上 通过以下命令查看您的 CentOS 内核: # uname -r 2 安装Do ...

  2. 用ADO操作数据库的方法步骤(ZT)

    http://www.cppblog.com/changshoumeng/articles/113437.html 学习ADO时总结的一些经验 用ADO操作数据库的方法步骤 ADO接口简介 ADO库包 ...

  3. tar 打包当前目录下文件但不包括该录

    今天想打包一些文件,但是不想把该目录打包进去 比如我想把test目录下文件打个包,安装正常的命令来 tar zcf test.tar.gz test 这样肯定会把test目录也打进去,解压后肯定是te ...

  4. LeetCode OJ 80. Remove Duplicates from Sorted Array II

    题目 Follow up for "Remove Duplicates": What if duplicates are allowed at most twice? For ex ...

  5. genymotion使用学习

    1 安装 直接去其官网(https://www.genymotion.com/#!/download)下载安装包安装即可,安装中会附带安装VirtualBox. 2 注册 必须使用帐号登录后,方可下载 ...

  6. JAVAWEB 一一 SpringMVC(注解)

    web.xml <?xml version="1.0" encoding="UTF-8"?> <web-app version="2 ...

  7. ORA-01704: 字符串文字太长 oracle

    .net连接oracle时,当流从文件读出来更新数据库时,会报"ORA-01704: 字符串文字太长"这样的错误. 原因:sql语句拼接而成. 解决:采用参数方法传值. strin ...

  8. asp.net 如何判断输入的值 包括 汉字?

    string input = " 里面是不是汉字 ";bool bl= System.Text.RegularExpressions.Regex.IsMatch(input, @& ...

  9. tensorflow中 tf.train.slice_input_producer 和 tf.train.batch 函数(转)

    tensorflow数据读取机制 tensorflow中为了充分利用GPU,减少GPU等待数据的空闲时间,使用了两个线程分别执行数据读入和数据计算. 具体来说就是使用一个线程源源不断的将硬盘中的图片数 ...

  10. hdu2421-Deciphering Password-(欧拉筛+唯一分解定理+积性函数+立方求和公式)

    Deciphering Password Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Oth ...