A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

PHP自动加载的背景  在开发过程中,我们如果想引入外部的class,通常都会使用require或者include方法,其实这个在小规模的开发中没多大问题,但是万一开发规模大了起来,那样子就会有很多require/include语句,这样子非常不优雅,同时如果遗漏引入,或者重复引入,那也是一个麻烦事,require_once的代价很大,暂且不提。
PHP自动加载函数程序员应该是懒惰的,我们不愿意这么做,所以有了更伟大的实践-自动加载,什么是自动加载,粗俗说,我需要你,我再引入你,而不是一开始就require/include进来,那怎么实现自动加载呢?就是用__autoload这个函数啦,下面是一个例子:


  • function __autoload($classname) {



  •     $filename = "./". $classname .".php";



  •     include_once($filename);



  • }



也就是当我们尝试去使用一个不存在此文件中的class时,就会调用这个函数,拼接filename并且用include_once引入进来,是不是觉得很舒服,解放我们双手,---但是官网文档已经明确说明这个特性在PHP7.2之后被弃用并且不鼓励使用了,为什么要弃用呢?--原因在这儿:
__autoload是全局函数所以只能定义一次?所以这个理由和上面弃用有什么相关联呢?
先再看看我们上面给的加载例子,再拼接文件名那块,对的,问题之一,我们在实际开发时候,类名和实际磁盘文件映射规则可能不相同,那我们是不是要再一个__autoload函数里面写完所有的映射规则,这样子映射规则多了,可能你的一个__autoload函数就十分庞大不美观了

弃用它代表更优秀的会顶替它,没错,就是SPL Autoload了,SPL全称Standard PHP Lobrary,是PHP5引入的一个扩展库,在这里我们就说spl_autoload_register这个注册函数吧,它接手了__autoload函数的工作并且完成的更加出色。详细文档还是建议参考官方文档,请戳这里,主要职责就是将自己写的自动加载函数注册到SPL __autoload函数队列中,当我们找不到class时候就会去调用这个队列,一个一个调用自动加载函数直到找到class为止


  • function my_autoloader($class) {



  •     include 'classes/' . $class . '.class.php';



  • }







  • spl_autoload_register('my_autoloader');



接下来要进入我们的正题了,在这里我假设你是明白PHP的命名空间了,如果不清楚还是请戳官方文档
PSRs规范大家应该了解PSRs的规范吧,这里简单说一下,这伟大的PSR规范是PHP-FIG组织撰写的,FIG 是 Framework Interoperability Group(框架可互用性小组)的缩写,由几位开源框架的开发者成立于 2009 年,虽然不是 “官方” 组织,但也代表了PHP社区中一个类似官方的力量。FIG的官方网址请戳这里,他为千千万万的PHP开发者规范了编码规范和这里要说的自动加载规范,等,大大降低我们因为代码风格不同而焦头烂额风险!

PSR-0SR0目前已经被PSR4替代,它被标记为弃用,这两个都是关于自动加载的标准
PSR0强制性要求以下几点:
1、一个完全限定类名的namespace和class必须符合这样的结构:“\<Vendor Name><Namespace><Namespace>*<Class Name>”;
2、每个命名空间必须有一个顶层的命名空间;
3、每个命名空间可以有多个子命名空间;
4、当从文件系统中加载时,每个namespace的分隔符要转换成操作系统路径分隔符;
5、在类名中,每个下划线(_)符号要转换成操作系统路径分隔符,在namespace中,下划线(_)符号是没有(特殊)意义的。
6、当从文件系统中载入时,合格的namespace和class一定是以 .php 结尾的
7、verdor name,namespaces,class名可以由大小写字母组合而成(大小写敏感的)
看不懂吗?咱们以laravel源码举例子

假设有\Symfony\Core\Request命名空间
规定为什么需要有顶级命名空间可以查看composer的自动加载源码,说明白其实是PSR0标准只负责顶级命名空间之后的映射关系,而顶层命名空间/Symfony应该关联到哪个目录,那就是用户或者框架自己定义的了;


又假设我们的命名空间是\Symfony\Core\Request_A,就会映射到我们就应该把它映射到\Symfony\Core\Request\a这样的目录,这是为了兼容PHP5之前没有命名空间的情况


  •   public static $prefixesPsr0 = array (



  •         'P' =>



  •         array (



  •             'Prophecy\\' =>



  •             array (



  •                 0 => __DIR__ . '/..' . '/phpspec/prophecy/src',



  •             ),



  •             'Parsedown' =>



  •             array (



  •                 0 => __DIR__ . '/..' . '/erusev/parsedown',



  •             ),



  •         ),







以上是composer自动加载PSR-0的一小片段代码,
i、我们假设有一个命名空间"\parsedown\a\b"他先截取命名空间第一个字母去遍历比较给出的数组,
ii、找到以p开头的两个顶层命名空间与\parsedown\a\b得到parsedown这个顶层命名空间,
iii、于是在映射数组得出映射目录
__DIR__ . '/..' . '/erusev/parsedown',
iv、查看
__DIR__ . '/..' . '/erusev/parsedown/parsedown/a/b.php是否存在

以上便是composer兼容PSR-0的规范大概,具体可以查看源码

PSR-4PSR4的规范

1、术语「类」是一个泛称;它包含类,接口,traits 以及其他类似的结构;
2、完全限定类名应该类似如下范例:
\<NamespaceName>(\<SubNamespaceNames>)*\<ClassName>
i、完全限定类名必须有一个顶级命名空间(Vendor Name);
ii、完全限定类名可以有多个子命名空间;
iii、完全限定类名应该有一个终止类名;
iv、下划线在完全限定类名中是没有特殊含义的;
v、字母在完全限定类名中可以是任何大小写的组合;
vi、所有类名必须以大小写敏感的方式引用;

3、当从完全限定类名载入文件时:
i、在完全限定类名中,连续的一个或几个子命名空间构成的命名空间前缀(不包括顶级命名空间的分隔符),至少对应着至少一个基础目录。
ii、在「命名空间前缀」后的连续子命名空间名称对应一个「基础目录」下的子目录,其中的命名 空间分隔符表示目录分隔符。子目录名称必须和子命名空间名大小写匹配;
iii、终止类名对应一个以 .php 结尾的文件。文件名必须和终止类名大小写匹配;

4、自动载入器的实现不可抛出任何异常,不可引发任何等级的错误;也不应返回值;
先说一下他和PSR-0的区别,这里离可以看出在类名的_是没有任何其他意义,同时PSR-4的规范比较干净,目录不会那么深,这里比较难以理解,我们可以看以下composer自动加载PSR-4的片段代码


  • public static $prefixLengthsPsr4 = array (



  •         'p' =>



  •         array (



  •             'phpDocumentor\\Reflection\\' => 25,



  •         ),



  • );







  • public static $prefixDirsPsr4 = array (



  •         'phpDocumentor\\Reflection\\' =>



  •         array (



  •             0 => __DIR__ . '/..' . '/phpdocumentor/reflection-common/src',



  •             1 => __DIR__ . '/..' . '/phpdocumentor/reflection-docblock/src',



  •             2 => __DIR__ . '/..' . '/phpdocumentor/type-resolver/src',



  •         ),



  • );







PSR-4比PSR-0多了一个数组,一开始是一样的:

i、我们假设有一个命名空间"\phpDocumentor\Reflection\b"他先截取命名空间第一个字母去遍历比较给出的数组,
ii、找到以p开头的顶层命名空间\phpDocumentor\Reflection\,
iii、返回的数字25代表的是这个顶层命名空间长度为25,
iv、接着再去遍历第二个数组得到三个目录映射
v、一一对比,这里的25派上用场了,比如__DIR__  /../phpDocumentor/reflection-common/src/  +  substr('phpDocumentor/Reflection/b.php',25)

说到这里大致意思都清楚了,如果哪位仁兄对以上有不同看法,恳切指出,共同进步!^_^
---------------------本文来自 lanco刘 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/m0_3775208 ... 846?utm_source=copy

1 个回复

倒序浏览
奈斯
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马