课程咨询 :17156168575 QQ:2720475033

  • thinkphp5 源码之自动加载

    发布:济南php培训      来源:济南php培训      时间:2016-09-08

  • 入口

    作为单入口框架,就从入口文件看起,按照tp5文档所示的规范,入口文件应该是放在public/ 下。

    那么为什么大多数要把入口放到子文件夹下呢?这是一个小技巧。

    第一为了动静分离,因为现在的php框架一般都是单入口,既然是单入口,那么必然要做rewrite,如果把静态文件和程序文件放到一起。框架路由势必要对每一个请求进行筛选。所以这些框架不约而同的把资源文件和程序文件区分开来,放在了不同的文件夹下。从整体来看,也就是为什么入口会在子目录下了。

    第二是为了安全,linux下的权限划分非常严格,分别分为读、写、执行,在这个基础上又分为文件所有组、所在组、其他组。这样划分可以更好的对文件权限进行梳理,避免上传漏洞(用户上传php文件被执行)等等。

    打开public/index.php

    define('APP_PATH', __DIR__ . '/../application/');

    // 加载框架引导文件

    require __DIR__ . '/../thinkphp/start.php';

    只有两行代码,定义 APP_PATH,加载 '/../thinkphp/start.php'。APP_PATH 可以自己修改。

    然后打开 /../thinkphp/start.php

    namespace think;

    // ThinkPHP 引导文件

    // 加载基础文件

    require __DIR__ . '/base.php';

    // 执行应用

    App::run()->send();

    也只有三行代码,定义命名空间,加载基础文件,启动应用。这里注意一下命名空间,所有thinkphp类都在think及其子命名空间下。程序中用到框架类的时候要先use 该类的命名空间;

    环境配置

    然后我们打开base.php

    12-31行定义了一坨常量。注意里面 defined('THINK_PATH') or define('THINK_PATH', __DIR__ . DS); 这种定义方式,先判断是否存在,如果不存在则定义。也就是说我们可以在这行代码之前(一般在index.php中)执行定义这个常量,而不会被覆盖。

    36-37行

    // 载入Loader类

    require CORE_PATH . 'Loader.php';

    载入Loader类,这个类比较重要,实现了自动加载。

    39-51行

    // 加载环境变量配置文件

    if (is_file(ROOT_PATH . 'env' . EXT)) {

    $env = include ROOT_PATH . 'env' . EXT;

    foreach ($env as $key => $val) {

    $name = ENV_PREFIX . strtoupper($key);

    if (is_bool($val)) {

    $val = $val ? 1 : 0;

    } elseif (!is_scalar($val)) {

    continue;

    }

    putenv("$name=$val");

    }

    }

    加载环境变量配置文件,可能很多同学不理解是干什么用的。

    我们假设一个场景,你在公司和家里开发程序,在内网服务器上进行测试,在外网服务器上部署,所有的配置不能可能全部相同(比如数据库帐号密码、文件路径等等)。总不能每次都改配置吧?如果做负载、有几十个服务器怎么部署?总不能都用ftp上传,然后改配置吧?

    所以现在主流的做法就是区分环境(开发环境、测试环境、生产环境),然后程序自动加载不同的配置。但是通过什么区分呢?方法有很多,但是大多数都是选择通过环境变量来区分,然后加载对应的配置文件。然后使用 git 做版本控制,然后在服务器部署同步脚本,通过 git push钩子进行代码同步,以达到自动化部署的模式。当然也还有其他方式,但是大多都类似。

    自动加载

    为什么要使用自动加载呢?因为像java、C等编译型语言在编译过程中会把程序中引用的库、包等等自动引入进来。但是php是脚本行语言啊,没有编译过程,怎么办呢?最早期的程序都是手动引入,比如早期的xxshop、xxcms,都是写一坨require、include。又搓又不方便,对于世界上最好的语言来说这样多丢面啊,所以我们需要用自动加载让我们最好的语言看起来更有B格(至于某些性能论的同学会说自动加载影响性能啊之类的,请用汇编!)。

    我们继续看base.php 的 54行\think\Loader::register();

    注册自动加载,从这一行之后就可以使用符合自动加载规范的任何类了。

    比如56-60行,虽然没有加载对应的文件,但是通过自动加载就可以直接使用。

    // 注册错误和异常处理机制

    \think\Error::register();

    // 加载惯例配置文件

    \think\Config::set(include THINK_PATH . 'convention' . EXT);

    接下来我们看一下自动加载的实现方法。打开Loader.php,按照上面的执行顺序,先看Loader类的register方法

    核心是

    spl_autoload_register($autoload ?: 'think\\Loader::autoload', true, true);

    // 注册命名空间定义

    self::addNamespace([

    'think' => LIB_PATH . 'think' . DS,

    'behavior' => LIB_PATH . 'behavior' . DS,

    'traits' => LIB_PATH . 'traits' . DS,

    ]);

    // 加载类库映射文件

    if (is_file(RUNTIME_PATH . 'classmap' . EXT)) {

    self::addClassMap(__include_file(RUNTIME_PATH . 'classmap' . EXT));

    }

    spl_autoload_register方法可能很多同学都有了解,在我们实例化一个当前已加载文件中不存在的类后(比如在a.php中new一个类,会先在a.php和已加载的文件中找),会执行此方法指定的函数,并把类名传递进去。在这个函数中如果能正确加载到该文件,那么也可以实例化成功,并不会报错。所以借助此函数可以达到自动加载。

    首先我们知道当 new 一个不存在的类时,如果使用spl_autoload_register定义了一个处理函数,那么这个函数可以获得一个参数,参数名是new 的类名。比如从前面base.php中我们看到 \think\Error::register();使用think命名空间下的Error类的register静态方法,但是我们并没有引入这个文件。于是我们可以在spl_autoload_register注册的函数中得到一个参数thinkError,如果我们的命名空间按照文件夹格式的方法命名(这也是推荐的、常用的命名方式),那么就可以通过该参数来加载对应的文件,但是如果特殊情况下没有按照文件夹的格式来进行命名空间的命名,那么就需要手动指定映射关系。self::addClassMap(__include_file(RUNTIME_PATH . 'classmap' . EXT)); 加载了映射文件。然后我们看spl_autoload_register中指定的函数:autoload。

    这个不用详细解释了,先处理由 addNamespace 设定的命名空间别名,然后通过 findFile 来处理映射关系,得到真实的路径,并加载文件。

    而__autoload()函数具有类似的功能。但是为什么用的很少呢?因为 __autoload()只能指定一个函数,而spl_autoload_register可以注册多个函数来处理这个逻辑。一旦业务复杂 __autoload()就完全不能胜任。比如我们继续看Loader类的register方法下面的内容

    // Composer自动加载支持

    if (is_dir(VENDOR_PATH . 'composer')) {

    self::registerComposerLoader();

    }

    // 自动加载extend目录

    self::$fallbackDirsPsr4[] = rtrim(EXTEND_PATH, DS);

    一个框架是否好用,很大程度取决于它的扩展能力。所以自动加载除了要处理自身类库的加载、还要处理扩展类库的自动加载。tp5支持使用两种方式来扩展类库一种是Composer,一种是手动放入 extend 目录。

    Composer不用多说,就像npm之于nodejs、yum之于Centos、apt-get之于、Ubuntu。一个php的包管理工具。Composer有一套自己的自动加载机制,tp5这里只不过是调用了Composer自己的注册自动加载函数的方法。有兴趣的同学可以看一下registerComposerLoader方法,以及vendor/composer下几个autoload开头的文件。原理基本上和上面的一致。

    Loader.php中核心方法已经介绍完毕,其他的几个方法有处理规范的方法(PSR-0,PSR-2,PSR-4,有兴趣的同学可以谷歌、百度了解一下)、和根据类名处理模型和控制器的方法,这点需要看完路由才能详解。

    达内济南php培训的小编将会不定期的为大家分享一些关于达内济南php培训中心的优惠活动、课堂知识点总结、最新php资讯以及学员就业感想!

上一篇:PHP的autoload自动加载机制详解

下一篇:【转】2016年程序员就业形势分析报告

最新开班日期  |  更多

PHP高级开发工程师就业班

PHP高级开发工程师就业班

开班日期:11月30日

PHP高级开发工程师周末班

PHP高级开发工程师周末班

开班日期:11月30日

济南PHP培训班就业班

济南PHP培训班就业班

开班日期:11月30日

PHP高级开发工程师周末班

PHP高级开发工程师周末班

开班日期:11月30日

 扫一扫,关注一下! 济南:历下区山大路47号数码港大厦 济南:历下区趵突泉北路三联商社
青岛:市南区金坛路17号 潍坊:奎文区东风东街299号建行大厦
烟台:海港路25号阳光100城市广场 临沂:兰山区红旗路1号苏宁易购
淄博:张店区金晶大道68号华润大厦 济宁:市中区太白路10号苏宁生活广场
课程培训电话:17156168575 QQ:2720475033 全国服务监督电话:400-111-8989    服务邮箱 tousu@tedu.cn

2001-2017 达内时代科技集团有限公司 版权所有 京ICP证8000853号-56