PHP 手写MVC(二) —— 自动加载和Bootstrap

entner

类自动加载:

在使用类的时候会用 requireinclude 将类加载进来,但是每次都手动加载,会导致一系列问题,比如类重复加载,管理困难等。所以解决上述问题,我们使用单独的一个文件来完成所有类的自动加载。

spl_autoload_register()

该函数可以注册任意数量的自动加载器,当使用尚未被定义的类(class)和接口(interface)时自动去加载。通过注册自动加载器,脚本引擎在 PHP 出错失败前有了最后一个机会加载所需的类。

尽管 __autoload() 函数也能自动加载类和接口,但更建议使用 spl_autoload_register() 函数。 spl_autoload_register() 提供了一种更加灵活的方式来实现类的自动加载(同一个应用中,可以支持任意数量的加载器,比如第三方库中的)。因此,不再建议使用 __autoload() 函数,在以后的版本中它可能被弃用。

core 目录下创建一个 Autoload.php 文件

$ cd tinyphp/core
$ touch Autoload.php

编辑 Autoload.php


// 命名空间和文件目录映射
<?php
$path = [
    'core' => 'core',
    'app' => 'app',
    'controller' => 'app/controllers',
    'model' => 'app/models',
    'view' => 'app/views',
    'dispatcher' => 'core/dispatcher',
];

/**
 * 参数 $class 表示自动加载的类名
 * 
 * 匿名函数中使用 use 可以使用外部变量
 */
spl_autoload_register(function($class) use ($path) {

    //解析类名,如果使用了命名空间,则会查找 $path 中对于的路径【这个地方强烈建议打印一下,自动加载就很清晰了】
    $position = strripos($class,'\\');

    $key = substr($class,0,$position);
    $value = $path[$key] ?? '';

    $file = substr($class,$position+1).'.php';

    require APP_PATH.'/'.$value.'/'.$file;
});

spl_autoload_register() 主要实现类自动加载,参数使用的是匿名函数,通过关键字 use 可以使用外部变量 $path,其作用是提供命名空间和路径的对应关系,例如使用 namespace controller,对应的类在路径 app/controllers

使用方式为,在入口文件public/index.php 中,直接 require 该文件即可。

<?php

define('APP_PATH',dirname(__DIR__));

require APP_PATH.'/core/Autoload.php';

然后我们在core 文件夹下创建一个名为 Application.php 并编辑内容

<?php

namespace core;

class Application
{
    public function run()
    {
        echo 'hello world';
    }
}

自动加载完成,待会启动框架(下文)之后就可以看到效果了。

注册启动项

有时候需要在框架启动时,额外加载一些应用配置或用户定义的方法,比如定义常量,加载自定义辅助函数等(像 Laravel 框架也会去加载 bootstrap 文件进行启动程序引导)。

我们在应用目录 app 中创建一个启动脚本 Bootstrap.php,规定该脚本内所有以 init 的方法都会被调用,并在 Application 类的构造函数中调用该脚本

创建 app/Bootstrap.php,编辑内容

<?php 

namespace app;

class Bootstrap
{
    /**
     * 所有以 init 开始的函数都会被依次调用
     *
     */
    public function initConst()
    {
        echo 'Bootstrap : 1'.PHP_EOL;
    }
    
    public function initHelper()
    {
        echo 'Bootstrap : 2'.PHP_EOL;
    }
}

Application 类中添加启动项

  • 在构造函数 __construct() 中获取 Bootstrap 实例。
  • run() 方法中遍历 Bootstrap 类中以 init 开头的方法并执行。
<?php
//这里用...省略之前的代码,在实际操作中,需要完整的代码,不然会报错
...
    public $boot;
    
    public function __construct()
    {
        /**
         * \app\为命名空间,
         * 第一个\表示在根目录,否在表示在当前命名空间下的子目录
         * 
         * 例如,app\Bootstrap() 表示在类在 tinyphp/core/app 目录下
         * \app\Bootstrap() 则表示在 tinyphp/app 目录下
         */
        $this->boot = new \app\Bootstrap();
    }
    public function run()
    {
        /**
         * 执行启动项
         * 所有init开头的方法都会被调用
         *
         */
        foreach(get_class_methods($this->boot) as $func) {
            if (0 === strpos($func, 'init')) {
                call_user_func([$this->boot,$func]);
            }
        }
    }
...

启动框架

在入口文件 index.php 中启动框架,并执行 run() 方法

<?php

use \core\Application; # 这里就是引入命名空间的起始地方

define('APP_PATH',dirname(__DIR__));

require APP_PATH.'/core/Autoload.php';

(new Application())->run();

进入 public 目录,两种方式执行 php

  • 命令行
$ php index.php
Bootstrap : 1
Bootstrap : 2
  • 使用 PHP 内置服务器
$ php -S localhost:8080

在浏览器输入 http://localhost:8080 , 结果如下

Bootstrap : 1Bootstrap : 2

从结果可以看出,Application 类在实例化的时候执行构造函数,构造函数中完成了启动项的方法调用。

三、总结

本节实现了一个简单的类自动加载和框架的核心类,在启动时加载用户启动项,在 app/Bootstrap.php 中所有以 init 开头的方法都会被依次调用。

下期见

阅读 2.3k

青年 ---------------

717 声望
177 粉丝
0 条评论

青年 ---------------

717 声望
177 粉丝
文章目录
宣传栏