Home  >  Article  >  Backend Development  >  PSR-4 Example

PSR-4 Example

WBOY
WBOYOriginal
2016-08-08 09:28:43985browse

以下是一个实现PSR-4规范的例子:

Closure Example

<code><span><?php</span><span>/**
 * 一个具体项目实现的例子.
 * 使用SPL注册了autoload函数之后,以下代码将触发autoload函数从
 * /path/to/project/src/Baz/Qux.php 文件加载
 * \Foo\Bar\Baz\Qux 类
 *      new \Foo\Bar\Baz\Qux;
 *      
 *<span> @param</span> string $class The fully-qualified class name.
 *<span> @return</span> void
 */</span>
spl_autoload_register(<span><span>function</span><span>(<span>$class</span>)</span> {</span><span>// 项目约定的命名空间前缀</span><span>$prefix</span> = <span>'Foo\\Bar\\'</span>;

    <span>// 命名空间前缀的基础目录</span><span>$base_dir</span> = <span>__DIR__</span> . <span>'/src/'</span>;

    <span>// 这个类是否使用了命名空间前缀?</span><span>$len</span> = strlen(<span>$prefix</span>);
    <span>if</span> (strncmp(<span>$prefix</span>, <span>$class</span>, <span>$len</span>) !== <span>0</span>) {
        <span>// 没有,尝试下个已注册的 autoloader</span><span>return</span>;
    }

    <span>// 获取相对类名(截取命名空间前缀后剩下的部分)</span><span>$relative_class</span> = substr(<span>$class</span>, <span>$len</span>);

    <span>// 用base目录替换命名空间前缀;</span><span>// 用目录分隔符替换命名空间分隔符;</span><span>// 坠上 .php</span><span>$file</span> = <span>$base_dir</span> . str_replace(<span>'\\'</span>, <span>'/'</span>, <span>$relative_class</span>) . <span>'.php'</span>;

    <span>// 如果文件存在,加载文件。</span><span>if</span> (file_exists(<span>$file</span>)) {
        <span>require</span><span>$file</span>;
    }
});</code>

Class Example

以下是一个处理多命名空间的类的例子。

<code><span><?php</span><span>namespace</span><span>Example</span>;

<span>/**
 * 这个例子是一个针对 一个命名空间前缀对应多个base谬的通用实现。
 * 
 * foo-bar 类的包分别在以下路径下...
 * 
 *     /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
 * 
 * ... 以下代码将类文件的路径添加到 \Foo\Bar\命名空间前缀下。
 * 
 *      <span><?php</span>
 *      // 初始化 loader
 *      $loader = new \Example\Psr4AutoloaderClass;
 *      
 *      // 注册 autoloader
 *      $loader->register();
 *      
 *      // 为命名空间前缀注册base目录。
 *      $loader->addNamespace('Foo\Bar', '/path/to/packages/foo-bar/src');
 *      $loader->addNamespace('Foo\Bar', '/path/to/packages/foo-bar/tests');
 * 
 * 下面的代码将触发 autoloader 尝试从
 * /path/to/packages/foo-bar/src/Qux/Quux.php 加载
 * \Foo\Bar\Qux\Quux 类:
 * 
 *      <span><?php</span>
 *      new \Foo\Bar\Qux\Quux;
 * 
 * 以下代码触发autoloader尝试从
 *  /path/to/packages/foo-bar/tests/Qux/QuuxTest.php
 * 加载\Foo\Bar\Qux\QuuxTest类 :
 * 
 *      <span><?php</span>
 *      new \Foo\Bar\Qux\QuuxTest;
 */</span><span><span>class</span><span>Psr4AutoloaderClass</span>
{</span><span>/**
     * An associative array where the key is a namespace prefix and the value
     * is an array of base directories for classes in that namespace.
     *
     *<span> @var</span> array
     */</span><span>protected</span><span>$prefixes</span> = <span>array</span>();

    <span>/**
     * Register loader with SPL autoloader stack.
     * 
     *<span> @return</span> void
     */</span><span>public</span><span><span>function</span><span>register</span><span>()</span>
    {</span>
        spl_autoload_register(<span>array</span>(<span>$this</span>, <span>'loadClass'</span>));
    }

    <span>/**
     * Adds a base directory for a namespace prefix.
     *
     *<span> @param</span> string $prefix The namespace prefix.
     *<span> @param</span> string $base_dir A base directory for class files in the
     * namespace.
     *<span> @param</span> 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.
     *<span> @return</span> void
     */</span><span>public</span><span><span>function</span><span>addNamespace</span><span>(<span>$prefix</span>, <span>$base_dir</span>, <span>$prepend</span> = false)</span>
    {</span><span>// normalize namespace prefix</span><span>$prefix</span> = trim(<span>$prefix</span>, <span>'\\'</span>) . <span>'\\'</span>;

        <span>// normalize the base directory with a trailing separator</span><span>$base_dir</span> = rtrim(<span>$base_dir</span>, DIRECTORY_SEPARATOR) . <span>'/'</span>;

        <span>// initialize the namespace prefix array</span><span>if</span> (<span>isset</span>(<span>$this</span>->prefixes[<span>$prefix</span>]) === <span>false</span>) {
            <span>$this</span>->prefixes[<span>$prefix</span>] = <span>array</span>();
        }

        <span>// retain the base directory for the namespace prefix</span><span>if</span> (<span>$prepend</span>) {
            array_unshift(<span>$this</span>->prefixes[<span>$prefix</span>], <span>$base_dir</span>);
        } <span>else</span> {
            array_push(<span>$this</span>->prefixes[<span>$prefix</span>], <span>$base_dir</span>);
        }
    }

    <span>/**
     * Loads the class file for a given class name.
     *
     *<span> @param</span> string $class The fully-qualified class name.
     *<span> @return</span> mixed The mapped file name on success, or boolean false on
     * failure.
     */</span><span>public</span><span><span>function</span><span>loadClass</span><span>(<span>$class</span>)</span>
    {</span><span>// the current namespace prefix</span><span>$prefix</span> = <span>$class</span>;

        <span>// work backwards through the namespace names of the fully-qualified</span><span>// class name to find a mapped file name</span><span>while</span> (<span>false</span> !== <span>$pos</span> = strrpos(<span>$prefix</span>, <span>'\\'</span>)) {

            <span>// retain the trailing namespace separator in the prefix</span><span>$prefix</span> = substr(<span>$class</span>, <span>0</span>, <span>$pos</span> + <span>1</span>);

            <span>// the rest is the relative class name</span><span>$relative_class</span> = substr(<span>$class</span>, <span>$pos</span> + <span>1</span>);

            <span>// try to load a mapped file for the prefix and relative class</span><span>$mapped_file</span> = <span>$this</span>->loadMappedFile(<span>$prefix</span>, <span>$relative_class</span>);
            <span>if</span> (<span>$mapped_file</span>) {
                <span>return</span><span>$mapped_file</span>;
            }

            <span>// remove the trailing namespace separator for the next iteration</span><span>// of strrpos()</span><span>$prefix</span> = rtrim(<span>$prefix</span>, <span>'\\'</span>);   
        }

        <span>// never found a mapped file</span><span>return</span><span>false</span>;
    }

    <span>/**
     * Load the mapped file for a namespace prefix and relative class.
     * 
     *<span> @param</span> string $prefix The namespace prefix.
     *<span> @param</span> string $relative_class The relative class name.
     *<span> @return</span> mixed Boolean false if no mapped file can be loaded, or the
     * name of the mapped file that was loaded.
     */</span><span>protected</span><span><span>function</span><span>loadMappedFile</span><span>(<span>$prefix</span>, <span>$relative_class</span>)</span>
    {</span><span>// are there any base directories for this namespace prefix?</span><span>if</span> (<span>isset</span>(<span>$this</span>->prefixes[<span>$prefix</span>]) === <span>false</span>) {
            <span>return</span><span>false</span>;
        }

        <span>// look through base directories for this namespace prefix</span><span>foreach</span> (<span>$this</span>->prefixes[<span>$prefix</span>] <span>as</span><span>$base_dir</span>) {

            <span>// replace the namespace prefix with the base directory,</span><span>// replace namespace separators with directory separators</span><span>// in the relative class name, append with .php</span><span>$file</span> = <span>$base_dir</span>
                  . str_replace(<span>'\\'</span>, <span>'/'</span>, <span>$relative_class</span>)
                  . <span>'.php'</span>;

            <span>// if the mapped file exists, require it</span><span>if</span> (<span>$this</span>->requireFile(<span>$file</span>)) {
                <span>// yes, we're done</span><span>return</span><span>$file</span>;
            }
        }

        <span>// never found it</span><span>return</span><span>false</span>;
    }

    <span>/**
     * If a file exists, require it from the file system.
     * 
     *<span> @param</span> string $file The file to require.
     *<span> @return</span> bool True if the file exists, false if not.
     */</span><span>protected</span><span><span>function</span><span>requireFile</span><span>(<span>$file</span>)</span>
    {</span><span>if</span> (file_exists(<span>$file</span>)) {
            <span>require</span><span>$file</span>;
            <span>return</span><span>true</span>;
        }
        <span>return</span><span>false</span>;
    }
}</span></span></code>

Unit Tests

The following example is one way of unit testing the above class loader:

<code><span><?php</span><span>namespace</span><span>Example</span>\<span>Tests</span>;

<span><span>class</span><span>MockPsr4AutoloaderClass</span><span>extends</span><span>Psr4AutoloaderClass</span>
{</span><span>protected</span><span>$files</span> = <span>array</span>();

    <span>public</span><span><span>function</span><span>setFiles</span><span>(array <span>$files</span>)</span>
    {</span><span>$this</span>->files = <span>$files</span>;
    }

    <span>protected</span><span><span>function</span><span>requireFile</span><span>(<span>$file</span>)</span>
    {</span><span>return</span> in_array(<span>$file</span>, <span>$this</span>->files);
    }
}

<span><span>class</span><span>Psr4AutoloaderClassTest</span><span>extends</span> \<span>PHPUnit_Framework_TestCase</span>
{</span><span>protected</span><span>$loader</span>;

    <span>protected</span><span><span>function</span><span>setUp</span><span>()</span>
    {</span><span>$this</span>->loader = <span>new</span> MockPsr4AutoloaderClass;

        <span>$this</span>->loader->setFiles(<span>array</span>(
            <span>'/vendor/foo.bar/src/ClassName.php'</span>,
            <span>'/vendor/foo.bar/src/DoomClassName.php'</span>,
            <span>'/vendor/foo.bar/tests/ClassNameTest.php'</span>,
            <span>'/vendor/foo.bardoom/src/ClassName.php'</span>,
            <span>'/vendor/foo.bar.baz.dib/src/ClassName.php'</span>,
            <span>'/vendor/foo.bar.baz.dib.zim.gir/src/ClassName.php'</span>,
        ));

        <span>$this</span>->loader->addNamespace(
            <span>'Foo\Bar'</span>,
            <span>'/vendor/foo.bar/src'</span>
        );

        <span>$this</span>->loader->addNamespace(
            <span>'Foo\Bar'</span>,
            <span>'/vendor/foo.bar/tests'</span>
        );

        <span>$this</span>->loader->addNamespace(
            <span>'Foo\BarDoom'</span>,
            <span>'/vendor/foo.bardoom/src'</span>
        );

        <span>$this</span>->loader->addNamespace(
            <span>'Foo\Bar\Baz\Dib'</span>,
            <span>'/vendor/foo.bar.baz.dib/src'</span>
        );

        <span>$this</span>->loader->addNamespace(
            <span>'Foo\Bar\Baz\Dib\Zim\Gir'</span>,
            <span>'/vendor/foo.bar.baz.dib.zim.gir/src'</span>
        );
    }

    <span>public</span><span><span>function</span><span>testExistingFile</span><span>()</span>
    {</span><span>$actual</span> = <span>$this</span>->loader->loadClass(<span>'Foo\Bar\ClassName'</span>);
        <span>$expect</span> = <span>'/vendor/foo.bar/src/ClassName.php'</span>;
        <span>$this</span>->assertSame(<span>$expect</span>, <span>$actual</span>);

        <span>$actual</span> = <span>$this</span>->loader->loadClass(<span>'Foo\Bar\ClassNameTest'</span>);
        <span>$expect</span> = <span>'/vendor/foo.bar/tests/ClassNameTest.php'</span>;
        <span>$this</span>->assertSame(<span>$expect</span>, <span>$actual</span>);
    }

    <span>public</span><span><span>function</span><span>testMissingFile</span><span>()</span>
    {</span><span>$actual</span> = <span>$this</span>->loader->loadClass(<span>'No_Vendor\No_Package\NoClass'</span>);
        <span>$this</span>->assertFalse(<span>$actual</span>);
    }

    <span>public</span><span><span>function</span><span>testDeepFile</span><span>()</span>
    {</span><span>$actual</span> = <span>$this</span>->loader->loadClass(<span>'Foo\Bar\Baz\Dib\Zim\Gir\ClassName'</span>);
        <span>$expect</span> = <span>'/vendor/foo.bar.baz.dib.zim.gir/src/ClassName.php'</span>;
        <span>$this</span>->assertSame(<span>$expect</span>, <span>$actual</span>);
    }

    <span>public</span><span><span>function</span><span>testConfusion</span><span>()</span>
    {</span><span>$actual</span> = <span>$this</span>->loader->loadClass(<span>'Foo\Bar\DoomClassName'</span>);
        <span>$expect</span> = <span>'/vendor/foo.bar/src/DoomClassName.php'</span>;
        <span>$this</span>->assertSame(<span>$expect</span>, <span>$actual</span>);

        <span>$actual</span> = <span>$this</span>->loader->loadClass(<span>'Foo\BarDoom\ClassName'</span>);
        <span>$expect</span> = <span>'/vendor/foo.bardoom/src/ClassName.php'</span>;
        <span>$this</span>->assertSame(<span>$expect</span>, <span>$actual</span>);
    }
}</span></code>

以上就介绍了PSR-4 实例,包括了方面的内容,希望对PHP教程有兴趣的朋友有所帮助。

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn