当php面向对象开发过程中,随着项目越来越大,需要通过include或require引入的文件越来越多,每个php脚本开头都要引入一大堆文件引入,不便于项目的管理,当有类更新时,也容易导致项目出现报错。所以就需要类的自动载入功能。php通过__autoload
和spl_autoload_register
函数来实现。__autoload函数在PHP 7.2中已经废弃,不推荐使用。
一、__autoload
当一个类实例化时会把类名以参数方式传给_autoload函数,spl_autoload_register也一样。然后通过目录+类名的方式引入文件。
不使用自动加载时引入文件:
require 'a.php'; // class name a require 'b.php'; // class name b require 'c.php'; // class name c
使用自动加载时引入文件,假设目录为/test:
function __autoload($class){ require_once '/test/' . $class . '.php'; } $a = new a(); // 类名a即__autoload参数名 $class = a $a = new b(); // $class = b $a = new c(); // $class = c
二、spl_autoload_register
当项目有多个模块时,__autoload不能满足需求,__autoload只能定义一次。如果需要多次使用autoload 函数时,spl_autoload_register() 满足了此类需求。 它实际上创建了 autoload 函数的队列,按定义时的顺序逐个执行。spl_autoload_register将函数注册到SPL __autoload函数队列中。如果该队列中的函数尚未激活,则激活它们。
1.加载一个模块,匿名函数方式
spl_autoload_register(function ($class) { require __DIR__ . '/class/' . $class . '.class.php'; });
2.加载多模块时,注册多个自定义自动装载函数
spl_autoload_register('homeAutoloader'); spl_autoload_register('adminAutoloader'); function homeAutoloader($class) { require_once __DIR__ . '/Home/class/' . $class . '.class.php'; } function adminAutoloader($class) { require_once __DIR__ . '/Admin/class/' . $class . '.class.php'; }
这时只要实例化后的类都会在一个队列中,如:
require_once __DIR__ . '/Home/class/index.class.php'; require_once __DIR__ . '/Home/class/edit.class.php'; require_once __DIR__ . '/Admin/class/index.class.php'; require_once __DIR__ . '/Admin/class/index.class.php';
上面的方式虽然实现了类的自动加载,但当出现同样的类名,就会报错。所以需要引入一些规范和命名空间来解决。
3.类自动加载PSR规范
PSR 是 PHP Standard Recommendations 的简写,由 PHP FIG 组织制定的 PHP 规范,是 PHP 开发的实践标准。现在的标准是PSR-4规范,在此之前还有PSR-1、PSR-2、PSR-3规范,PSR-1规范已弃用。PSR-4规范是在PSR-1、PSR-2规范中更新和扩展而来。
PSR-1规范要求(已弃用)
*命名空间结构必须时由 \vendor Name\Namespace\Class Name 结构组成,即:\app\controller\Index,Vendor Name 为每个命名空间都必须要有的一个顶级命名空间名,vendor Name、Namespace、Class Name可由任意大小写字母组成;
*命名空间结构必须是路径的一部分;
*类名称中的每个 _ 字符也会被转换成文件夹路径分隔符;即:\app\controller\Index_Index 。index目录中的index.php类,用下划线间隔;
*类文件名必须是 .php 后缀;
PSR-2规范要求:
*代码 必须 遵循PSR-1中的编码规范;
*代码 必须 使用 4 个空格符进行缩进;
*每行的字符数应该软性保持在 80 个之内;
*namespace命名空间声明语句和 use 声明语句块后面必须 插入一个空白行;
*类和类中的方法开始花括号{和结束花括号}必须独自占有用一行;
*类的属性和方法 必须 添加访问修饰符(private、protected 以及 public),abstract 以及 final 必须 声明在访问修饰符之前,而 static 必须 声明在访问修饰符之后;
*控制结构的关键字后 必须 要有一个空格符,而调用方法或函数时不用空格;
*控制结构的开始花括号{必须 写在声明的同一行,而结束花括号}必须 写在主体后自成一行;
*控制结构的开始左括号后和结束右括号前不能有空格符;
PSR-3规范要求:
psr-3规范制定了日志类库的通用接口规范。和这里自动加载关系不大,这里不作介绍。
PSR-4规范要求:
*「class」指的是类(classes)、接口(interfaces)、特征(traits)和其他类似的结构。
*全限定类名具有以下形式:\NamespaceName\SubNamespaceNames\ClassName;
*全限定类名必须拥有一个顶级命名空间名称,也称为供应商命名空间(vendor namespace);
*全限定类名可以有一个或者多个子命名空间名称;
*全限定类名必须有一个最终的类名,不能是这样的形势 \NamespaceName\SubNamespaceNames*\;
*去除了psr-1中的下划线来间隔文件夹和文件名,下划线在psr-4中无意义;
*全限定类名可以是任意大小写字母的组合;
*所有类名的引用必须区分大小写;
*全限定类名的加载过程:
*在全限定的类名(一个「命名空间前缀」)中,一个或多个前导命名空间和子命名空间组成的连续命名空间,不包括前导命名空间的分隔符,至少对应一个「根目录」;
*「命名空间前缀」后面的相邻子命名空间与根目录下的目录名称相对应(且必须区分大小写),其中命名空间的分隔符表示目录分隔符;
*最终的类名与以.php 结尾的文件名保持一致,这个文件的名字必须和最终的类名相匹配(意思就是如果类名是 FooController,那么这个类所在的文件名必须是 FooController.php);
*自动加载文件禁止抛出异常,禁止出现任何级别的错误,也不建议有返回值;
psr全规范参考:https://learnku.com/docs/psr
4.实现一个简单的mvc类的自动加载
目录结构:
-----------------------------------------
app // 应用目录
controller // 控制器目录
index
Index.php // Index控制器
test
Test.php // Test模型
model // 模型目录
index
Index.php // index模型
view // 视图目录
loader.php // 自动加载类
index.php // 入口文件
-----------------------------------------
执行过程:在入口index.php文件中引入自动加载类文件并调用。每次调用类方法时,通过spl_autoload_register函数穿类名路径的方式,处理后引用类文件。
详细代码:
入口Index.php
<?php define('BASE_DIR', __DIR__); // 引入自动加载 require __DIR__ . '/' . 'loader.php'; Loader::autoLoad(); // 调用Index控制器中index方法 \app\controller\index\Index::index(); // 调用Test控制器中test方法 \app\controller\test\Test::test();
自动加载类loader.php
<?php /** * 类的自动加载 * Class Loader */ class Loader { public static function autoLoad() { // 注册类 spl_autoload_register(function ($class) { $class = str_replace('\\', DIRECTORY_SEPARATOR, $class); $filePath = BASE_DIR . DIRECTORY_SEPARATOR . $class . '.php'; if (file_exists($filePath)) { include $filePath; } else { echo 'Class Not Found !'; } }); } }
控制器Index.php
<?php namespace app\controller\index; class Index { public static function index() { echo 'Index.php: Index Controller' . "<br/>"; } }
控制器Test.php
<?php namespace app\controller\test; use \app\model\index\Index as IndexModel; class Test { public static function test() { echo 'Test.php: Test Controller' . ' '; // 控制器调用模型 IndexModel::index(); } }
模型Index.php
<?php namespace app\model\index; class Index { public static function index() { echo 'Call Index Model ' . "<br/>"; } }