PSR-4の例

WBOY
WBOYオリジナル
2016-08-08 09:28:431012ブラウズ

以下是一个实现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教程有兴趣的朋友有所帮助。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。