PSR-4与PSR-0

上一篇文章中,介绍了PSR-0和autoload相关的内容。继PSR-0这个PHP autoloading的规范之后,PHP-FIG又推出了PSR-4,称为改进的autoloading规范。

在PSR-0中,\Symfony\Core\Request会被转换成文件系统的/path/to/project/lib/vendor/Symfony/Core/Request.php这个路径。PSR-4与PSR-0在内容上相差也不大。

在此就不详谈两者的定义了。来看看两者在实际中的一些区别吧。由于Composer的流行,这里对Composer中这两种风格进行比较。

在Composer中,遵循PSR-0标准的典型目录结构是这样的:

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/和test/目录又重新包含了Vendor和Package目录。

再来看看PSR-4的:

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

可以看到目录结构更加简洁了。

在PSR-0中目录结构要与命名空间层层对应,无法插入一个单独的目录。而PSR-4可以。

对比PSR-0,除了PSR-4可以更简洁外,需要注意PSR-0中对下划线(_)是有特殊的处理的,下划线会转换成DIRECTORY_SEPARATOR,这是出于对PHP5.3以前版本兼容的考虑,而PSR-4中是没有这个处理的,这也是两者比较大的一个区别。

此外,PSR-4要求在autoloader中不允许抛出exceptions以及引发任何级别的errors,也不应该有返回值。这是因为可能注册了多个autoloaders,如果一个autoloader没有找到对应的class,应该交给下一个来处理,而不是去阻断这个通道。

PSR-4更简洁更灵活了,但这使得它相对更复杂了。例如通过完全符合PSR-0标准的class name,通常可以明确的知道这个class的路径,而PSR-4可能就不是这样了。

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');

     //此时一个namespace prefix对应到了多个"base directory"

     //autoloader会去加载/path/to/packages/foo-bar/src/Qux/Quux.php
     new \Foo\Bar\Qux\Quux;

     //autoloader会去加载/path/to/packages/foo-bar/tests/Qux/QuuxTest.php
     new \Foo\Bar\Qux\QuuxTest;

下面是如上PSR-4 autoloader的实现:

<?php
namespace Example;

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 array
        if (isset($this->prefixes[$prefix]) === false) {
            $this->prefixes[$prefix] = array();
        }

        // retain the base directory for the namespace prefix
        if ($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 name
        while (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 file
        return 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 prefix
        foreach ($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 it
            if ($this->requireFile($file)) {
                // yes, we're done
                return $file;
            }
        }

        // never found it
        return 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;
    }
}

单元测试代码:

<?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);
    }
}

完。

PSR-4与PSR-0,布布扣,bubuko.com

时间: 2024-12-12 17:40:24

PSR-4与PSR-0的相关文章

[转帖]Oracle 补丁体系(PSR/PSU/CPU) 及 opatch 工具 介绍

Oracle 补丁体系(PSR/PSU/CPU) 及 opatch 工具 介绍 原文:http://blog.csdn.net/tianlesoftware/article/details/5809526 其实 原始blog 比较早了 应该是六年前的内容了 但是感觉还是挺好的 自己公司的产品暂时没做到这样 但是存在一个问题, 一个是 云转型 一个是 原始的monolith 的应用 的区别对待 其实 认为不管是micro service 还是 monolithic 的应用 其实都是差不多的东西.

Deploy Oracle 10.2.0.5 on Red Hat Enterprise Linux 6.4

一.Linux系统安装配置 1.安装系统时选Desktop 2.设置eth0网卡为静态IP,添加子网掩码,网关,DNS,并配置自动启动 3.修改/etc/hosts,添加主机名和对应IP 4.禁用firewall和selinux chkconfig iptables off vi /etc/selinux/config SELINUX=disabled [[email protected] selinux]# setenforce 0 [[email protected] selinux]# g

进程相关命令的使用

ps, top, htop,  vmstat, dstat,pstree,pidof, pgrep 在htop默认没有添加字段的情况下. ps top htop 常驻内存 rss,  rsz,  rssize 线性内存 vsz,  vsize VIRT VIRT 进程占用CPU %cpu %CPU CPU% 进程状态 state,  S S S 物理内存使用 %mem,RES MEM%,  RES 共享内享 SHR SHR 进程已使用CPU时长 time,  cputime TIME+ TIME

arcengine 要素类的复制

转自原文arcengine 要素类的复制 using System; using System.Collections.Generic; using System.Text; using ESRI.ArcGIS.Geodatabase; using ESRI.ArcGIS.Geometry; using ESRI.ArcGIS.Carto; namespace MyGISClass { /// <summary> /// 该类主要包含了要素类的复制以及同要素类数据的加载 /// 函数主要用于S

Linux程序管理:top.htop.glances,dstat等命令使用方法

什么是进程? 在Linux系统中:触发任何一个事件时,系统都会将它定义为一个进程,并且给予这个进程一个ID,称为PID,同时依据触发这个进程的用户与相关属性,给予这个PID一组有效的全乡设置.而程序就是系统工作时,启动的二进制文件.程序一般是在磁盘中的,通过用户的执行触发,然后加载到内存中成为一个个体,即进程.而常驻在内存中的进程通常都是负责一些系统所提供的功能以服务用户的各项任务,因此这些常驻进程就会被称为服务 进程和线程的区别: 1.定义 进程是具有一定独立功能的程序,是系统进行资源分配和调

windbg命令详解

DLL 该扩展仅在内核模式下使用,即使它是在Ext.dll中的. Windows NT 4.0 Ext.dll Windows 2000 Ext.dll Windows XP和之后 Ext.dll 注释 如果不提供参数,调试器会列出所有进程,以及时间和优先级统计.这和使用!process @#Process 0 作为CommandString值一样. To terminate execution at any point, press CTRL+BREAK (in WinDbg) or CTRL

处理器执行模式+进程切换

大多数处理器至少支持两种执行模式.某些指令只能在特权模式下执行,包括读取或者改变诸如程序状态字之类控制寄存器的指令.原始IO指令和内存管理相关的指令.另外,有一部分内存区域仅在特权下可以被访问到. 非特权态常被称为用户态,这是因为用户程序通常在该模式下执行:特权态可称作系统态.控制态或者内核态,内核态指的是操作系统的内核,这是操作系统中包含重要系统功能的部分. 这样产生了两个问题:处理器如何知道它正在什么模式下执行以及如何改变这一模式.对第一个问题,程序状态字中有一个位表示执行模式,这一位应某些

Linux进程管理命令之ps、dstat、top、htop

Linux的进程及cpu.内存等状况的监测是一件非常重要的事情.这里介绍几个比较常用的工具. 概述:ps命令.dstat命令.top命令.htop命令 一.ps命令 - 进程状态查看工具 工作机制:通过抽取内核提供的/proc/PID(每个进程的PID,有多个)目录下的各进程状态来显示给用户以供查看. 使用格式:ps [options] 选项有三种风格: 1.UNIX风格,选项可以组合在一起且前面跟一个短横线. 2.BSD风格,选项可以组合在一起且前面可以不跟一个短横线. 3.GNU风格,是长选

进程和cpu的相关知识和简单调优方案

进程就是一段执行的程序,每当一个程序运行时,对于操作系统本身来说,就创建了一个进程,并且分配了对应的资源.进程可以分为3个类别: 1.交互式进程(I/O) 2.批处理进程 (CPU) 3.实时进程 (REAL-TIME) 对于交互式进程来说,一般其占用的cpu时间片很段,但是优先级偏高;批处理进程占用的cpu时间片很长,但是优先级偏底;实时进程是内核所使用的,其优先级高于前面两种. 上面说到了优先级,linux进程是具有优先级的,一般分为两种: 1.实时优先级 2.静态优先级 实时优先级的取值范

转://Oracle补丁及opatch工具介绍

一. CPU(Critical Patch Update) 一个CPU内包含了对多个安全漏洞的修复,并且也包括相应必需的非安全漏洞的补丁.CPU是累积型的,只要安装最新发布的CPU即可,其中包括之前发布的所有CPU的内容.事实上,在CPU之前的安全漏洞修改除去个别例外也被包括在CPU中.Oracle公司只对处于标准技术支持和延长支持期间的产品提供CPU更新,对处于维持支持范围的产品不提供新的CPU.(对于9.2以前的版本,只对处于ECS和EMS期间的版本提供CPU更新.) 一般对当前补丁发行版及