架构总览

优质
小牛编辑
164浏览
2023-12-01

ThinkPHP支持传统的MVC(Model-View-Controller)模式以及流行的MVVM(Model-View-ViewModel)模式的应用开发,但无论采用何种模式,URL的规范仍然是统一的。

5.1的URL访问受路由决定,如果在没有定义或匹配路由的情况下(并且没有开启强制路由模式的话),则是基于:

http://serverName/index.php(或者其它入口文件)/模块/控制器/操作/参数/值…

下面的一些概念有必要做下了解,可能在后面的内容中经常会被提及。

入口文件

用户请求的PHP文件,负责处理一个请求(注意,不一定是URL请求)的生命周期,最常见的入口文件就是index.php,有时候也会为了某些特殊的需求而增加新的入口文件,例如给后台模块单独设置的一个入口文件admin.php或者一个命令行程序入口think都属于入口文件。

应用

应用在ThinkPHP中是一个管理系统架构及生命周期的对象,由系统的 \think\App类完成,应用通常在入口文件中被调用和执行,具有相同的应用目录的应用我们认为是同一个应用,但一个应用可能存在多个入口文件(绑定不同的模块或者使用不同的配置)。

应用具有自己独立的配置文件、公共(函数)文件和路由定义文件。

路由

路由是用于规划(一般同时也会进行简化)请求的访问地址,在访问地址和实际操作方法之间建立一个路由规则 => 路由地址的映射关系。

ThinkPHP并非强制使用路由,如果没有定义路由,则可以直接使用“模块/控制器/操作”的方式访问,如果定义了路由,则该路由对应的路由地址就被不能直接访问了。一旦开启强制路由参数,则必须为每个请求定义路由(包括首页)。

使用路由有一定的性能损失,但随之也更加安全,因为每个路由都有自己的生效条件,如果不满足条件的请求是被过滤的。你远比你在控制器的操作中进行各种判断要实用的多。

其实路由的作用远非URL规范这么简单,还可以实现验证、权限、参数绑定及响应设置等功能。

模块

一个典型的应用是由多个模块组成的,这些模块通常都是应用目录下面的一个子目录,每个模块都有自己独立的配置文件、公共文件和类库文件。

支持单一模块架构设计,如果你的应用下面只有一个模块,那么通过配置这个模块的子目录可以省略(同时应用类库的命名空间也随之简化)。

控制器

每个模块拥有独立的类库及配置文件,一个模块下面有多个控制器负责响应请求,而每个控制器其实就是一个独立的控制器类。

控制器主要负责请求的接收,并调用相关的模型处理,并最终通过视图输出。严格来说,控制器不应该过多的介入业务逻辑处理。

事实上,控制器是可以被跳过的,通过路由我们可以直接把请求调度到某个模型或者其他的类进行处理。

ThinkPHP的控制器类比较灵活,可以无需继承任何基础类库。

一个典型的Index控制器类如下:

namespace app\index\controller;

class Index 
{
    public function index()
    {
        return 'hello,thinkphp!';
    }
}

继承系统控制器think\Controller的话,可以使用内置的功能,享受更多的便利。

操作

一个控制器包含多个操作(方法),操作方法是一个URL访问的最小单元。

下面是一个典型的Index控制器的操作方法定义,包含了两个操作方法:

namespace app\index\controller;

class Index 
{
    public function index()
    {
        return 'index';
    }

    public function hello($name)
    {
        return 'Hello,'.$name;
    }
}

操作方法可以不使用任何参数,如果定义了一个非可选参数,则该参数必须通过用户请求传入,如果是URL请求,则通常是通过当前的请求传入。

模型

模型类通常完成实际的业务逻辑和数据封装,并返回和格式无关的数据。

模型类并不一定要访问数据库,而且在5.1的架构设计中,只有进行实际的数据库查询操作的时候,才会进行数据库的连接,是真正的惰性连接。

ThinkPHP的模型层支持多层设计,你可以对模型层进行更细化的设计和分工,例如把模型层分为逻辑层/服务层/事件层等等。

视图

控制器调用模型类后,返回的数据通过视图组装成不同格式的输出。视图根据不同的需求,来决定调用模板引擎进行内容解析后输出还是直接输出。

视图通常会有一系列的模板文件对应不同的控制器和操作方法,并且支持动态设置模板目录。

模板引擎

模板文件中可以使用一些特殊的模板标签,这些标签的解析通常由模板引擎负责实现。

ThinkPHP内置了一个基于XML解析的编译型模板引擎,可以很方便的实现模板输出和控制。

同时也可以支持第三方的模板引擎扩展。

驱动

系统很多的组件都采用驱动式设计,从而可以更灵活的扩展,驱动类的位置默认是放入核心类库目录下面,也可以重新定义驱动类库的命名空间而改变驱动的文件位置。

5.1版本的驱动更多是采用Composer的方式安装和管理。

行为

行为(Behavior)是在预先定义好的一个应用钩子(Hook)位置执行的一些操作。类似于AOP编程中的“切面”的概念,给某一个钩子绑定相关行为就成了一种类AOP编程的思想。

所以,行为通常是和某个Hook位置相关,行为的执行时间取决于行为绑定到了哪个位置上。

要执行行为,首先要在应用程序中进行行为侦听,例如:

// 在app_init位置侦听行为
\think\facade\Hook::listen('app_init');

然后对某个位置进行行为绑定:

// 绑定行为到app_init位置
\think\facade\Hook::add('app_init','\app\index\behavior\Test');

一个位置上如果绑定了多个行为的,按照绑定的顺序依次执行,除非遇到中断。

中间件

中间件主要用于HTTP请求的拦截处理

事件

ThinkPHP5中的事件一般是指数据库操作和模型操作在完成数据写入之后的回调机制。

数据库操作的回调也称为查询事件,是针对数据库的CURD操作而设计的回调方法,主要包括:

事件描述
before_selectselect查询前回调
before_findfind查询前回调
after_insertinsert操作成功后回调
after_updateupdate操作成功后回调
after_deletedelete操作成功后回调

模型事件可以看成是模型层的钩子和行为,只不过钩子的位置主要针对模型数据的写入操作,包含下面这些:

钩子对应操作快捷注册方法
before_insert新增前beforeInsert
after_insert新增后afterInsert
before_update更新前beforeUpdate
after_update更新后afterUpdate
before_write写入前beforeWrite
after_write写入后afterWrite
before_delete删除前beforeDelete
after_delete删除后afterDelete

before_writeafter_write表示无论是新增还是更新都会执行的钩子。

助手函数

系统为一些常用的操作提供了助手函数支持,但核心框架本身并不依赖任何助手函数。使用助手函数和性能并无直接影响,只是某些时候无法享受IDE自动提醒的便利,但是否使用助手函数看项目自身规范,在应用的公共函数文件中也可以对系统提供的助手函数进行重写。