牛骨文教育服务平台(让学习变的简单)
博文笔记

thinkphp3.2源码 ----- 自动加载

创建时间:2017-06-02 投稿人: 浏览次数:851
  • 知识预备

    • 命名空间
    • __autoload的工作机制
  • 写在前面
    tp3.2实现的自动加载可以通过命名空间自动定位到类文件,实现这样的效果除了合理的处理手段外,比较有规律的项目结构也是必不可少的。

    • 源码(ThinkPHP/Library/Think/Think.class.php):
      • 注册自动加载函数
public static function start()
    {
        // 注册AUTOLOAD方法
        spl_autoload_register("ThinkThink::autoload");
.............................
  • autoload方法:
public static function autoload($class)
    {
        // 检查是否存在映射
        if (isset(self::$_map[$class])) {
            include self::$_map[$class];
        } elseif (false !== strpos($class, "\")) {
            $name = strstr($class, "\", true);
            if (in_array($name, array("Think", "Org", "Behavior", "Com", "Vendor")) || is_dir(LIB_PATH . $name)) {
                // Library目录下面的命名空间自动定位
                $path = LIB_PATH;
            } else {
                // 检测自定义命名空间 否则就以模块为命名空间
                $namespace = C("AUTOLOAD_NAMESPACE");
                $path      = isset($namespace[$name]) ? dirname($namespace[$name]) . "/" : APP_PATH;
            }
            $filename = $path . str_replace("\", "/", $class) . EXT;
            if (is_file($filename)) {
                // Win环境下面严格区分大小写
                if (IS_WIN && false === strpos(str_replace("/", "\", realpath($filename)), $class . EXT)) {
                    return;
                }
                include $filename;
            }
        } elseif (!C("APP_USE_NAMESPACE")) {
            // 自动加载的类库层
            foreach (explode(",", C("APP_AUTOLOAD_LAYER")) as $layer) {
                if (substr($class, -strlen($layer)) == $layer) {
                    if (require_cache(MODULE_PATH . $layer . "/" . $class . EXT)) {
                        return;
                    }
                }
            }
            // 根据自动加载路径设置进行尝试搜索
            foreach (explode(",", C("APP_AUTOLOAD_PATH")) as $path) {
                if (import($path . "." . $class))
                // 如果加载类成功则返回
                {
                    return;
                }

            }
        }
    }
  • 自动加载:
    tp通过spl_aotuload_register()注册atuoload()方法,并且将当前引用类的类名传入:
    • 类库映射:
      tp支持给类文件别名,实现通过这个别名去映射这个类文件的路径,例如:
      ThinkThink::addMap("ThinkLog",THINK_PATH."ThinkLog.php");
     ThinkThink::addMap("OrgUtilArray",THINK_PATH."OrgUtilArray.php");

addMap():

 // 注册classmap
    public static function addMap($class, $map = "")
    {
        if (is_array($class)) {
            self::$_map = array_merge(self::$_map, $class);
        } else {
            self::$_map[$class] = $map;
        }
    }

我们可以看到,执行addMap()就会给这个类的私有属性$_map[$class]添加成员,而且从autoload()方法代码执行顺序来看,是先加载类库映射的。

  • 获取类文件所属目录
    如果不存在类库映射,tp将自己去搜索这个类的路径,首先我们要知道这个常量 LIB_PATH,这个常理是在框架的入口文件中定义的(ThinkPHP/ThinkPHP.php) :
defined("LIB_PATH") or define("LIB_PATH", realpath(THINK_PATH . "Library") . "/"); // 系统核心类库目录

tp的自动加载就是以这个常量所定义的路径作为参照点的,这个常量里面又包含另外一个THINK_PATH常量,这个常量是这样定义的

defined("THINK_PATH") or define("THINK_PATH", __DIR__ . "/");

这样LIB_PATH这个常量就是指的你的框架Library目录下面了。明白这个常量的含义后我们在回到tp的autoload()这个方法中:首先判断传入的类名有没有’’(注意,第一个’’是转义符),如果有,获取‘’前面的字符,判断是不是Library下面目录,如果是,这个类文件的目录就是LIB_PATH这个常量所指的目录。现在有一个疑问?为什么传入的类名‘/’前面的字符可以实现路径的查找呢?其实这就和tp命名空间的规范很有关系了,因为在tp中,命名空间的名字和当前文件的所在相对目录是一致的,打个比方:
比如说现在的Think.class.php的目录是在 :“ThinkPHP/Library/Think/”下面,那么这个类所在的命名空间就是“Think”,假如我们尝试在Home/Index/index实例一个图像类,并且在自动加载函数里打印tp自动加载的类库,首先实例化图像类

这里写图片描述

我们知道,new Image()是Think命名空间下的,这个名称是非限定名称,编译是相当于是 new ThinkImage() ,我们可以在加载方法中打印来看:

     // 检查是否存在映射
        if (isset(self::$_map[$class])) {
            include self::$_map[$class];
        } elseif (false !== strpos($class, "\")) {
            echo $class."<hr/>";   //打印加载类文件
            $name = strstr($class, "\", true);
            if (in_array($name, array("Think", "Org", "Behavior", "Com", "Vendor")) || is_dir(LIB_PATH . $name)) {
           ........................

打印已自动加载类
传过来的Think/Image这个类 ‘/’ 的前面的命名空间其实就是当前类所在的相对位置,所有tp通过这个命名空间来定位(命名空间根空间和目录一致)。
源码读到下面我就有点懵逼了……..

 if (in_array($name, array("Think", "Org", "Behavior", "Com", "Vendor")) || is_dir(LIB_PATH . $name)) {
                // Library目录下面的命名空间自动定位
                $path = LIB_PATH;
            } else {
                // 检测自定义命名空间 否则就以模块为命名空间
                $namespace = C("AUTOLOAD_NAMESPACE");
                $path      = isset($namespace[$name]) ? dirname($namespace[$name]) . "/" : APP_PATH;
            }

这个

 $namespace = C("AUTOLOAD_NAMESPACE");

我实在没找到是在哪里配置的,换句话讲,跑到Library目录以外怎么去自动加载就不知道了,先留个坑在这里以后来填,有知道的博友希望能够不吝赐教,感激不尽!

(残次文还是发表了吧………)

声明:该文观点仅代表作者本人,牛骨文系教育信息发布平台,牛骨文仅提供信息存储空间服务。