文章目录
一、架构总览
ThinkPHP5.0应用基于MVC(模型-视图-控制器)的方式来组织。
- Controller(控制器)、Model(模型)、 View(视图)
5.0的URL访问受路由决定,如果
关闭路由
或者没有匹配路由
的情况下,则是基于:
http://serverName/index.php(或者其它应用入口文件)/模块/控制器/操作/参数/值…
// 是否开启路由
'url_route_on' => true
1.1、有关常用的一些概念
入口文件
程序的入口,只有通过本模块,才可以访问到,对应要访问的主程序;
在php中通过有index.php(前台应用程序入口)、还有后台程序入口(自动安装后会修改为随机名)、还有可以自定义。
应用
应用在ThinkPHP
中是一个管理系统架构及生命周期的对象,由系统中的thinkphp/library/think/App.php
完成,通常在入口文件中调用和执行。
// [ 应用入口文件 ]
// 定义应用目录
//__DIR__:当前文件所在的文件夹。
define('APP_PATH', __DIR__ . '/../application/');
// 判断是否安装
if (!is_file(APP_PATH . 'admin/command/Install/install.lock')) {
header("location:./install.php");
exit;
}
// 加载框架引导文件
require __DIR__ . '/../thinkphp/start.php';
通过入口文件(index.php)然后入口文件加载框架的启动类(start.php
)
start.php
// ThinkPHP 引导文件
// 1. 加载基础文件
require __DIR__ . '/base.php';
// 2. 执行应用
App::run()->send();
- 启动类
start.php
通过加载 框架的base.php
(基础类)、
【通过此类base.php
,通过获取系统文件位置信息,调用注册自动加载机制(Load.php
),若本地环境变量(.env)存在,若其中有内容,取出其配置信息,将key和value分别取出通过putenv设置服务器的环境变量。】
完成自动加载【Loader::register()
】
注册错误信息和异常处理【\think\Error::register();
】
加载惯例配置文件【\think\Config::set(include THINK_PATH . 'convention' . EXT);
】- 惯例配置文件:核心框架内置的配置文件。
- 启动主启动类,将信息发送到客户端页面;
应用具有自己独立的配置文件、公共(函数)文件。
模块
一个典型的应用是由多个模块组成的,这些模块通常都是应用目录下面的一个子目录,每个模块都有自己独立的配置文件、公共文件和类库文件。
// 是否支持多模块
'app_multi_module' => true
控制器
- 每个模块拥有独立的MVC类库及配置文件,一个模块下面有多个
控制器负责响应请求
,而每个控制器其实就是一个独立的控制器类。 - 控制器主要负责请求的接收,并调用相关的模型处理,并最终通过视图输出。
- 严格来说,控制器不应该过多的介入业务逻辑处理。
事实上,5.0中控制器是可以被跳过的,通过路由我们可以直接把请求调度到某个模型或者其他的类进行处理。
5.0的控制器类比较灵活,可以无需继承任何基础类库:
namespace app\index\controller;
class Index
{
public function index()
{
return 'hello,thinkphp!';
}
}
操作
一个控制器包含多个操作(方法),操作方法是一个URL访问的最小单元。
public function hello($name)
{
return 'Hello,'.$name;
}
操作方法可以不使用任何参数,如果定义了一个非可选参数,则该参数必须通过用户请求传入,如果是URL请求,则通常是$_GET或者$_POST方式传入。
模型
和数据库交互,处理业务逻辑。
模型类并不一定要访问数据库,而且在5.0的架构设计中,只有进行实际的数据库查询操作的时候,才会进行数据库的连接,是真正的
惰性连接
。
ThinkPHP的模型层支持多层设计,你可以对模型层进行更细化的设计和分工,例如把模型层分为逻辑层/服务层/事件层等等。
视图
控制器调用模型类后返回的数据通过视图组装成不同格式的输出。视图根据不同的需求,来决定调用模板引擎进行内容解析后输出还是直接输出。
视图通常会有一系列的模板文件对应不同的控制器和操作方法,并且支持动态设置模板目录。
驱动
系统很多的组件都采用驱动式设计,从而可以更灵活的扩展,驱动类的位置默认是放入核心类库目录下面,也可以重新定义驱动类库的命名空间而改变驱动的文件位置。
行为
行为(Behavior)是在预先定义好的一个应用位置执行的一些操作。类似于AOP【AOP面向切面编程+IOC控制反转】编程中的“切面”的概念,给某一个切面绑定相关行为就成了一种类AOP编程的思想。所以,行为通常是和某个位置相关,行为的执行时间依赖于绑定到了哪个位置上。
要执行行为,首先要在应用程序中进行行为侦听,例如:
// 在app_init位置侦听行为
\think\Hook::listen('app_init');
然后对某个位置进行行为绑定:
// 绑定行为到app_init位置
\think\Hook::add('app_init','\app\index\behavior\Test');
一个位置上如果绑定了多个行为的,按照绑定的顺序依次执行,除非遇到中断。
命名空间【全限定类名】
ThinkPHP5采用了PHP的命名空间进行类库文件的设计和规划,并且符合PSR-4的自动加载规范。是为了解决路径重名出现的。
1.补充
- PSR-4
PSR 是PHP Standards Recommendation的简称,制定的代码规范,简称PSR,是代码开发的事实标准。
PSR-4使代码更加规范,能够满足面向package的自动加载,它规范了如何从文件路径自动加载类,同时规范了自动加载文件的位置
。
PSR-4 规范也描述了放置 autoload 文件。
二、生命周期
从启动应用到结束应用经历的过程。
2.1、入口文件
用户发起的请求都会经过应用的入口文件,通常是 public/index.php文件。当然,你也可以更改或者增加新的入口文件。
通常入口文件的代码都比较简单,一个普通的入口文件代码如下:
// 应用入口文件
// 定义项目路径
define('APP_PATH', __DIR__ . '/../application/');
// 加载框架引导文件
require __DIR__ . '/../thinkphp/start.php';
2.2、引导文件
接下来就是执行框架的引导文件,start.php文件就是系统默认的一个引导文件。在引导文件中,会依次执行下面操作:
- 加载系统常量定义;
- 加载环境变量定义文件;
- 注册自动加载机制;
- 注册错误和异常处理机制;
- 加载惯例配置文件;
- 执行应用;
start.php
引导文件首先会调用base.php
基础引导文件,某些特殊需求下面可能直接在入口文件中引入基础引导文件。
2.3、注册自动加载
系统会调用 Loader::register()
方法注册自动加载,在这一步完成后,所有符合规范的类库(包括Composer
依赖加载的第三方类库)都将自动加载。
- 系统的自动加载由下面主要部分组成:
// 通过base.php注册自动加载 Loader (thinkphp\library\think\Loader.php)
public static function register($autoload = null)
{
// 1、注册系统的自动加载方法 \think\Loader::autoload
spl_autoload_register($autoload ?: 'think\\Loader::autoload', true, true);
// Composer 自动加载支持
if (is_dir(VENDOR_PATH . 'composer')) {
if (PHP_VERSION_ID >= 50600 && is_file(VENDOR_PATH . 'composer' . DS . 'autoload_static.php')) {
require VENDOR_PATH . 'composer' . DS . 'autoload_static.php';
$declaredClass = get_declared_classes();
$composerClass = array_pop($declaredClass);
foreach (['prefixLengthsPsr4', 'prefixDirsPsr4', 'fallbackDirsPsr4', 'prefixesPsr0', 'fallbackDirsPsr0', 'classMap', 'files'] as $attr) {
if (property_exists($composerClass, $attr)) {
self::${$attr} = $composerClass::${$attr};
}
}
} else {
self::registerComposerLoader();
}
}
// 2、注册系统命名空间定义
self::addNamespace([
'think' => LIB_PATH . 'think' . DS,
'behavior' => LIB_PATH . 'behavior' . DS,
'traits' => LIB_PATH . 'traits' . DS,
]);
// 3、加载类库映射文件
if (is_file(RUNTIME_PATH . 'classmap' . EXT)) {
self::addClassMap(__include_file(RUNTIME_PATH . 'classmap' . EXT));
}
//4、如果存在Composer安装,则注册Composer自动加载
self::loadComposerAutoloadFiles();
// 5、注册extend(拓展类类库)目录
self::$fallbackDirsPsr4[] = rtrim(EXTEND_PATH, DS);
}
// 加载composer autofile文件
public static function loadComposerAutoloadFiles()
{
foreach (self::$files as $fileIdentifier => $file) {
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
__require_file($file);
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
}
}
}
2.4、注册错误和异常处理
// php核心框架错误处理文件 thinkphp/library/think/Error.php
public static function register()
{
error_reporting(E_ALL);
//错误处理方法 think\Error::appError
set_error_handler([__CLASS__, 'appError']);
//异常处理方法 think\Error::appException
set_exception_handler([__CLASS__, 'appException']);
//应用关闭方法 think\Error::appShutdown 拦截一些系统错误。
register_shutdown_function([__CLASS__, 'appShutdown']);
}
2.5、应用初始化
执行应用的第一步操作就是对应用进行初始化,包括:
- 加载应用(公共)配置;
- 加载扩展配置文件(由extra_config_list定义);
- 加载应用状态配置;
- 加载别名定义;
- 加载行为定义;
- 加载公共(函数)文件;
- 注册应用命名空间;
- 加载扩展函数文件(由extra_file_list定义);
- 设置默认时区;
- 加载系统语言包;
2.6、URL访问检测
应用初始化完成后,就会进行URL的访问检测,包括PATH_INFO检测和URL后缀检测。
5.0的URL访问必须是PATH_INFO方式(包括兼容方式)的URL地址,例如
http://serverName/index.php/index/index/hello/val/value
2.7、路由检测
如果开启了url_route_on参数的话,会首先进行URL的路由检测。
如果一旦检测到匹配的路由,根据定义的路由地址会注册到相应的URL调度。
- 5.0的路由地址支持如下方式:
- 路由到模块/控制器/操作;
- 路由到外部重定向地址;
- 路由到控制器方法;
- 路由到闭包函数;
- 路由到类的方法
路由地址可能会受域名绑定的影响。
如果关闭路由或者路由检测无效则进行默认的模块/控制器/操作
的分析识别。
如果在应用初始化的时候指定了应用调度方式,那么路由检测是可选的。
可以使用 \think\App::dispatch() 进行应用调度,例如:
App::dispatch([‘type’ => ‘module’, ‘module’ => ‘index/index’]);
2.8、分发请求
在完成了URL检测和路由检测之后,路由器会分发请求到对应的路由地址,这也是应用请求的生命周期中最重要的一个环节。
在这一步骤中,完成应用的业务逻辑及数据返回。
使用return返回数据
,而不是echo输出,如非必要,请不要使用exit或者die中断执行
。
直接echo输出的数据将无法进行自动转换响应输出的便利。
- 模块/控制器/操作
这是默认的分发请求机制,系统会根据URL或者路由地址来判断当前请求的模块、控制器和操作名,并自动调用相应的访问控制器类,执行操作对应的方法。
该机制下面,首先会判断当前模块
,
并进行模块的初始化操作
(和应用的初始化操作类似),
模块的配置参数
会覆盖应用的尚未生效的配置参数
。
支持模块映射、URL参数绑定到方法,以及操作绑定到类等一些功能。
- 控制器方法
和前一种方式类似,只是无需判断模块、控制器和操作,直接分发请求到一个指定的控制器类的方法,因此没有进行模块的初始化操作。
- 外部重定向
可以直接分发请求到一个外部的重定向地址,支持指定
重定向代码
,默认为301
重定向。
- 闭包函数
路由地址定义的时候可以直接采用闭包函数,完成一些相对简单的逻辑操作和输出。
- 类的方法
除了以上方式外,还支持分发请求到类的方法,包括:
静态方法: ‘blog/:id’=>‘\org\util\Blog::read’
类的方法:‘blog/:id’=>‘\app\index\controller\Blog@read’
2.9、响应输出
控制器的所有操作方法都是return返回而不是直接输出,系统会调用Response::send方法将最终的应用返回的数据输出到页面或者客户端,并自动转换成default_return_type参数配置的格式。所以,应用执行的数据输出只需要返回一个正常的PHP数据即可。
2.10、应用结束
事实上,在应用的数据响应输出之后,应用并没真正的结束,系统会在应用输出或者中断后进行日志保存写入操作。
系统的日志包括用户调试输出的和系统自动生成的日志,统一会在应用结束的时候进行写入操作。
而日志的写入操作受日志初始化的影响。
三、入口文件
thinkPHP采用
单一入口
模式进行项目部署和访问,无论完成什么功能,一个应用都有一个统一(但不一定是唯一)的入口。
入口文件定义
入口文件主要完成:
- 定义框架路径、项目路径(可选)
- 定义系统相关常量(可选)
- 载入框架入口文件(必须)
5.0默认的应用入口文件位于public/index.php
入口文件设计目的
入口文件位置的设计是为了让应用部署更安全,
public目录为web可访问目录,
其他的文件都可以放到非WEB访问目录下面。
四、模块设计
├─application 应用目录(可设置)
│ ├─common 公共模块目录(可选)
│ ├─common.php 公共函数文件
│ ├─route.php 路由配置文件
│ ├─database.php 数据库配置文件
│ ├─config.php 应用配置文件
│ ├─module1 模块1目录
│ │ ├─config.php 模块配置文件
│ │ ├─common.php 模块函数文件
│ │ ├─controller 控制器目录
│ │ ├─model 模型目录(可选)
│ │ ├─view 视图目录(可选)
│ │ └─ ... 更多类库目录
│ │
│ ├─module2 模块2目录
│ │ ├─config.php 模块配置文件
│ │ ├─common.php 模块函数文件
│ │ ├─controller 控制器目录
│ │ ├─model 模型目录(可选)
│ │ ├─view 视图目录(可选)
│ │ └─ ... 更多类库目录
遵循ThinkPHP5.0的命名规范,模块目录全部采用小写和下划线命名
模块是以app开头的,但是可以设置,通过修改config项中的app_namesapce
可以设置模块的命名空间