PHP 实现自动加载(含有命名空间) spl_autoload_register
ps:
- 看了一些别的博主关于自动加载的机制实现 此处来记录一下
- 原文链接 https://www.cnblogs.com/chihuobao/p/9895202.html…
文件结构树
//index.php
<?php
require './autoload.php';
new \vender\os\Linux();
?>
// Linux.php Window.php 类似
<?php
namespace vender\os;
class Linux
{
function __construct()
{
echo '<h1>' . __CLASS__ . '</h1>';
}
}
?>
spl_autoload_register 函数的功能就是把传入的函数(参数可以为回调函数或函数名称形式)注册到 SPL __autoload 函数队列中,并移除系统默认的 __autoload() 函数。
一旦调用 spl_autoload_register() 函数,当调用未定义类时,系统就会按顺序调用注册到 spl_autoload_register() 函数的所有函数,而不是自动调用 __autoload() 函数。
//autoload.php
<?php
spl_autoload_register(function ($class) {
// 限定类名路径映射
$class_map = array(
// 限定类名 => 文件路径
'vender\\os\\Linux' => './vender/os/Linux.php',
'vender\\os\\Window' => './vender/os/Window.php',
);
// 根据类名确定文件名
$file = $class_map[$class];
// 引入相关文件
if (file_exists($file)) {
include $file;
}
});
?>
这里我们使用了一个数组去保存类名与文件路径的关系,这样当类名传入时,自动加载器就知道该引入哪个文件去加载这个类了。
但是一旦文件多起来的话,映射数组会变得很长,这样的话维护起来会相当麻烦。如果命名能遵守统一的约定,就可以让自动加载器自动解析判断类文件所在的路径。接下来要引用的PSR-4规范 就是一种被广泛采用的约定方式。
PSR-4 规范中必须要有一个顶级命名空间,它的意义在于表示某一个特殊的目录(文件基目录)。子命名空间代表的是类文件相对于文件基目录的这一段路径(相对路径),类名则与文件名保持一致(注意大小写的区别)。
举个例子:在全限定类名 \app\view\news\Index 中,如果 app 代表 C:\Baidu,那么这个类的路径则是 C:\Baidu\view\news\Index.php
我们就以解析 \app\view\news\Index 为例,编写一个简单的 Demo:
$class = 'app\view\news\Index';
/* 顶级命名空间路径映射 */
$vendor_map = array(
'app' => 'C:\Baidu',
);
/* 解析类名为文件路径 */
$vendor = substr($class, 0, strpos($class, '\\')); // 取出顶级命名空间[app]
$vendor_dir = $vendor_map[$vendor]; // 文件基目录[C:\Baidu]
$rel_path = dirname(substr($class, strlen($vendor))); // 相对路径[/view/news]
$file_name = basename($class) . '.php'; // 文件名[Index.php]
/* 输出文件所在路径 */
echo $vendor_dir . $rel_path . DIRECTORY_SEPARATOR . $file_name;
具体实现:
// autoload.php
<?php
/*
* 注册一个自动加载函数,就是本类钟的 autoload 方法
* 第二个参数为true表示注册失败的时候会抛出异常
* 第三个参数为true表示该自动加载函数是加入到队列的头部,自动加载函数是有很多的,可以认为是一个队列,默认false值是往队尾添加
* 加载类的时候,会先从队头的自动加载函数去寻找类和实例化,这里是true,就是作为第一个去执行的自动加载函数。
*/
spl_autoload_register('Loader::autoload',true,true); // 注册自动加载
/**
* 自动加载类
* Class Loader
*/
class Loader{
/**
* @var array 路径映射
*/
public static $vendorMap = array(
'vender' => __DIR__ . DIRECTORY_SEPARATOR . 'vender',
);
/**
* 自动加载器
* @param $class
*/
public static function autoload($class)
{
$file = self::findFile($class);
if (file_exists($file)) {
self::includeFile($file);
}
}
/**
* 解析文件路径
* @param $class
* @return bool|string
*/
private static function findFile($class)
{
$vendor = substr($class, 0, strpos($class, '\\')); // 顶级命名空间
if(array_key_exists($vendor, self::$vendorMap)){
$vendorDir = self::$vendorMap[$vendor]; // 文件基目录
$filePath = substr($class, strlen($vendor)) . '.php'; // 文件相对路径
return strtr($vendorDir . $filePath, '\\', DIRECTORY_SEPARATOR); // 文件标准路径
}
return false;
}
/**
* 引入文件
* @param $file
*/
private static function includeFile($file)
{
if (is_file($file)) {
include $file;
}
}
}
结果:
至此 ,就简单实现了psr4 自动加载
示例中的代码其实就是 ThinkPHP 自动加载器源码的精简版,它是 ThinkPHP 5 能实现惰性加载的关键。