实现 PSR-0和PSR-4的类自动加载器并带案例说明

8人阅读 评论(0) 收藏 举报
分类:

大家在阅读文档 或者使用一些第三方的框架或者软件的时候,都听过或者看过里面要求说实现了psr0或者psr4的规范。

我也一直在查资料,找痕迹。现在我的理解是,其实这2个规范就是对类的装载,实现自动寻路径


首先我们看下 PSR0

我写代码实现了它的自动加载器

这是加载器代码

<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2018/4/14
 * Time: 22:27
 */
class Psr0{
    /* psr-0 规范说明
     * 1.一个完整的标准的类文件格式是这样的 \vendor\namespace\class
     * 2.每个命名空间必须有一个顶级命名空间 子命名空间可以有多个 或者没有
     * 3.加载文件的时候 命名空间分隔符会被转换为 DIRECTORY_SEPARATOR
     * 4.类名如果带'_',都会转换为DIRECTORY_SEPARATOR,_的拼接部分必须是类目录的子目录部分并且是一一对应
     * 打个比方 Tik_Tb_Order.php  其中 Tik和Tb必须是该类文件所在的子目录。
     * 5.加载的文件后缀必须以.php结尾
     * 6.verdor namespace class必须由大小写字母组合而成
     */
    public static function autoload($className)
    {
        //去掉最左边的\
        $className = ltrim($className,'\\');
        //获取命名空间
        $position = strrpos($className,'\\');
        $strnamespaces = substr($className,0,$position);
        //获取类名
        $class = substr($className,$position+1);
        $namespaces = explode('\\',$strnamespaces);
        //组装类路径。。psr0规定 命名空间分隔符要被DIRECTORY_SEPARATOR替换。
        $file_path = '';
        foreach ($namespaces as $namespace)
        {
            $file_path .= $namespace.DIRECTORY_SEPARATOR;
        }
        //类名下划线处理
        $class_file = str_replace('_',DIRECTORY_SEPARATOR,$class);
        //最终的file
        $file = './vendor/'.$file_path.$class_file.'.php';
        include $file;
    }
}

然后我的文件目录


我的调用代码 是 start.php

<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2018/4/14
 * Time: 22:22
 */
header("Content-type: text/html; charset=utf-8");
require 'Psr0.php';
//定义类的自动加载器
spl_autoload_register('Psr0::autoload');
//测试
$three = new \Com\Three();
$one = new \Com\One\One();
$two = new \Com\One\Fki_Tb_Two();
$three->sh();
$one->sh();
$two->sh();

这是运行效果



看懂了没 没的话 我带大家分析一波。

1.首先 psr0规定 必须有个组织名 我这里是vendor 然后得有个顶级命名空间 我这里是Com

打开我的three.php

<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2018/4/14
 * Time: 22:49
 */
namespace Com;

class Three{
    public function sh()
    {
        echo '我是three'.'<br>';
    }
}

我这个类就在当前顶级命名空间下 且没有子命名空间 当然也可以被加载到。


再看one.php

<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2018/4/15
 * Time: 10:30
 */
namespace Com\One;
class One{
    public function sh()
    {
        echo '我是one'.'<br>';
    }
}

我这里是设置了子命名空间 为One 根据psr0规范。子命名空间必须是类的文件系统加载的子目录。所以也能通过。

我们再看fki_Tb_two.php

<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2018/4/15
 * Time: 10:34
 */
namespace Com\One;

class Fki_Tb_Two{
    public function sh(){
        echo '我是tow'.'<br>';
    }
}

这个类名带下划线 psr0规范说  下划线最后的一部分才是类名。其余部分充当该类被加载的子目录部分,并且和子目录名字母大小一一对应 在本例中。根据我上面目录结构图,它是这个类的2个上一级目录名。所以也能被加载。


我们再看psr4

我先贴出加载器代码

<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2018/4/14
 * Time: 22:27
 */
//单命名空间
class Psr4_1{
    public static function autoload($className)
    {
        /*
         * Psr-4规范说明
         * 1.合法的类的完全限定名格式是 \namespacename\subnamespace\class
         * 2.必须有一个顶级的命名空间
         * 3.必须有一个终止类名
         * 4.下划线在类中无特殊意义
         * 5.子命名空间必须对于从文件系统载入类文件的一个子目录
         * 6.子目录和子命名空间必须大小写和字母一一对应。子命名空间分割符表示子目录分隔符
         * 7.
         */



        //设置命名空间目录映射
        $namespace_prefix='Lib1\\Red1';
        $base_dir='./Lib1/Red';

        //去掉最左边的\
        $className = ltrim($className,'\\');
        //获取最右边的分隔符位置 用来做命名空间和class的分界点
        $position = strrpos($className,'\\');
        //获取命名空间部分
        $strnamespaces = substr($className,0,$position);
        //判断前缀是否存在 。0表示是合法的,false直接返回错误
        $is_exsists = strpos($strnamespaces,$namespace_prefix);
        if($is_exsists!==0)
        {
            return;
        }
        //获取子命名空间部分
        $Len = strlen($namespace_prefix);
        $str_sub_namespace = substr($strnamespaces,$Len);
        $str_sub_namespace_path = str_replace('\\',DIRECTORY_SEPARATOR,$str_sub_namespace);
        //获取类名
        $class = substr($className,$position+1);

        //拼接目录
        $file = $base_dir.$str_sub_namespace_path.DIRECTORY_SEPARATOR.$class.'.php';
        include $file;

    }
}

在贴出调用代码

<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2018/4/14
 * Time: 22:22
 */
header("Content-type: text/html; charset=utf-8");
require 'Psr4_1.php';
//定义类的自动加载器
spl_autoload_register('Psr4_1::autoload');

$red = new \Lib1\Red1\Red();

$green = new \Lib1\Red1\Sub\Green();

$red_green = new \Lib1\Red1\Red_Green();

$red->sh();

$green->sh();

$red_green->sh();

效果图



再是目录结构图



我分析一下吧。

首先 psr4是要求命名空间前缀和base_dir有个设置好的对应关系。

我这个psr4_1是一个单命名空间的加载器。稍后我发一个批量的。先拿这个单的说明

我们打开red.php

<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2018/4/15
 * Time: 14:05
 */
namespace Lib1\Red1;

class Red{
    public function sh(){
        echo '我是red<br>';
    }
}


这里 我们的命名空间 符合之前预设置好的映射关系。使用能被加载。


再看一个带子命名空间的例子

green.php

<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2018/4/15
 * Time: 15:24
 */
namespace Lib1\Red1\Sub;

class Green{
    public function sh(){
        echo '我是green<br>';
    }
}

这里的子命名空间的意思 就是他不在预设值的命名空间前缀里面 也就是没包含他。

那么要让加载器加载到他  我们必须把这个Sub也就是子命名空间 对应一个子目录。也就是在设置好的那个映射关系basedir后面添加一个子目录  要求字母大小一一对应即可。

详情请看我的目录结构图 

psr4规范说明 类名的下划线格式 并没有实际的意义..这个和psr0是区别的。

看red_green.php

<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2018/4/15
 * Time: 14:05
 */
namespace Lib1\Red1;

class Red_Green{
    public function sh(){
        echo '我是red_green<br>';
    }
}

把这个当成是一个普通的类文件进行加载

明白这个原理后  我贴出多命名空间支持的psr4加载器 也就是我的psr4_2.php

<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2018/4/14
 * Time: 22:27
 */
//支持多命名空间部署
class Psr4_2{
    private static $_classMapper = '';
    public static function addClassMapper($prefix,$dir)
    {
        self::$_classMapper[$prefix] = $dir;
    }
    public static function autoload($className)
    {
        //去掉最左边的\
        $className = ltrim($className,'\\');
        //获取最右边的分隔符位置 用来做命名空间和class的分界点
        $position = strrpos($className,'\\');
        //获取命名空间部分
        $strnamespaces = substr($className,0,$position);

        //判断前缀是否存在 。0表示是合法的,false直接返回错误
        foreach (self::$_classMapper as $mapperkey=> $classmapper)
        {
            $namespace_prefix = $mapperkey;

            $is_exsists = strpos($strnamespaces,$namespace_prefix);

            if($is_exsists!==false)
            {
                //获取子命名空间部分
                $Len = strlen($namespace_prefix);
                $str_sub_namespace = substr($strnamespaces,$Len);
                $str_sub_namespace_path = str_replace('\\',DIRECTORY_SEPARATOR,$str_sub_namespace);
                //获取类名
                $class = substr($className,$position+1);
                //拼接目录
                $base_dir = self::$_classMapper[$namespace_prefix];
                $file = $base_dir.$str_sub_namespace_path.DIRECTORY_SEPARATOR.$class.'.php';
                include $file;
                return;
            }
        }
    }
}

我在贴出我的调用代码

<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2018/4/14
 * Time: 22:22
 */
header("Content-type: text/html; charset=utf-8");
require 'Psr4_2.php';
spl_autoload_register('Psr4_2::autoload');
//定义类的自动加载器
Psr4_2::addClassMapper('Lib1\Red1','./Lib1/Red');
Psr4_2::addClassMapper('Lib2','./Lib2');
Psr4_2::addClassMapper('Lib3\Hei','./lib3/hei/src');

$red = new \Lib1\Red1\Red();
$red->sh();

$green = new \Lib1\Red1\Sub\Green();
$green->sh();

$red_green = new \Lib1\Red1\Red_Green();
$red_green->sh();

$bule = new \Lib2\Bule();
$bule->sh();

$ming = new \Lib1\Red1\sub\Subb\Ming();
$ming->sh();

$fi_cc = new \Lib2\Fi_CC();
$fi_cc->sh();

$hei = new \Lib3\Hei\Hei();
$hei->sh();

$req = new \Lib3\Hei\Req\Req();
$req->sh();

效果图




抽几个具有代表性的分析一下 给大家看。

看我的req.php


文件代码

<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2018/4/15
 * Time: 17:56
 */

namespace Lib3\Hei\Req;

class Req{
    public function sh(){
        echo '我是req<br>';
    }
}

也是存在一个子命名空间的问题 我们只要指定对应的子目录就能实现对他的加载。

总结一下  :根据psr4的自动加载规则 我们可以根据配置好的命名空间前缀。快速定位类的文件位置。还有一点,为了开发的便捷性 我们可以配置多个命名空间前缀 ,使类文件避免深度索引。


查看评论

Yii2中的代码自动加载机制

Yii2中的代码自动加载机制yii php Darkgel 2017年08月04日发布赞  |   1收藏  |  2805 次浏览1.基本知识Include与require 的作用:当一个文件被包含...
  • u010412301
  • u010412301
  • 2018-04-15 18:37:44
  • 11

PHP PSR-4与PSR-0规范

在上一篇文章中,介绍了PSR-0和autoload相关的内容。继PSR-0这个PHP自动加载的规范之后,PHP-FIG又推出了一个PSR-4,称为改进的autoload规范。 在此不详谈两者的定义了。...
  • baizhebz
  • baizhebz
  • 2014-08-16 22:35:10
  • 12479

PSR-4与PSR-0的区别

FIG制定的PHP规范,简称PSR,是PHP开发的事实标准。PSR原本有四个规范,分别是:PSR-0 自动加载 PSR-1 基本代码规范 PSR-2 代码样式 PSR-3 日志接口 2013年底,新出...
  • soonfly
  • soonfly
  • 2016-11-02 16:03:50
  • 2913

「PSR 规范」PSR-4 自动加载规范

1. 概述 本 PSR 是关于由文件路径 自动载入 对应类的相关规范, 本规范是可互操作的,可以作为任一自动载入规范的补充,其中包括 PSR-0,此外, 本 PSR 还包括自动载入的类对应的文件...
  • TAKUNHA
  • TAKUNHA
  • 2016-09-05 17:12:09
  • 1265

PHP中PSR-[0-4]规范

PHP中PSR-0,PSR-1,PSR-2,PSR-3,PSR-4规范的用法以及使用。
  • yangyi2083334
  • yangyi2083334
  • 2015-01-18 22:02:30
  • 5777

PSR-0 自动加载标准

从2014-10-21日起,PSR-0标准被标记为废弃。推荐使用PSR-4标准作为替换。 加载器(autoloader)必须遵循以下所下要求。要求: 一个完整的命名空间或类必须拥有以下结构 \\(\)...
  • bigsunnyside
  • bigsunnyside
  • 2016-03-31 22:42:25
  • 441

psr-0 和 psr-4的区别

来源:https://segmentfault.com/a/1190000006686978composer同时兼容这两种规范, 但是这两者有什么区别呢?比如说在composer.json中我这样定义...
  • maquealone
  • maquealone
  • 2018-03-22 10:41:47
  • 9

PSR-0与PSR-4

PHP自动加载的背景  在开发过程中,我们如果想引入外部的class,通常都会使用require或者include方法,其实这个在小规模的开发中没多大问题,但是万一开发规模大了起来,那样子就会有很多r...
  • m0_37752084
  • m0_37752084
  • 2018-03-18 10:20:44
  • 7

Composer初探

1、简介Composer 是PHP的一个包依赖管理工具,类似Ruby中的RubyGems或者Node中的NPM,它并非官方,但现在已经非常流行。 Framework Interoperability...
  • fationyyk
  • fationyyk
  • 2016-03-17 10:18:27
  • 1105

PHPStorm 添加支持 PSR-4 命名空间前缀设置

许久没有更新博客啦, 太忙了, 七月这最后一天来写点自己在使用 PHPStorm 上的小却很有用的功能吧.PHPStorm 默认是使用 PSR-0 命名空间规范的, 前提是你需要标记好项目中的源码根目...
  • zsjangel
  • zsjangel
  • 2017-07-31 12:33:05
  • 1681
    个人资料
    持之以恒
    等级:
    访问量: 2997
    积分: 588
    排名: 8万+
    最新评论