PSR 4 自动加载 - 说明文档
1. 概要
此规范的目的是为可互操作的 PHP 自动加载器指定规则,该规则将名称空间映射到文件系统路径,并可与任何其它的 SPL 注册的自动加载器共存。这是对 PSR 0 的补充而不是替代
很明显就是替代了,因为 PSR-0 被废弃了,不过从内容上来讲,也的确是补充
2. 何必?
PSR 0 的历史
PSR 0 规范中的类命名和自动加载标准使用的是 PHP 5.2 及更低的版本约束条件下的被广泛接受的 Horde / PEAR 惯例。 这种惯例倾向于将所有的 PHP 类和源文件都放在一个主目录中,在类名中使用下划线来表示伪命名空间,如下所示:
/path/to/src/ VendorFoo/ Bar/ Baz.php # VendorFoo_Bar_Baz VendorDib/ Zim/ Gir.php # Vendor_Dib_Zim_Gir
命名空间是 PHP 5.3 引入的,之前的版本只能使用伪命名空间
随着 PHP 5.3 的发布和新增了命名空间特性,PSR 0 诞生了。 为了鼓励更广泛的使用,允许旧的 Horde / PEAR 下划线伪命名空间和新的命名空间同时存在,类名称中仍允许使用下划线来兼容旧的伪命名空间
/path/to/src/ VendorFoo/ Bar/ Baz.php # VendorFoo_Bar_Baz VendorDib/ Zim/ Gir.php # VendorDib_Zim_Gir Irk_Operation/ Impending_Doom/ V1.php V2.php # Irk_Operation\Impending_Doom\V2
PEAR 使用者对这个结构应该非常熟悉,PEAR 安装程序会将源文件从 PEAR 包移动到一个统一的中央目录
紧随 Composer 步伐
在 Composer 中, 软件包源不再被复制到单个全局的目录中,它们在安装的位置被使用,不会被四处移动。 也就是说,不同于 PEAR, PHP 源代码没有 「 单一主目录 ( single main directory ) 」 的概念,相反的,有多个目录,项目中的每个软件包都可以有自己的独立目录
为了满足 PSR 0 的要求,Composer 包看起来会是下面的结构:
vendor/ vendor_name/ package_name/ src/ Vendor_Name/ Package_Name/ ClassName.php # Vendor_Name\Package_Name\ClassName tests/ Vendor_Name/ Package_Name/ ClassNameTest.php # Vendor_Name\Package_Name\ClassNameTest
src
和 tests
目录必须包含组织名和软件包目录名称,这是为了兼容 PSR-0
也许你也发现了,其实很多都发现这种结构比必须的更深更重复。因此我们需要额外的或者替代的 PSR 规范,就为了减少目录的层级,我们期待的目录结构应该是下面这样的
vendor/ vendor_name/ package_name/ src/ ClassName.php # Vendor_Name\Package_Name\ClassName tests/ ClassNameTest.php # Vendor_Name\Package_Name\ClassNameTest
这种目录结构,就要求我们遵循之前提到的 「 面向包的自动加载 ( package-oriented autoloading ) 」 ( 相对于传统的 「 类到文件的直接自动加载 ( direct class-to-file autoloading ) 」来说)
面向包的自动加载
显然,面向包的自动加载会让软件包更加简洁
但是通过对 PSR-0 的扩展或修改来实现面向包的自动加载是很困难的,因为 PSR-0 不允许类名的任何部分之间建立一条互相干涉的路径。 这意味着面向包的自动加载器的实现将比 PSR-0 更复杂
刚开始时,添加了以下几条推荐的规则
-
实现类库 必须 至少使用两个层级的命名空间:组织名称和组织名称下的包名
-
实现类库 必须 允许在组织名称和全限定类名称中使用路径中缀符 (
-
) -
组织名称 可以 映射到任何目录,全限定类名的剩余部分 必须,并且比 必须 将类名映射到
.php
结尾的同名文件
注意: 这意味着类名中的下划线不再作为目录分隔符。虽然有些人认为还应该保留 PSR 0 中下划线的原本作用,但是,下划线的存在本身就是为了兼容 PHP 5.3 之前的版本,因此,这里移除了下划线的特殊意义
3. 正文
3.1 目标
-
保留 PSR 0 规则,实现者必须至少使用两个层级的命名空间:组织名称和组织名称中的包名称
-
允许组织名称 (
vendor-package
) 命名空间和全限定类名中出现路径中缀符 (-
) -
允许组织名称(
vendor-package
) 命名空间 可以 映射到任何目录,还可以是多个目录 -
类名中的下划线不再作为目录分隔符
3.2 非目标
- 为非类资源提供一个通用的转换算法
4. 方案
4.1 选择的方案
此草案保留了 PSR 0 的关键特性,同时减少了目录结构的层次。此外,还定义了一些额外的规则,这些规则让实现明显地更具互操作性
尽管与目录映射无关,但最终草案还是规范了自动加载器应如何处理错误:禁止抛出异常或引发错误
原因是双重的:
-
很明显,PHP 中的自动加载器是可堆叠的,如果一个加载器失败,另一个则会尝试继续自动加载。自动加载器触发的错误会中断这种机制,也就违反了兼容性
-
class_exists()
和interface_exists()
允许 「 没找到,则自动加载 ( not found, even after trying to autoload ) 」 。一般情况下,自动加载器引发的异常会导致class_exists()
不可用,从互操作性的角度来看这完全是不可接受的。自动加载器希望在类没有找到的情况下可以通过日志记录来提供附加调试信息,而不是与 PSR-3 兼容的记录器或其它
优点:
-
较浅的目录结构
-
更灵活自由的文件位置
-
类名中的下划线不再作为目录分隔符
-
让实现更具互操作性
缺点:
- 跟 PSR-0 一样,不可能只通过类名就能确定它在文件系统中的位置 ( Horde / PEAR 中的「 类-到-文件 ( class-to-file ) 」 惯例 )
4.2 弃选: 只保留 PSR-0
只保留 PSR-0,看似合理,却给我们留下了相对较深的目录结构
优点:
- 无须改变任何人的习惯或实现
缺点:
-
更深的目录结构
-
习惯性的将类中的下划线视为目录分隔符
4.3 弃选: 分离自动加载和转换规则
Beau Simensen 和一些人提出,可以将转换算法从自动加载规范中分离出来,这样,其它规范就可以单独引用转换规则。
完成将它们分开的工作之后,接着进行的投票和一些讨论明显偏好组合的版本 ( 在自动加载规范中嵌入转换规则 )
优点:
- 其它 PSR 规范可以单独引用转换规则
缺点:
- 背离民意调查和一些发起人的意愿
4.4 弃选:使用强制性和叙述性的文字描述
第二次投票是由发起人在听取多个 +1 选民的意见后提出的,他们支持这一提议,但不同意 ( 或理解 ) 提案的措词。
曾有那么一段时间,修改了提案并添加了更多叙述性和更强制性的文字,但这做法遭到了少数参与这的谴责。
一段时间后,Beau Simensen 着眼于 PSR 0 并开始了新的实验性修订。
撰稿者和发起人对这种更加简洁的方式表现出了偏爱并且积极指导正在考虑中的,由 Paul M. Jones
所写的和其它人贡献的版本
对 PHP 5.3.2 及更低版本的兼容性说明
类库实现者需要意识到,5.3.3 版本之前的 PHP 并不会移除命名空间前导的分隔符 ( 正斜杠 ( \
) ) 。未移除命名空间前导的分隔符可能会导致意外的行为
5. 参与人员
5.1 撰稿者
- Paul M. Jones, Solar/Aura
5.2 发起人
- Phil Sturgeon, PyroCMS (Coordinator)
- Larry Garfield, Drupal
5.3 贡献者
- Andreas Hennings
- Bernhard Schussek
- Beau Simensen
- Donald Gilbert
- Mike van Riel
- Paul Dragoonis
- Too many others to name and count
6. 投票表决
-
表决入口: https://groups.google.com/d/msg/php-fig/_LYBgfcEoFE/ZwFTvVTIl4AJ
-
表决结果:
-
第一次尝试: https://groups.google.com/forum/#!topic/php-fig/Ua46E344_Ls, 在新的工作流程出来之前提交; 因为意外修改了提议内容而终止
-
第二次尝试: https://groups.google.com/forum/#!topic/php-fig/NWfyAeF7Psk, 发起人自行决定取消 https://groups.google.com/forum/#!topic/php-fig/t4mW2TQF7iE
-
第三次尝试: TBD
-
7. 参考文档
- Autoloader, round 4
- POLL: Autoloader: Split or Combined?
- PSR-X autoloader spec: Loopholes, ambiguities
- Autoloader: Combine Proposals?
- Package-Oriented Autoloader, Round 2
- Autoloader: looking again at namespace
- DISCUSSION: Package-Oriented Autoloader - vote against
- VOTE: Package-Oriented Autoloader
- Proposal: Package-Oriented Autoloader
- Towards a Package Oriented Autoloader
- List of Alternative PSR-4 Proposals
- Summary of [post-Acceptance Vote pull] PSR-4 discussions