PSR规范

PSR是由PHP Framework Interoperability Group(PHP通用性框架小组)发布的一系列标准/规范,目前包括了PSR-0~PSR-4共4个,而PSR-0就是其中的自动加载标准(其后的PSR-4称为改进的自动加载的标准,是PSR-0的补充。PSR-0使用更广泛)。
https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md

强制要求

下面描述了具体互操作性的自动加载所必须的条件:

  1. 一个完全合格的namespace和class必须符合这样的结构 \*
  2. 每个namespace必须有一个顶层的namespace(”Vendor Name”提供者名字)
  3. 每个namespace可以有多个子namespace
  4. 当从文件系统中加载时,每个namespace的分隔符要转换成 DIRECTORY_SEPARATOR(操作系统路径分隔符)
  5. 在CLASS NAME(类名)中,每个下划线(_)符号要转换成DIRECTORY_SEPARATOR。在namespace中,下划线(_)符号是没有(特殊)意义的。
  6. 当从文件系统中载入时,合格的namespace和class一定是以 .php 结尾的
  7. verdor name,namespaces,class名可以由大小写字母组合而成(大小写敏感的)
  8. 除此之外可能还会遵循这个规则:如果文件不存在则返回false。

例子

1
2
3
4
5
6
7
\Doctrine\Common\IsolatedClassLoader => /path/to/project/lib/vendor/Doctrine/Common/IsolatedClassLoader.php
\Symfony\Core\Request => /path/to/project/lib/vendor/Symfony/Core/Request.php
\Zend\Acl => /path/to/project/lib/vendor/Zend/Acl.php
\Zend\Mail\Message => /path/to/project/lib/vendor/Zend/Mail/Message.php
NameSpace和Class Name中的下划线
\namespace\package\Class_Name => /path/to/project/lib/vendor/namespace/package/Class/Name.php
\namespace\package_name\Class_Name => /path/to/project/lib/vendor/namespace/package_name/Class/Name.php

将下划线转换成DIRECTORY_SEPARATOR实际上是出于兼容PHP5.3之前的版本的考虑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php  
function autoload($className)
{
//这里的$className一般是用namespace的方式来引用的,文章开头已有介绍
//去除$className左边的'\' 这是PHP5.3的一个bug,详见https://bugs.php.net/50731
$className = ltrim($className, '\\');
$fileName = '';
$namespace = '';
//找到最后一个namespace分隔符的位置
if ($lastNsPos = strrpos($className, '\\')) {
$namespace = substr($className, 0, $lastNsPos);
$className = substr($className, $lastNsPos + 1);
$fileName = str_replace('\\', DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR;
}
$fileName .= str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php';

require $fileName;
}
?>

NameSpace(命名空间)

namespace是PHP5.3版本加入的新特性,用来解决在编写类库或应用程序时创建可重用的代码如类或函数时碰到的两类问题:

  1. 用户编写的代码与PHP内部的类/函数/常量或第三方类/函数/常量之间的名字冲突。
  2. 为很长的标识符名称(通常是为了缓解第一类问题而定义的)创建一个别名(或简短)的名称,提高源代码的可读性。

PHP 命名空间中的元素使用了类似文件系统的原理。例如,类名可以通过三种方式引用:

  1. 非限定名称,或不包含前缀的类名称,例如 $a=new foo(); 或 foo::staticmethod();。如果当前命名空间是 currentnamespace,foo 将被解析为 currentnamespace\foo。如果使用 foo 的代码是全局的,不包含在任何命名空间中的代码,则 foo 会被解析为foo。 警告:如果命名空间中的函数或常量未定义,则该非限定的函数名称或常量名称会被解析为全局函数名称或常量名称。详情参见 使用命名空间:后备全局函数名称/常量名称。
  2. 限定名称,或包含前缀的名称,例如 $a = new subnamespace\foo(); 或 subnamespace\foo::staticmethod();。如果当前的命名空间是 currentnamespace,则 foo 会被解析为 currentnamespace\subnamespace\foo。如果使用 foo 的代码是全局的,不包含在任何命名空间中的代码,foo 会被解析为subnamespace\foo。
  3. 完全限定名称,或包含了全局前缀操作符的名称,例如, $a = new \currentnamespace\foo(); 或 \currentnamespace\foo::staticmethod();。在这种情况下,foo 总是被解析为代码中的文字名(literal name)currentnamespace\foo。
    另外注意访问任意全局类、函数或常量,都可以使用完全限定名称,例如 \strlen() 或 \Exception 或 \INI_ALL。
1
2
3
4
5
6
7
8
9
10
11
12
<?php  
use My\Full\Classname as Another, My\Full\NSname;

$obj = new Another; // 实例化一个 My\Full\Classname 对象
$obj = new \Another; // 实例化一个Another对象
$obj = new Another\thing; // 实例化一个My\Full\Classname\thing对象
$obj = new \Another\thing; // 实例化一个Another\thing对象

$a = \strlen('hi'); // 调用全局函数strlen
$b = \INI_ALL; // 访问全局常量 INI_ALL
$c = new \Exception('error'); // 实例化全局类 Exception
?>

Autoload(自动加载)

通过定义的一个或一系列autoload函数,它会在试图使用尚未被定义的类时自动调用。通过调用autoload函数,脚本引擎在 PHP 出错失败前有了最后一个机会加载所需的类。这个autoload函数可以是默认的__autoload(),如下:

1
2
3
4
5
6
7
<?php  
function __autoload($class_name) {
require_once $class_name . '.php';
}

$obj = new MyClass();
?>

也可以采用更灵活的方式,通过spl_autoload_register()来定义我们自己的__autoload()函数:

1
2
3
4
5
6
7
<?php  
function my_autoload($class_name) {
require_once $class_name . '.php';
}
spl_autoload_register("my_autoload");
$obj = new MyClass();
?>

以上代码将my_autoload()函数注册到autoload栈中,从而取到autoload()函数(注意autoload()函数将不再起作用,但可以显式的将其注册到autoload栈)。注意到刚才提到了__autoload栈,意味着我们可以注册多个autoload函数,根据注册的顺序依次加载(通过spl_autoload_register的第三个参数可以改变这一顺序)。
在这里我们展示了autoload函数最简单的例子,当然通过一些规则的设置,也可以胜任实际环境中许多复杂的情况。