FIG制定的PHP规范,简称PSR,是PHP开发的事实标准。

PSR原本有四个规范,分别是:

PSR-0 自动加载
PSR-1 基本代码规范
PSR-2 代码样式
PSR-3 日志接口

2013年底,新出了第5个规范——PSR-4。

PSR-4规范了如何指定文件路径从而自动加载类定义,同时规范了自动加载文件的位置。这个乍一看和PSR-0重复了,实际上,在功能上确实有所重复。区别在于PSR-4的规范比较干净,去除了兼容PHP 5.3以前版本的内容,有一点PSR-0升级版的感觉。当然,PSR-4也不是要完全替代PSR-0,而是在必要的时候补充PSR-0——当然,如果你愿意,PSR-4也可以替代PSR-0。PSR-4可以和包括PSR-0在内的其他自动加载机制共同使用。

PSR-4和PSR-0最大的区别是对下划线(underscore)的定义不同。PSR-4中,在类名中使用下划线没有任何特殊含义。而PSR-0则规定类名中的下划线_会被转化成目录分隔符。
代码样例

以下代码展示了遵循PSR-4的类定义,这个类处理多个命名空间:

/*** An example of a general-purpose implementation that includes the optional* functionality of allowing multiple base directories for a single namespace* prefix.* * Given a foo-bar package of classes in the file system at the following* paths ...* *     /path/to/packages/foo-bar/*         src/*             Baz.php             # Foo\Bar\Baz*             Qux/*                 Quux.php        # Foo\Bar\Qux\Quux*         tests/*             BazTest.php         # Foo\Bar\BazTest*             Qux/*                 QuuxTest.php    # Foo\Bar\Qux\QuuxTest* * ... add the path to the class files for the \Foo\Bar\ namespace prefix* as follows:* *      <?php*      // instantiate the loader*      $loader = new \Example\Psr4AutoloaderClass;*      *      // register the autoloader*      $loader->register();*      *      // register the base directories for the namespace prefix*      $loader->addNamespace('Foo\Bar', '/path/to/packages/foo-bar/src');*      $loader->addNamespace('Foo\Bar', '/path/to/packages/foo-bar/tests');* * The following line would cause the autoloader to attempt to load the* \Foo\Bar\Qux\Quux class from /path/to/packages/foo-bar/src/Qux/Quux.php:* *      <?php*      new \Foo\Bar\Qux\Quux;* * The following line would cause the autoloader to attempt to load the * \Foo\Bar\Qux\QuuxTest class from /path/to/packages/foo-bar/tests/Qux/QuuxTest.php:* *      <?php*      new \Foo\Bar\Qux\QuuxTest;*/
class Psr4AutoloaderClass
{/*** An associative array where the key is a namespace prefix and the value* is an array of base directories for classes in that namespace.** @var array*/protected $prefixes = array();/*** Register loader with SPL autoloader stack.* * @return void*/public function register(){spl_autoload_register(array($this, 'loadClass'));}/*** Adds a base directory for a namespace prefix.** @param string $prefix The namespace prefix.* @param string $base_dir A base directory for class files in the* namespace.* @param bool $prepend If true, prepend the base directory to the stack* instead of appending it; this causes it to be searched first rather* than last.* @return void*/public function addNamespace($prefix, $base_dir, $prepend = false){// normalize namespace prefix$prefix = trim($prefix, '\\') . '\\';// normalize the base directory with a trailing separator$base_dir = rtrim($base_dir, '/') . DIRECTORY_SEPARATOR;$base_dir = rtrim($base_dir, DIRECTORY_SEPARATOR) . '/';// initialize the namespace prefix arrayif (isset($this->prefixes[$prefix]) === false) {$this->prefixes[$prefix] = array();}// retain the base directory for the namespace prefixif ($prepend) {array_unshift($this->prefixes[$prefix], $base_dir);} else {array_push($this->prefixes[$prefix], $base_dir);}}/*** Loads the class file for a given class name.** @param string $class The fully-qualified class name.* @return mixed The mapped file name on success, or boolean false on* failure.*/public function loadClass($class){// the current namespace prefix$prefix = $class;// work backwards through the namespace names of the fully-qualified// class name to find a mapped file namewhile (false !== $pos = strrpos($prefix, '\\')) {// retain the trailing namespace separator in the prefix$prefix = substr($class, 0, $pos + 1);// the rest is the relative class name$relative_class = substr($class, $pos + 1);// try to load a mapped file for the prefix and relative class$mapped_file = $this->loadMappedFile($prefix, $relative_class);if ($mapped_file) {return $mapped_file;}// remove the trailing namespace separator for the next iteration// of strrpos()$prefix = rtrim($prefix, '\\');   }// never found a mapped filereturn false;}/*** Load the mapped file for a namespace prefix and relative class.* * @param string $prefix The namespace prefix.* @param string $relative_class The relative class name.* @return mixed Boolean false if no mapped file can be loaded, or the* name of the mapped file that was loaded.*/protected function loadMappedFile($prefix, $relative_class){// are there any base directories for this namespace prefix?if (isset($this->prefixes[$prefix]) === false) {return false;}// look through base directories for this namespace prefixforeach ($this->prefixes[$prefix] as $base_dir) {// replace the namespace prefix with the base directory,// replace namespace separators with directory separators// in the relative class name, append with .php$file = $base_dir. str_replace('\\', DIRECTORY_SEPARATOR, $relative_class). '.php';$file = $base_dir. str_replace('\\', '/', $relative_class). '.php';// if the mapped file exists, require itif ($this->requireFile($file)) {// yes, we're donereturn $file;}}// never found itreturn false;}/*** If a file exists, require it from the file system.* * @param string $file The file to require.* @return bool True if the file exists, false if not.*/protected function requireFile($file){if (file_exists($file)) {require $file;return true;}return false;}
}

为什么这里要循环着去试图查找文件,在while循环中,会慢慢的缩短命名空间前缀的名称去需找合适的命名空间前缀,为什么要这么做呢?

  循环查找文件是为了在命名空间中包含更多的内容,不用每次在父命名空间中新建一个文件夹的时候都去添加一个新的命名空间前缀,就像下面这个图中描述的那样:

  当一个文件在一个命名空间下的子目录下的时候,我们不用去新建命名空间前缀就可以成功加载需要的文件,维护命名空间前缀的数组内容更少,更好维护。相反的如果没有循环查找,就是下面这个样子的

每次新建一个子目录就要去新加一个命名空间前缀,是不是很麻烦

相应的单元测试代码

<?php
namespace Example\Tests;class MockPsr4AutoloaderClass extends Psr4AutoloaderClass
{protected $files = array();public function setFiles(array $files){$this->files = $files;}protected function requireFile($file){return in_array($file, $this->files);}
}class Psr4AutoloaderClassTest extends \PHPUnit_Framework_TestCase
{protected $loader;protected function setUp(){$this->loader = new MockPsr4AutoloaderClass;$this->loader->setFiles(array('/vendor/foo.bar/src/ClassName.php','/vendor/foo.bar/src/DoomClassName.php','/vendor/foo.bar/tests/ClassNameTest.php','/vendor/foo.bardoom/src/ClassName.php','/vendor/foo.bar.baz.dib/src/ClassName.php','/vendor/foo.bar.baz.dib.zim.gir/src/ClassName.php',));$this->loader->addNamespace('Foo\Bar','/vendor/foo.bar/src');$this->loader->addNamespace('Foo\Bar','/vendor/foo.bar/tests');$this->loader->addNamespace('Foo\BarDoom','/vendor/foo.bardoom/src');$this->loader->addNamespace('Foo\Bar\Baz\Dib','/vendor/foo.bar.baz.dib/src');$this->loader->addNamespace('Foo\Bar\Baz\Dib\Zim\Gir','/vendor/foo.bar.baz.dib.zim.gir/src');}public function testExistingFile(){$actual = $this->loader->loadClass('Foo\Bar\ClassName');$expect = '/vendor/foo.bar/src/ClassName.php';$this->assertSame($expect, $actual);$actual = $this->loader->loadClass('Foo\Bar\ClassNameTest');$expect = '/vendor/foo.bar/tests/ClassNameTest.php';$this->assertSame($expect, $actual);}public function testMissingFile(){$actual = $this->loader->loadClass('No_Vendor\No_Package\NoClass');$this->assertFalse($actual);}public function testDeepFile(){$actual = $this->loader->loadClass('Foo\Bar\Baz\Dib\Zim\Gir\ClassName');$expect = '/vendor/foo.bar.baz.dib.zim.gir/src/ClassName.php';$this->assertSame($expect, $actual);}public function testConfusion(){$actual = $this->loader->loadClass('Foo\Bar\DoomClassName');$expect = '/vendor/foo.bar/src/DoomClassName.php';$this->assertSame($expect, $actual);$actual = $this->loader->loadClass('Foo\BarDoom\ClassName');$expect = '/vendor/foo.bardoom/src/ClassName.php';$this->assertSame($expect, $actual);}
}

Composer

PHP的包管理系统Composer已经支持PSR-4,同时也允许在composer.json中定义不同的prefix使用不同的自动加载机制。

Composer使用PSR-0风格

vendor/vendor_name/package_name/src/Vendor_Name/Package_Name/ClassName.php       # Vendor_Name\Package_Name\ClassNametests/Vendor_Name/Package_Name/ClassNameTest.php   # Vendor_Name\Package_Name\ClassName

Composer使用PSR-4风格

vendor/vendor_name/package_name/src/ClassName.php       # Vendor_Name\Package_Name\ClassNametests/ClassNameTest.php   # Vendor_Name\Package_Name\ClassNameTest

对比以上两种结构,明显可以看出PSR-4带来更简洁的文件结构。

PSR-4与PSR-0的区别相关推荐

  1. href=#与href=javascript:void(0)的区别

    href="#"与href="javascript:void(0)"的区别 # 包含了一个位置信息,默认的锚是#top 也就是网页的上端. 而javascrip ...

  2. 计算机usb接口充电效率差,usb2.0和3.0的区别 从传输速度和充电效率等来区别

    在选择电脑主板的时候,一般都会看一下主板的USB接口是2.0还是3.0.很多人都知道3.0应该比2.0要好,那么具体好在哪里,usb2.0和usb3.0的区别有哪些?下面将从传输速度.充电效率和接线接 ...

  3. HTTP 2.0与HTTP 1.0的区别 ?

    HTTP 2.0与HTTP 1.0的区别 1.什么是HTTP 2.0 2.与HTTP 1.1相比,主要区别包括 3.HTTP/2为什么是二进制? 4.为什么 HTTP/2 需要多路传输? 5.消息头为 ...

  4. HTTP1.0、HTTP1.1和HTTP2.0的区别

    HTTP1.0.HTTP1.1和HTTP2.0的区别 文章目录 HTTP1.0.HTTP1.1和HTTP2.0的区别 一.HTTP的历史 二.HTTP的基本优化 三.HTTP1.0和HTTP1.1的区 ...

  5. 硬件知识:USB3.0和USB2.0的区别,看完你就懂了!

    1.USB的概念介绍 USB是计算机公司和通信公司在1994年联合制定的新一代接口标准,全称为通用串行总线 (Universal Serial Bus,USB) .USB 总线作为一种高速串行总线,其 ...

  6. border:none 与border:0的区别

    border:none与border:0的区别体现为两点:一是理论上的性能差异,二是浏览器兼容性的差异. 性能差异: [border:0;]把border设为"0"像素效果等于bo ...

  7. void ,NULL与0的区别联系

    void ,NULL及0的区别联系 void的详解: void的字面意思是"无类型"或"空类型",void*则为"无针型指针",那就意味着v ...

  8. a href=#与 a href=javascript:void(0) 的区别

    a href="#"> 点击链接后,页面会向上滚到页首,# 默认锚点为 #TOP<a href="javascript:void(0)" onCli ...

  9. Java中list==null与list.size( )==0的区别

    在此特别说明一下list!=null和list.size()>0的区别: 1.list==null,意味着list压根没有地址,在堆内就不存在. 2.list.size()=0 意思堆内有lis ...

  10. 计算机的USB是什么,usb2.0和3.0的区别,教您电脑usb2.0和3.0的有什么区别

    随着电脑的不断普及,现在的人们越来越离不开电脑.同时随着电脑的普及,高速USB3.0时代也随之到来,不过很多用户对USB2.0和USB3.0不知道如何区别,有用户不禁问电脑usb2.0和3.0的有什么 ...

最新文章

  1. 把时间当作朋友(四)
  2. [转:Pro ASP.NET MVC 5中的例子]使用MVC4,Ninject,EF,Moq,构建一个真实的应用电子商务SportsStore...
  3. 正确认识使用UML中的类图——辨析类图的两种存在形式
  4. jQuery避免$符和其他JS库冲突的方法对比
  5. 一站式快速自助建站-超低价0代码建站套餐助你轻松拥有自己的网站
  6. Veebot-自动静脉抽血机器人
  7. 敏捷开发总结(1)软件研发过程
  8. 【Python可视化】Windows 10系统上Pyecharts安装教程
  9. 什么是响应式布局设计
  10. Linux系统安装MySql步骤及截屏
  11. java int 详解,int与Integer详解(java基础篇)
  12. 天呐,你竟然还在用 try–catch-finally
  13. JMeter录制的两种方法
  14. 易恢复Ontrack EasyRecovery15绿色版
  15. 我的世界服务器标记家位置,我的世界:你真的会看藏宝图吗?学会用标记,位置一次就找对...
  16. 搭建sip软电话环境
  17. IDF实验室之万里寻踪红与黑
  18. 使用域名查询网站ip地址
  19. [原创] Bandwagon 追加 swap 大小
  20. DirectX11--教程项目无法编译、运行的解决方法

热门文章

  1. 选型宝访谈:Office 365+微信=?
  2. android字体颜色黑色,Android中颜色选择器和改变字体颜色的实例教程
  3. linux串口介绍与编程
  4. vue实现机器人聊天
  5. document.getElementById()方法使用
  6. linux64下编译32位程序,报错
  7. 手动杀毒专题(黑防VIP)
  8. 关于opencv遍历像素速度的提高方案
  9. 数据库移动数据总结一附录
  10. mysql怎么删除函数节点_JS removeChild()方法:删除节点