zoukankan      html  css  js  c++  java
  • PHP自动加载下——PSR4

    1.先来介绍一下PSR规范

    PHP-FIG,它的网站是:www.php-fig.org。就是这个联盟组织发明和创造了PSR规范,其中自动加载涉及其中两个规范,一个是PSR0,一个是PSR4, PSR0规范已经过时了,官方有提示,现在主要是用PSR4规范定义自动加载标准。

    2.PRS4简介

    这个 PSR 描述的是通过文件路径自动载入类的指南;它作为对 PSR-0 的补充;根据这个 指导如何规范存放文件来自动载入;
    术语「类」是一个泛称;它包含类,接口,traits 以及其他类似的结构;

    完全限定类名应该类似如下范例:

    ()*
    完全限定类名必须有一个顶级命名空间(Vendor Name);
    完全限定类名可以有多个子命名空间;
    完全限定类名应该有一个终止类名;
    下划线在完全限定类名中是没有特殊含义的;
    字母在完全限定类名中可以是任何大小写的组合;
    所有类名必须以大小写敏感的方式引用;

    当从完全限定类名载入文件时:

    在完全限定类名中,连续的一个或几个子命名空间构成的命名空间前缀(不包括顶级命名空间的分隔符),至少对应着至少一个基础目录。
    在「命名空间前缀」后的连续子命名空间名称对应一个「基础目录」下的子目录,其中的命名 空间分隔符表示目录分隔符。子目录名称必须和子命名空间名大小写匹配;
    终止类名对应一个以 .php 结尾的文件。文件名必须和终止类名大小写匹配;
    自动载入器的实现不可抛出任何异常,不可引发任何等级的错误;也不应返回值;

    完全限定类名 命名空间前缀 基础路径 完全路径
    AcmeLogWriterFile_Writer AcmeLogWrite ./acme-log-writer/lib/ ./acme-log-writer/lib/File_Writer.php
    AuraWebResponseStatus AuraWeb /path/to/aura-web/src/ /path/to/aura-web/src/Response/Status.php
    SymfonyCoreRequest SymfonyCore ./vendor/Symfony/Core/ ./vendor/Symfony/Core/Request.php
    endAcl Zend /usr/includes/Zend/ /usr/includes/Zend/Acl.php

    大家注意看第二列和第四列,命名空间前缀对应基础路径,命名空间前缀之后的子命名空间必须对应代码目录(类名必须是PHP文件)

    3.优化自动加载方法

    上一节中封装自动加载的方法比较简单,无法自动加载带命名空间的类

    spl_autoload_register(function ($class) {
    
        // 命名空间前缀
        $prefix = 'Foo\Bar\';
    
        // 命名空间前缀对应的基础目录
        $base_dir = __DIR__ . '/src/';
    
        // 检查new的类是否有命名空间前缀
        $len = strlen($prefix);
        if (strncmp($prefix, $class, $len) !== 0) {
            return;
        }
    
        // 获取去掉命名空间前缀后的类名
        $relative_class = substr($class, $len);
    
        // 将命名空间的中的分隔符替换为目录分隔符,再加上基础目录和.php后缀,最终拼接成
        // 文件路径
        $file = $base_dir . str_replace('\', '/', $relative_class) . '.php';
    
        // 如果文件存在则require
        if (file_exists($file)) {
            require $file;
        }
    });
    

    但是上面的方法只能适用固定的命名空间前缀,不能通用。

    4、再次优化通用自动加载方法

    <?php
    namespace Example;
    
    /**
     * 下面这个例子实现了一个命名空间前缀对应多个基础目录
     *
     * 现在我们的目录结构是下面这样:
     *
     *     /demo/autoload/
     *          controller/
     *             DemoController.php              # FooBarDemoController
     *             Admin/
     *                 AdminController.php         # FooBarAdminAdminController
     *          model/
     *             DemoModel.php                   # FooBarDemoModel
     *             Admin/
     *                 AdminModel.php              # FooBarAdminAdminModel
     *
     * FooBar分别对应基础路径 /demo/autoload/controller 和 /demo/autoload/model
     */
    class Psr4AutoloaderClass
    {
        /**
         * 一个数组,key为命名空间前缀,值为基础路径
         *
         * @var array
         */
        protected $prefixes = array();
    
        /**
         * 封装自动加载函数
         *
         * @return void
         */
        public function register()
        {
            spl_autoload_register(array($this, 'loadClass'));
        }
    
        /**
         *
         * 添加一个基础路径对应一个命名空间前缀
         *
         * @param string $prefix 命名空间前缀.
         * @param string $base_dir 命名空间类文件的基础路径
         * @param bool true为往数组头部添加元素,false为往数组尾部添加元素
         * @return void
         */
        public function addNamespace($prefix, $base_dir, $prepend = false)
        {
            // 去掉左边的
            $prefix = trim($prefix, '\') . '\';
    
            // 规范基础路径
            $base_dir = rtrim($base_dir, DIRECTORY_SEPARATOR) . '/';
    
            // 初始化数组
            if (isset($this->prefixes[$prefix]) === false) {
                $this->prefixes[$prefix] = array();
            }
    
            // 将命名空间前缀和基础路径存入数组
            if ($prepend) {
                array_unshift($this->prefixes[$prefix], $base_dir);
            } else {
                array_push($this->prefixes[$prefix], $base_dir);
            }
        }
    
        /**
         * 真正包含文件方法,将给到类名文件包含进来
         *
         * @param string $class 全限定类名(包含命名空间).
         * @return 成功将返回文件路径,失败则返回false
         */
        public function loadClass($class)
        {
            $prefix = $class;
            //查找$prefix最后一个的位置,看看最后一个之前的字符串是否在$this->prefixes中
            //如果不存在则继续查询上一个的位置,获取上一个之前的字符串是否在$this->prefixes中
            //如果循环结束还是没有找到则返回false
            while (false !== $pos = strrpos($prefix, '\')) {
                $prefix = substr($class, 0, $pos + 1);
    
                $relative_class = substr($class, $pos + 1);
    
                $mapped_file = $this->loadMappedFile($prefix, $relative_class);
                if ($mapped_file) {
                    return $mapped_file;
                }
    
                //去掉右边的
                $prefix = rtrim($prefix, '\');
            }
    
            return false;
        }
    
        /**
         * 如果参数中的$prefix在$this->prefixes中存在,那么将循环$this->prefixes[$prefix]里的value(基础路径)
         * 之后拼接文件路径,如果文件存在将文件包含进来
         *
         * @param string $prefix 命名空间前缀.
         * @param string $relative_class 真正的类名(不包含命名空间路径的类名).
         * @return mixed 包含成功返回文件路径,否则返回false
         */
        protected function loadMappedFile($prefix, $relative_class)
        {
            // 检查数组中是否有$prefix这个key
            if (isset($this->prefixes[$prefix]) === false) {
                return false;
            }
    
            // 将数组中所有的基础路径中的文件包含进来
            foreach ($this->prefixes[$prefix] as $base_dir) {
    
                // 拼接文件绝对路径
                $file = $base_dir
                    . str_replace('\', '/', $relative_class)
                    . '.php';
    
                // 如果文件存在则包含进来
                if ($this->requireFile($file)) {
                    // 返回文件路径
                    return $file;
                }
            }
    
            // 没有找到文件
            return false;
        }
    
        /**
         *如果文件存在则包含进来.
         *
         * @param string $file 文件路径.
         * @return bool
         */
        protected function requireFile($file)
        {
            if (file_exists($file)) {
                require $file;
                return true;
            }
            return false;
        }
    }
    
  • 相关阅读:
    分布式文件系统--->fastDFS
    varnish4.0缓存代理配置
    varnish4.0缓存代理配置
    varnish4.0缓存代理配置
    SDN 是什么
    SDN 是什么
    SDN 是什么
    Solidworks如何保存为网页可以浏览的3D格式
    github 的 配置SSH
    当一个实例被创建,__init__()就会被自动调用
  • 原文地址:https://www.cnblogs.com/scofi/p/7522131.html
Copyright © 2011-2022 走看看