spl_autoload_register的执行原理是如何通过注册一个自动加载函数来动态加载类文件的?

2026-04-02 03:151阅读0评论SEO基础
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计1851个文字,预计阅读时间需要8分钟。

spl_autoload_register的执行原理是如何通过注册一个自动加载函数来动态加载类文件的?

应用场景+在PHP中,当我们想要使用其他文件时,需要先引入文件,这时候我们会使用include或require引入。当我们的项目只有几个文件时,使用这种手动引入方式就很方便,但现在是。

应用场景

在PHP中,当我们想要使用其它文件时,需要先引入文件,这时候我们会使用include或者require引入。当我们的项目只有几个文件时,使用这种方式手动引入,就很nice,但是现在的项目都是使用框架,动辄几百上千个文件,如果还是使用这种方法引入的话,就会出现以下问题:

  • 文件刚打开,发现头部好多include,很不美观
  • 有时候不确定某个文件是否引入了,还得先调试代码,发现报错了,再手动引入,这种开发模式就很不友好

所以有没有解决这种问题的方法,可以自动引入呢?答案当然是有了,PHP在SPL标准库中给我们内置了今天的主角,也就是spl_autoload_register函数。

怎么用
定义:将给定函数注册为 __autoload() 实现,即自动加载函数

function spl_autoload_register(?callable $callback, bool $throw = true, bool $prepend = false): bool {}

该函数接受三个参数,

  1. 闭包函数的名字,也可以是类的方法,也可以是闭包
  2. 是否应在 无法注册时抛出异常 默认为true,从 PHP 8.0.0 开始,该参数被忽略,spl_autoload_register()现在总是会在无效参数上 抛出TypeError 。
  3. 是否将注册函数放到队列的头部,默认false,追加到队列的尾部,设置为true,追加到队列头部
详解

为了更好的理解该函数,我们现在创建一个测试demo

A.php

<?php class A{ public static function test() { echo 'a'; } }

B.php

<?php class B{ public static function test() { echo 'b'; } }

index.php

<?php include './a.php'; A::test();

访问index.php 就会输出a,这是我们正常引入文件,使用上面的方式,但是我们今天的主角是spl_autoload_register函数,所以我们修改一下index.php

<?php function loaderA(){ var_dump(1111); include './A.php'; } spl_autoload_register('loaderA',true,true); A::test();

此时访问index.php,会输出如下

int(1111) a

现在我们解读一下上面的代码,在第6行代码我们将loaderA函数注册为一个自动加载函数,当运行到第7行代码的时候,发现找不到类A,但是此时不会直接报错,系统监测到我们已经提前注册了spl_autoload_register函数,所以,此时代码走到第2行,调用loaderA函数,就输出了如上信息

现在我们再次修改一下index.php的代码

<?php function loaderA(){ var_dump(1111); include './A.php'; } function loaderB(){ var_dump(2222); include './B.php'; } spl_autoload_register('loaderA',true,true); spl_autoload_register('loaderB',true,true); B::test(); A::test();

访问index.php,会输出如下,提示我们重复引入了B.php文件,接下来我们好好分析一下为什么发生如下报错信息

int(2222) bint(2222) PHP Fatal error: Cannot declare class B, because the name is already in use in E:\phpstudy\phpstudy_pro\WWW\localhost\spl\B.php on line 4 Fatal error: Cannot declare class B, because the name is already in use in E:\phpstudy\phpstudy_pro\WWW\localhost\spl\B.php on line 4

在分析这个报错信息之前,我们需要先了解一个函数

spl_autoload_functions //返回所有已注册的 __autoload() 函数

看以下代码

<?php function loaderA(){ var_dump(1111); include './A.php'; } function loaderB(){ var_dump(2222); include './B.php'; } spl_autoload_register('loaderA',true,true); spl_autoload_register('loaderB',true,true); $registers = spl_autoload_functions(); var_dump($registers);

执行以上代码输出以下信息,spl_autoload_register函数的最后一个参数=true,所以后注册的追加到队列最前面

array(2) { [0]=> string(7) "loaderB" [1]=> string(7) "loaderA" }

有了以上了解,我们就可以好好的分析上面的报错信息了,我们在上面注册了两个spl_autoload_register函数,所以当代码执行到第13行的时候,找不到B文件,就会按照队列array('loaderB','loaderA')先去加载loaderB函数,执行第7,8行代码,所以打印出了int(2222)下面echo出了b,代码继续往下走,执行到14行的时候,发现找不到A文件,就会按照队列array('loaderB','loaderA')重新去加载loaderB函数,此时代码重新走到第7行,打印出了int(2222),但是再往下执行第8行代码的时候,发现B文件已经被引入了,重复引入就报了上面的错误。

我们修改一下index.php代码

<?php function loaderA(){ var_dump(1111); include './A.php'; } function loaderB(){ var_dump(2222); include './B.php'; } spl_autoload_register('loaderA',true,true); spl_autoload_register('loaderB',true,true); A::test(); B::test();

访问index.php,输出如下信息

int(2222) int(1111) ab

我们来分析一下,代码执行到13行,按照队列array('loaderB','loaderA')会调用loaderB函数,执行第7行代码会输出int(2222) ,执行第8行代码,将B.php引入进来,此时发现A文件还是没有找到,此时按照队列array('loaderB','loaderA'),会往下调用loaderA函数,执行第3行代码,输出int(1111) ,继续执行第4行代码,此时会echo 出a,代码走到第14行,此时发现B.php已经被引入进来了,所以执行echo输出了b。

重新修改一下index.php代码,加了include_once,修改了A跟B的先后调用顺序

<?php function loaderA(){ var_dump(1111); include_once './A.php'; } function loaderB(){ var_dump(2222); include_once './B.php'; } spl_autoload_register('loaderA',true,true); spl_autoload_register('loaderB',true,true); B::test(); A::test();

访问index.php,输入如下信息

spl_autoload_register的执行原理是如何通过注册一个自动加载函数来动态加载类文件的?

int(2222) bint(2222) int(1111) a

我们来分析一下,代码执行到13行,首先访问loaderB函数,输出int(2222) ,echo出了b,代码执行到14行,发现A.php没加载进来,按照队列从头开始执行,首先访问loaderB函数,输出int(2222) ,因为使用了include_once,所以B.php不会被重复加载,继续按照队列往后走,访问loaderA函数,输出int(1111) echo出了a

其实loaderA 跟loaderB函数是可以接受参数的,我们修改一下index.php文件

<?php function loader($file){ var_dump($file); include './'.$file.'.php'; } spl_autoload_register('loader',true,true); B::test(); A::test();

访问index.php会输出如下

string(1) "B" bstring(1) "A" a

此时队列中数据为array('loader');$file参数为要加载的没有找到的类名。分析一下代码执行流程:代码执行到第7行,调用loader函数,此时file= 'B',加载了B.php,代码继续往下执行,走到第8行,发现A.php也没加载进来,此时又重新调用loader函数,此时file= 'A',加载了A.php

总结

spl_autoload_register 是按照注册队列顺序执行的,如果前面的加载没有找到文件,就顺序执行队列后面的,直到执行到最后一个,有则加载,无则报错。

我们平时在写代码的时候可能会用不到该函数,但是基本主流框架中都会使用到该函数,如Tp6,Laravel,Yii2等。因为框架的执行第一步基本都是使用spl_autoload_register函数处理文件加载,了解该函数有助于我们了解composer包的执行原理。如下,在Tp6中引入了composer包,在包中使用了该函数处理了文件加载。

标签:执行原理

本文共计1851个文字,预计阅读时间需要8分钟。

spl_autoload_register的执行原理是如何通过注册一个自动加载函数来动态加载类文件的?

应用场景+在PHP中,当我们想要使用其他文件时,需要先引入文件,这时候我们会使用include或require引入。当我们的项目只有几个文件时,使用这种手动引入方式就很方便,但现在是。

应用场景

在PHP中,当我们想要使用其它文件时,需要先引入文件,这时候我们会使用include或者require引入。当我们的项目只有几个文件时,使用这种方式手动引入,就很nice,但是现在的项目都是使用框架,动辄几百上千个文件,如果还是使用这种方法引入的话,就会出现以下问题:

  • 文件刚打开,发现头部好多include,很不美观
  • 有时候不确定某个文件是否引入了,还得先调试代码,发现报错了,再手动引入,这种开发模式就很不友好

所以有没有解决这种问题的方法,可以自动引入呢?答案当然是有了,PHP在SPL标准库中给我们内置了今天的主角,也就是spl_autoload_register函数。

怎么用
定义:将给定函数注册为 __autoload() 实现,即自动加载函数

function spl_autoload_register(?callable $callback, bool $throw = true, bool $prepend = false): bool {}

该函数接受三个参数,

  1. 闭包函数的名字,也可以是类的方法,也可以是闭包
  2. 是否应在 无法注册时抛出异常 默认为true,从 PHP 8.0.0 开始,该参数被忽略,spl_autoload_register()现在总是会在无效参数上 抛出TypeError 。
  3. 是否将注册函数放到队列的头部,默认false,追加到队列的尾部,设置为true,追加到队列头部
详解

为了更好的理解该函数,我们现在创建一个测试demo

A.php

<?php class A{ public static function test() { echo 'a'; } }

B.php

<?php class B{ public static function test() { echo 'b'; } }

index.php

<?php include './a.php'; A::test();

访问index.php 就会输出a,这是我们正常引入文件,使用上面的方式,但是我们今天的主角是spl_autoload_register函数,所以我们修改一下index.php

<?php function loaderA(){ var_dump(1111); include './A.php'; } spl_autoload_register('loaderA',true,true); A::test();

此时访问index.php,会输出如下

int(1111) a

现在我们解读一下上面的代码,在第6行代码我们将loaderA函数注册为一个自动加载函数,当运行到第7行代码的时候,发现找不到类A,但是此时不会直接报错,系统监测到我们已经提前注册了spl_autoload_register函数,所以,此时代码走到第2行,调用loaderA函数,就输出了如上信息

现在我们再次修改一下index.php的代码

<?php function loaderA(){ var_dump(1111); include './A.php'; } function loaderB(){ var_dump(2222); include './B.php'; } spl_autoload_register('loaderA',true,true); spl_autoload_register('loaderB',true,true); B::test(); A::test();

访问index.php,会输出如下,提示我们重复引入了B.php文件,接下来我们好好分析一下为什么发生如下报错信息

int(2222) bint(2222) PHP Fatal error: Cannot declare class B, because the name is already in use in E:\phpstudy\phpstudy_pro\WWW\localhost\spl\B.php on line 4 Fatal error: Cannot declare class B, because the name is already in use in E:\phpstudy\phpstudy_pro\WWW\localhost\spl\B.php on line 4

在分析这个报错信息之前,我们需要先了解一个函数

spl_autoload_functions //返回所有已注册的 __autoload() 函数

看以下代码

<?php function loaderA(){ var_dump(1111); include './A.php'; } function loaderB(){ var_dump(2222); include './B.php'; } spl_autoload_register('loaderA',true,true); spl_autoload_register('loaderB',true,true); $registers = spl_autoload_functions(); var_dump($registers);

执行以上代码输出以下信息,spl_autoload_register函数的最后一个参数=true,所以后注册的追加到队列最前面

array(2) { [0]=> string(7) "loaderB" [1]=> string(7) "loaderA" }

有了以上了解,我们就可以好好的分析上面的报错信息了,我们在上面注册了两个spl_autoload_register函数,所以当代码执行到第13行的时候,找不到B文件,就会按照队列array('loaderB','loaderA')先去加载loaderB函数,执行第7,8行代码,所以打印出了int(2222)下面echo出了b,代码继续往下走,执行到14行的时候,发现找不到A文件,就会按照队列array('loaderB','loaderA')重新去加载loaderB函数,此时代码重新走到第7行,打印出了int(2222),但是再往下执行第8行代码的时候,发现B文件已经被引入了,重复引入就报了上面的错误。

我们修改一下index.php代码

<?php function loaderA(){ var_dump(1111); include './A.php'; } function loaderB(){ var_dump(2222); include './B.php'; } spl_autoload_register('loaderA',true,true); spl_autoload_register('loaderB',true,true); A::test(); B::test();

访问index.php,输出如下信息

int(2222) int(1111) ab

我们来分析一下,代码执行到13行,按照队列array('loaderB','loaderA')会调用loaderB函数,执行第7行代码会输出int(2222) ,执行第8行代码,将B.php引入进来,此时发现A文件还是没有找到,此时按照队列array('loaderB','loaderA'),会往下调用loaderA函数,执行第3行代码,输出int(1111) ,继续执行第4行代码,此时会echo 出a,代码走到第14行,此时发现B.php已经被引入进来了,所以执行echo输出了b。

重新修改一下index.php代码,加了include_once,修改了A跟B的先后调用顺序

<?php function loaderA(){ var_dump(1111); include_once './A.php'; } function loaderB(){ var_dump(2222); include_once './B.php'; } spl_autoload_register('loaderA',true,true); spl_autoload_register('loaderB',true,true); B::test(); A::test();

访问index.php,输入如下信息

spl_autoload_register的执行原理是如何通过注册一个自动加载函数来动态加载类文件的?

int(2222) bint(2222) int(1111) a

我们来分析一下,代码执行到13行,首先访问loaderB函数,输出int(2222) ,echo出了b,代码执行到14行,发现A.php没加载进来,按照队列从头开始执行,首先访问loaderB函数,输出int(2222) ,因为使用了include_once,所以B.php不会被重复加载,继续按照队列往后走,访问loaderA函数,输出int(1111) echo出了a

其实loaderA 跟loaderB函数是可以接受参数的,我们修改一下index.php文件

<?php function loader($file){ var_dump($file); include './'.$file.'.php'; } spl_autoload_register('loader',true,true); B::test(); A::test();

访问index.php会输出如下

string(1) "B" bstring(1) "A" a

此时队列中数据为array('loader');$file参数为要加载的没有找到的类名。分析一下代码执行流程:代码执行到第7行,调用loader函数,此时file= 'B',加载了B.php,代码继续往下执行,走到第8行,发现A.php也没加载进来,此时又重新调用loader函数,此时file= 'A',加载了A.php

总结

spl_autoload_register 是按照注册队列顺序执行的,如果前面的加载没有找到文件,就顺序执行队列后面的,直到执行到最后一个,有则加载,无则报错。

我们平时在写代码的时候可能会用不到该函数,但是基本主流框架中都会使用到该函数,如Tp6,Laravel,Yii2等。因为框架的执行第一步基本都是使用spl_autoload_register函数处理文件加载,了解该函数有助于我们了解composer包的执行原理。如下,在Tp6中引入了composer包,在包中使用了该函数处理了文件加载。

标签:执行原理