大家在阅读文档 或者使用一些第三方的框架或者软件的时候,都听过或者看过里面要求说实现了psr0或者psr4的规范。
我也一直在查资料,找痕迹。现在我的理解是,其实这2个规范就是对类的装载,实现自动寻路径。
首先我们看下 PSR0
我写代码实现了它的自动加载器
这是加载器代码
<?php
/**
* Created by PhpStorm.
* User: Administrator
* Date: 2018/4/14
* Time: 22:27
*/
class Psr0{
/* psr-0 规范说明
* 1.一个完整的标准的类文件格式是这样的 \vendor\namespace\class
* 2.每个命名空间必须有一个顶级命名空间 子命名空间可以有多个 或者没有
* 3.加载文件的时候 命名空间分隔符会被转换为 DIRECTORY_SEPARATOR
* 4.类名如果带'_',都会转换为DIRECTORY_SEPARATOR,_的拼接部分必须是类目录的子目录部分并且是一一对应
* 打个比方 Tik_Tb_Order.php 其中 Tik和Tb必须是该类文件所在的子目录。
* 5.加载的文件后缀必须以.php结尾
* 6.verdor namespace class必须由大小写字母组合而成
*/
public static function autoload($className)
{
//去掉最左边的\
$className = ltrim($className,'\\');
//获取命名空间
$position = strrpos($className,'\\');
$strnamespaces = substr($className,0,$position);
//获取类名
$class = substr($className,$position+1);
$namespaces = explode('\\',$strnamespaces);
//组装类路径。。psr0规定 命名空间分隔符要被DIRECTORY_SEPARATOR替换。
$file_path = '';
foreach ($namespaces as $namespace)
{
$file_path .= $namespace.DIRECTORY_SEPARATOR;
}
//类名下划线处理
$class_file = str_replace('_',DIRECTORY_SEPARATOR,$class);
//最终的file
$file = './vendor/'.$file_path.$class_file.'.php';
include $file;
}
}
然后我的文件目录
我的调用代码 是 start.php
<?php
/**
* Created by PhpStorm.
* User: Administrator
* Date: 2018/4/14
* Time: 22:22
*/
header("Content-type: text/html; charset=utf-8");
require 'Psr0.php';
//定义类的自动加载器
spl_autoload_register('Psr0::autoload');
//测试
$three = new \Com\Three();
$one = new \Com\One\One();
$two = new \Com\One\Fki_Tb_Two();
$three->sh();
$one->sh();
$two->sh();
这是运行效果
看懂了没 没的话 我带大家分析一波。
1.首先 psr0规定 必须有个组织名 我这里是vendor 然后得有个顶级命名空间 我这里是Com
打开我的three.php
<?php
/**
* Created by PhpStorm.
* User: Administrator
* Date: 2018/4/14
* Time: 22:49
*/
namespace Com;
class Three{
public function sh()
{
echo '我是three'.'<br>';
}
}
我这个类就在当前顶级命名空间下 且没有子命名空间 当然也可以被加载到。
再看one.php
<?php
/**
* Created by PhpStorm.
* User: Administrator
* Date: 2018/4/15
* Time: 10:30
*/
namespace Com\One;
class One{
public function sh()
{
echo '我是one'.'<br>';
}
}
我这里是设置了子命名空间 为One 根据psr0规范。子命名空间必须是类的文件系统加载的子目录。所以也能通过。
我们再看fki_Tb_two.php
<?php
/**
* Created by PhpStorm.
* User: Administrator
* Date: 2018/4/15
* Time: 10:34
*/
namespace Com\One;
class Fki_Tb_Two{
public function sh(){
echo '我是tow'.'<br>';
}
}
这个类名带下划线 psr0规范说 下划线最后的一部分才是类名。其余部分充当该类被加载的子目录部分,并且和子目录名字母大小一一对应 在本例中。根据我上面目录结构图,它是这个类的2个上一级目录名。所以也能被加载。
我们再看psr4
我先贴出加载器代码
<?php
/**
* Created by PhpStorm.
* User: Administrator
* Date: 2018/4/14
* Time: 22:27
*/
//单命名空间
class Psr4_1{
public static function autoload($className)
{
/*
* Psr-4规范说明
* 1.合法的类的完全限定名格式是 \namespacename\subnamespace\class
* 2.必须有一个顶级的命名空间
* 3.必须有一个终止类名
* 4.下划线在类中无特殊意义
* 5.子命名空间必须对于从文件系统载入类文件的一个子目录
* 6.子目录和子命名空间必须大小写和字母一一对应。子命名空间分割符表示子目录分隔符
* 7.
*/
//设置命名空间目录映射
$namespace_prefix='Lib1\\Red1';
$base_dir='./Lib1/Red';
//去掉最左边的\
$className = ltrim($className,'\\');
//获取最右边的分隔符位置 用来做命名空间和class的分界点
$position = strrpos($className,'\\');
//获取命名空间部分
$strnamespaces = substr($className,0,$position);
//判断前缀是否存在 。0表示是合法的,false直接返回错误
$is_exsists = strpos($strnamespaces,$namespace_prefix);
if($is_exsists!==0)
{
return;
}
//获取子命名空间部分
$Len = strlen($namespace_prefix);
$str_sub_namespace = substr($strnamespaces,$Len);
$str_sub_namespace_path = str_replace('\\',DIRECTORY_SEPARATOR,$str_sub_namespace);
//获取类名
$class = substr($className,$position+1);
//拼接目录
$file = $base_dir.$str_sub_namespace_path.DIRECTORY_SEPARATOR.$class.'.php';
include $file;
}
}
在贴出调用代码
<?php
/**
* Created by PhpStorm.
* User: Administrator
* Date: 2018/4/14
* Time: 22:22
*/
header("Content-type: text/html; charset=utf-8");
require 'Psr4_1.php';
//定义类的自动加载器
spl_autoload_register('Psr4_1::autoload');
$red = new \Lib1\Red1\Red();
$green = new \Lib1\Red1\Sub\Green();
$red_green = new \Lib1\Red1\Red_Green();
$red->sh();
$green->sh();
$red_green->sh();
效果图
再是目录结构图
我分析一下吧。
首先 psr4是要求命名空间前缀和base_dir有个设置好的对应关系。
我这个psr4_1是一个单命名空间的加载器。稍后我发一个批量的。先拿这个单的说明
我们打开red.php
<?php
/**
* Created by PhpStorm.
* User: Administrator
* Date: 2018/4/15
* Time: 14:05
*/
namespace Lib1\Red1;
class Red{
public function sh(){
echo '我是red<br>';
}
}
这里 我们的命名空间 符合之前预设置好的映射关系。使用能被加载。
再看一个带子命名空间的例子
green.php
<?php
/**
* Created by PhpStorm.
* User: Administrator
* Date: 2018/4/15
* Time: 15:24
*/
namespace Lib1\Red1\Sub;
class Green{
public function sh(){
echo '我是green<br>';
}
}
这里的子命名空间的意思 就是他不在预设值的命名空间前缀里面 也就是没包含他。
那么要让加载器加载到他 我们必须把这个Sub也就是子命名空间 对应一个子目录。也就是在设置好的那个映射关系basedir后面添加一个子目录 要求字母大小一一对应即可。
详情请看我的目录结构图
psr4规范说明 类名的下划线格式 并没有实际的意义..这个和psr0是区别的。
看red_green.php
<?php
/**
* Created by PhpStorm.
* User: Administrator
* Date: 2018/4/15
* Time: 14:05
*/
namespace Lib1\Red1;
class Red_Green{
public function sh(){
echo '我是red_green<br>';
}
}
把这个当成是一个普通的类文件进行加载
明白这个原理后 我贴出多命名空间支持的psr4加载器 也就是我的psr4_2.php
<?php
/**
* Created by PhpStorm.
* User: Administrator
* Date: 2018/4/14
* Time: 22:27
*/
//支持多命名空间部署
class Psr4_2{
private static $_classMapper = '';
public static function addClassMapper($prefix,$dir)
{
self::$_classMapper[$prefix] = $dir;
}
public static function autoload($className)
{
//去掉最左边的\
$className = ltrim($className,'\\');
//获取最右边的分隔符位置 用来做命名空间和class的分界点
$position = strrpos($className,'\\');
//获取命名空间部分
$strnamespaces = substr($className,0,$position);
//判断前缀是否存在 。0表示是合法的,false直接返回错误
foreach (self::$_classMapper as $mapperkey=> $classmapper)
{
$namespace_prefix = $mapperkey;
$is_exsists = strpos($strnamespaces,$namespace_prefix);
if($is_exsists!==false)
{
//获取子命名空间部分
$Len = strlen($namespace_prefix);
$str_sub_namespace = substr($strnamespaces,$Len);
$str_sub_namespace_path = str_replace('\\',DIRECTORY_SEPARATOR,$str_sub_namespace);
//获取类名
$class = substr($className,$position+1);
//拼接目录
$base_dir = self::$_classMapper[$namespace_prefix];
$file = $base_dir.$str_sub_namespace_path.DIRECTORY_SEPARATOR.$class.'.php';
include $file;
return;
}
}
}
}
我在贴出我的调用代码
<?php
/**
* Created by PhpStorm.
* User: Administrator
* Date: 2018/4/14
* Time: 22:22
*/
header("Content-type: text/html; charset=utf-8");
require 'Psr4_2.php';
spl_autoload_register('Psr4_2::autoload');
//定义类的自动加载器
Psr4_2::addClassMapper('Lib1\Red1','./Lib1/Red');
Psr4_2::addClassMapper('Lib2','./Lib2');
Psr4_2::addClassMapper('Lib3\Hei','./lib3/hei/src');
$red = new \Lib1\Red1\Red();
$red->sh();
$green = new \Lib1\Red1\Sub\Green();
$green->sh();
$red_green = new \Lib1\Red1\Red_Green();
$red_green->sh();
$bule = new \Lib2\Bule();
$bule->sh();
$ming = new \Lib1\Red1\sub\Subb\Ming();
$ming->sh();
$fi_cc = new \Lib2\Fi_CC();
$fi_cc->sh();
$hei = new \Lib3\Hei\Hei();
$hei->sh();
$req = new \Lib3\Hei\Req\Req();
$req->sh();
效果图
抽几个具有代表性的分析一下 给大家看。
看我的req.php
文件代码
<?php
/**
* Created by PhpStorm.
* User: Administrator
* Date: 2018/4/15
* Time: 17:56
*/
namespace Lib3\Hei\Req;
class Req{
public function sh(){
echo '我是req<br>';
}
}
也是存在一个子命名空间的问题 我们只要指定对应的子目录就能实现对他的加载。
总结一下 :根据psr4的自动加载规则 我们可以根据配置好的命名空间前缀。快速定位类的文件位置。还有一点,为了开发的便捷性 我们可以配置多个命名空间前缀 ,使类文件避免深度索引。