类自动加载:
在使用类的时候会用 require
或 include
将类加载进来,但是每次都手动加载,会导致一系列问题,比如类重复加载,管理困难等。所以解决上述问题,我们使用单独的一个文件来完成所有类的自动加载。
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
开头的方法都会被依次调用。
下期见
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。