在构建健壮、可维护的Web应用时,除了核心的业务逻辑与数据交互,一套完善的辅助机制同样至关重要。ThinkPHP 为此提供了丰富的内置功能,涵盖从错误处理、日志记录到调试支持,以及缓存、会话、文件上传等常用组件。这些功能共同构成了应用稳定运行与高效开发的基础。本文作为 ThinkPHP 系列学习的收官篇章,将系统记录这些实用功能,重点学习异常与错误的处理机制、日志的记录与管理、调试方法,以及缓存、Session、Cookie、文件上传等核心组件的应用。掌握这些内容,将帮助我们打造出更加稳定、高效且功能完整的应用程序。
[1.4、HTTP 异常](#1.4、HTTP 异常)
一、错误和日志
1、异常处理
和PHP默认的异常处理不同,ThinkPHP 抛出的不是单纯的错误信息,而是一个人性化的错误页面。
1.1、异常显示
异常页面显示会自动判断当前的请求是否为JSON请求,如果是JSON请求则采用JSON格式输出异常信息,否则按照HTML格式输出。
在调试模式下,系统默认展示的异常页面:

只有在调试模式下才能显示具体的错误信息,如果在部署模式下,可能看到的是一个简单的提示文字,例如:

可以通过设置 config\app.php 文件中的 exception_tmpl 配置参数来自定义异常页面模板,默认的异常模板配置为:
php
// 异常页面的模板文件
'exception_tmpl' => app()->getThinkPath() . 'tpl/think_exception.tpl',
默认的异常页面会返回 500 状态码,如果是一个 HttpException 异常则会返回HTTP的错误状态码。
1.2、异常处理接管
框架支持异常处理由开发者自定义类进行接管,只需在 app\provider.php 文件中绑定异常处理类,例如:
php
// 绑定自定义异常处理handle类
'think\exception\Handle' => '\\app\\exception\\Http',
自定义异常类需要继承 think\exception\Handle 并且实现 render 方法,可以参考下面代码:
php
<?php
namespace app\exception;
use think\exception\Handle;
use think\exception\HttpException;
use think\exception\ValidateException;
use think\Response;
use Throwable;
class Http extends Handle
{
public function render($request, Throwable $e): Response
{
// 参数验证错误
if ($e instanceof ValidateException) {
return json($e->getError(), 422);
}
// 请求异常
if ($e instanceof HttpException && $request->isAjax()) {
return response($e->getMessage(), $e->getStatusCode());
}
// 其他错误交给系统处理
return parent::render($request, $e);
}
}
自定义异常处理的主要作用是根据不同的异常类型发送不同的状态码和响应输出格式。
默认安装应用后,系统已经内置了 app\ExceptionHandle 异常处理类,直接修改该类的相关方法即可完成应用的自定义异常处理机制,无需额外新建异常处理类。
注意:如果自定义异常处理类没有再次调用系统 render 方法的话,配置 http_exception_template 则不会生效。
1.3、手动抛出和捕获异常
ThinkPHP大部分情况异常都是自动抛出和捕获的,也可以手动使用 throw 来抛出一个异常,例如:
php
// 使用think自带异常类抛出异常
throw new \think\Exception('异常消息', 10001);
手动捕获异常方式是使用 try-catch,例如:
php
try {
// 这里是主体代码
} catch (ValidateException $e) {
// 这是进行验证异常捕获
return json($e->getError());
} catch (\Exception $e) {
// 这是进行异常捕获
return json($e->getMessage());
}
支持使用 try-catch-finally 结构捕获异常。
1.4、HTTP 异常
可以使用 \think\exception\HttpException 类来抛出HTTP异常
php
<?php
namespace app\index\controller;
use think\exception\HttpException;
class Index
{
public function index()
{
// 抛出 HTTP 异常
throw new HttpException(404, '异常消息');
}
}
系统提供了助手函数 abort 用于简化HTTP异常处理,例如:
php
<?php
namespace app\index\controller;
class Index
{
public function index()
{
// 抛出404异常
abort(404, '页面异常');
}
}
如果应用是API接口,那么请注意在客户端应首先判断HTTP状态码是否正常,然后再进行数据处理,当遇到错误的状态码时,应该根据状态码自行给出错误提示。
部署模式 下一旦抛出 HttpException 异常,可以定义单独的异常页面模板,只需要在 config\app.php 配置文件中增加如下配置:
php
'http_exception_template' => [
// 定义404错误的模板文件地址
404 => \think\facade\App::getAppPath() . '404.html',
// 还可以定义其它的HTTP status
401 => \think\facade\App::getAppPath() . '401.html',
]
模板文件支持模板引擎中的标签。
注意 :http_exception_template 配置仅在部署模式下生效。
2、日志处理
日志记录和写入由 \think\Log 类完成,通常我们使用 think\facade\Log 类进行静态调用。
由于日志记录了所有的运行错误,因此养成经常查看日志文件的习惯,可以避免和及早发现很多的错误隐患。
日志遵循 PSR-3 规范,除非是实时写入的日志,其它日志都是在当前请求结束的时候统一写入的 所以不要在日志写入之后使用 exit 等中断操作会导致日志写入失败。
PSR-3 规范
日志接口规范。本规范的主要目的,是为了让日志类库以简单通用的方式,通过接收一个 Psr\Log\LoggerInterface 对象,来记录日志信息。框架以及CMS内容管理系统如有需要,可以对此接口进行扩展,但须遵循本规范,才能保证使用第三方的类库文件时,日志接口仍能正常对接。
LoggerInterface 接口对外定义了八个方法,分别用来记录RFC 5424中定义的八个登记日志:debug、info、notice、warning、error、critical、alert以及emergency。
2.1、日志配置
日志的配置文件是配置文件(config)目录下的 log.php 文件,系统在进行日志写入之前会读取该配置文件进行初始化。
新版的日志配置支持多通道,默认配置如下:
php
<?php
return [
// 默认日志记录通道
'default' => 'file',
// 日志记录级别
'level' => [],
// 日志类型记录的通道 ['error'=>'email',...]
'type_channel' => [],
// 关闭全局日志写入
'close' => false,
// 全局日志处理 支持闭包
'processor' => null,
// 日志通道列表
'channels' => [
'file' => [
// 日志记录方式
'type' => 'File',
// 日志保存目录
'path' => '',
// 单文件日志写入
'single' => false,
// 独立日志级别
'apart_level' => [],
// 最大日志文件数量
'max_files' => 0,
// 使用JSON格式记录
'json' => false,
// 日志处理
'processor' => null,
// 关闭通道日志写入
'close' => false,
// 日志输出格式化
'format' => '[%s][%s] %s',
// 是否实时写入
'realtime_write' => false,
],
// 其它日志通道配置
],
];
可以添加多个日志通道,每个通道可以设置不同的日志类型。日志配置参数根据不同的日志类型有所区别,内置的日志类型包括:file,日志类型使用 type 参数配置即可。
如果是自定义驱动,type 的值则为自定义驱动的类名(包含命名空间)。
日志的全局配置参数包含:
|--------------|-----------|
| 参数 | 描述 |
| default | 默认的日志通道 |
| level | 允许记录的日志级别 |
| type_channel | 日志类型记录的通道 |
默认的日志类型是 File 方式,可以通过驱动的方式来扩展支持更多的记录方式。
文件类型日志,还支持下列配置参数:
|----------------|---------------------|
| 参数 | 描述 |
| path | 日志存储路径 |
| file_size | 日志文件大小限制(超出会生成多个文件) |
| apart_level | 独立记录的日志级别 |
| time_format | 时间记录格式 |
| single | 是否单一文件日志 |
| max_files | 最大日志文件数(超过自动清理 ) |
| format | 日志输出格式 |
| realtime_write | 是否实时写入 |
为了避免同一个目录下面的日志文件过多的性能问题,日志文件会自动生成日期子目录。
2.2、日志写入
2.2.1、手动记录
一般情况下,系统的错误日志记录是自动的,如果需要记录应用的业务日志或者额外的日志信息,就需要手动记录日志信息,Log 类提供了2个方法用于记录日志。
|----------|------------|
| 方法 | 描述 |
| record() | 记录日志信息到内存 |
| write() | 实时写入一条日志信息 |
系统在请求结束后会自动调用 Log::save 方法统一进行日志信息写入。
record 方法用法:
php
Log::record('测试日志信息');
默认记录的日志级别是 info,也可以指定日志级别:
php
Log::record('测试日志信息,这是警告级别', 'notice');
使用 record 方法记录的日志信息不是实时保存的,如果需要实时记录的话,需要使用 write 方法,例如:
php
Log::write('测试日志信息,这是警告级别,并且实时写入', 'notice');
也可以在日志通道配置开启实时写入,每次记录日志信息的时候就会实时写入。
php
'file' => [
// 日志记录方式
'type' => 'File',
// 日志保存目录
'path' => '',
// 单文件日志写入
'single' => false,
// 独立日志级别
'apart_level' => [],
// 最大日志文件数量
'max_files' => 0,
// 日志处理
'processor' => null,
// 实时写入
'realtime_write' => true,
],
2.2.2、关闭日志
调用 Log::close() 方法关闭本次请求的日志写入。
php
// 关闭当前日志写入
Log::close();
2.2.3、日志级别
ThinkPHP 对系统的日志按照级别来分类记录,按照 PSR-3 日志规范,日志的级别从低到高依次为: debug、info、notice、warning、error、critical、alert、emergency,ThinkPHP 额外增加了一个 sql 日志级别仅用于记录 SQL 日志(仅当开启数据库调试模式有效)。
系统发生异常记录的日志级别是 error。
系统提供了不同日志级别的快速记录方法,例如:
php
Log::error('错误信息');
Log::info('日志信息');
还封装了助手函数用于日志记录,例如:
php
trace('错误信息', 'error');
trace('日志信息', 'info');
也可以增加自定义的日志类型,例如:
php
Log::diy('这是一个自定义日志类型');
也支持指定级别日志的写入,需要配置:
php
return [
// 日志记录级别,使用数组表示
'level' => ['error', 'alert'],
];
上面的配置表示只记录 error 和 alert 级别的日志信息。
默认情况下是不会记录HTTP异常日志(避免受一些攻击的影响写入大量日志),除非接管了系统的异常处理,重写了 report 方法。
2.2.4、上下文信息
日志可以传入上下文信息(数组),并且替换到日志内容中,例如:
php
Log::info('日志信息{user}', ['user' => '张三']);
写入日志的时候,{user} 会被替换为"张三"。
2.2.5、独立日志
为了便于分析,File 类型的日志还支持设置某些级别的日志信息以单独文件进行记录,例如:
php
return [
'default' => 'file',
'channels' => [
'file' => [
'type' => 'file',
// error和sql日志单独记录
'apart_level' => ['error','sql'],
],
],
];
设置后,就会单独生成 error 和 sql 两个类型的日志文件,主日志文件中将不再包含这两个级别的日志信息。
如果 apart_level 设置为 true,则表示所有的日志类型都会独立记录。
2.2.6、单文件日志
默认情况下,日志是按照日期为目录,按天生成文件的。但如果希望仅生成单个文件(方便其它的工具或者服务读取以及分析日志),则可以按如下方式进行配置:
php
return [
'default' => 'file',
'channels' => [
'file' => [
'type' => 'file',
'single' => true,
'file_size' => 1024*1024*10,
],
],
];
开启生成单个文件后,file_size 和 apart_level 参数依然有效。超过文件大小限制后,系统会自动生成备份日志文件。
默认的单文件日志名是 single.log,如果需要更改日志文件名,可以这样设置
php
return [
'default' => 'file',
'channels' => [
'file' => [
'type' => 'file',
'single' => 'single_file',
'file_size' => 1024*1024*10,
],
],
];
实际生成的日志文件名是 single_file.log。如果设置了 apart_level 的话,可能还会生成 single_file_error.log 之类的日志。
单文件日志也支持 max_files 参数设置,因为单文件日志同样会生成多个日志备份文件而导致日志文件数据过大。
2.2.7、格式化日志信息
系统提供了两个参数用于日志信息的格式化,第一个是用于自定义时间显示格式的 time_format,第二个是调整日志输出格式的 format 参数。
php
return [
'default' => 'file',
'channels' => [
'file' => [
'type' => 'file',
'json' => true
'file_size' => 1024*1024*10,
'time_format' => 'Y-m-d H:i:s',
'format' => '[%s][%s]:%s',
],
],
];
2.2.8、清空日志
一旦执行 save 方法后,内存中的日志信息就会被自动清空,如果需要手动清空可以使用 clear 方法:
php
Log::clear();
在清空日志方法之前,可以使用 getLog 方法获取内存中的日志。
php
// 获取全部日志
$logs = Log::getLog();
日志清空仅仅是清空内存中的日志。
2.2.9、日志自动清理
文件类型的日志支持自动清理。可以设置 max_files 参数,超过数量的最早日志将会自动删除。
示例 设置日志最多保存数量为10个
php
return [
'default' => 'file',
'channels' => [
'file' => [
'type' => 'file',
'max_files' => 10,
'file_size' => 1024*1024*10,
],
],
];
设置 max_files 参数后,日志文件将不会分日期子目录存放。
2.2.10、JSON格式日志
可以支持 JSON 格式记录文件日志,更加方便一些第三方日志分析工具进行日志分析。在日志配置文件中,添加
php
return [
'default' => 'file',
'channels' => [
'file' => [
'type' => 'file',
'json' => true
'file_size' => 1024*1024*10,
],
],
];
即可开启 JSON 格式记录。
2.3、日志通道
可以配置不同的日志通道,并且把不同的日志记录到不同的通道。
php
Log::channel('email')->info('一条测试日志');
Log::channel('socket')->error('记录错误日志');
可以配置不同的日志类型,记录到不同的日志通道,这样在记录日志的时候会自动选择对应的通道写入。
php
return [
'default' => 'file',
'type_channel' => [
'error' => 'email',
'sql' => 'sql',
],
'channels' => [
'file' => [
'type' => 'file',
],
'email' => [
'type' => 'email',
],
'sql' => [
'type' => 'sql',
],
],
];
表示如果是 error 日志和 sql 日志,会分别记录到指定的通道。同时还需要在日志配置文件中,添加 email 和 sql 日志通道的配置。核心只有 file 日志类型,其它的可能需要自己扩展或者安装扩展。
如果需要获取内存中的通道日志信息,可以使用 getLog 方法并传入通道名称
php
// 获取某个日志通道的日志
$error = Log::getLog('file');
可以单独关闭某个通道的日志写入,只需要把日志通道的 close 配置参数设置为 true,或者使用方法关闭。
php
Log::close('file');
可以使用 clear 方法单独清空某个通道的日志(如果没有开启实时写入的话)
php
Log::clear('file');
2.4、自定义驱动
如果要自定义日志驱动,需要实现 think\contract\LogHandlerInterface 接口。
php
interface LogHandlerInterface
{
/**
* 日志写入接口
* @access public
* @param array $log 日志信息
* @return bool
*/
public function save(array $log): bool;
}
要使用自定义驱动,只需在 type 参数中直接传入完整的自定义类名即可。
二、调试
1、调试模式
ThinkPHP 有专门为开发过程而设置的调试模式,开启调试模式后,会牺牲一定的执行效率,但带来的方便和除错功能非常值得。
应用默认是部署模式,在开发阶段,可以修改环境变量 APP_DEBUG 开启调试模式,上线部署后再切换到部署模式。
本地开发的时候可以在应用根目录下面定义 .env 文件。
通过 create-project 默认安装的话, 会在根目录自带一个 .example.env 文件,可以直接将其更名为 .env 文件。
.env 文件的定义如下所示:
php
// 设置开启调试模式
APP_DEBUG = true
// 其它的环境变量设置
// ...
调试模式的优势:
- 开启日志记录,任何错误信息和调试信息都会详细记录,便于调试;
- 会详细记录整个执行过程;
- 模板修改可以即时生效;
- 通过Trace功能更好的调试和发现错误;
- 发生异常时会显示详细的异常信息。
由于调试模式没有任何缓存,因此涉及到较多的文件IO操作和模板实时编译,所以在开启调试模式的情况下,性能会有一定的下降,但不会影响部署模式的性能。
一旦关闭调试模式,发生错误后不会提示具体的错误信息,如果仍希望看到具体的错误信息,那么可以在 config\app.php 文件中进行如下设置:
php
// 显示错误信息
'show_error_msg' => true,
2、Trace调试
调试模式并不能完全满足我们调试的需要,有时候需要手动的输出一些调试信息。除了本身可以借助一些开发工具进行调试外,ThinkPHP 还提供了一些内置的调试工具和函数。
Trace 调试功能是 ThinkPHP 提供给开发人员的一个用于开发调试的辅助工具。可以实时显示当前页面或者请求的请求信息、运行情况、SQL执行、错误信息和调试信息等,并支持自定义显示,并且支持没有页面输出的操作调试。最新版本页面Trace功能已经不再内置在核心,但默认安装的时候会自动安装 topthink/think-trace 扩展,所以可以在项目里面直接使用。
如果部署到服务器的话,可以执行如下命令
bash
composer install --no-dev
这样在部署情况下就不会安装页面Trace扩展。
2.1、使用
页面Trace功能仅在调试模式下有效。
安装页面Trace扩展后,如果开启调试模式并且运行后有页面有输出的话,页面右下角会显示 ThinkPHP 的 LOGO:

LOGO后面的数字就是当前页面的执行时间(单位是秒) 点击该图标后,会展开详细的Trace信息,如图所示:

Trace框架有6个选项卡,分别是基本、文件、流程、错误、SQL和调试,点击不同的选项卡会切换到不同的Trace信息窗口。
|---------|--------------------------------------|
| 选项卡 | 描述 |
| 基本 | 当前页面的基本摘要信息,例如执行时间、内存开销、文件加载数、查询次数等等 |
| 文件 | 详细列出当前页面执行过程中加载的文件及其大小 |
| 流程 | 会列出当前页面执行到的行为和相关流程 |
| 错误 | 当前页面执行过程中的一些错误信息,包括警告错误 |
| SQL | 当前页面执行到的SQL语句信息 |
| 调试 | 开发人员在程序中进行的调试输出 |
Trace的选项卡是可以定制和扩展的,如果希望增加新的选项卡,则可以修改配置目录(config)下的 trace.php 文件,配置参数如下所示:
php
return [
'type' => 'Html',
'tabs' => [
'base' => '基本',
'file' => '文件',
'info' => '流程',
'error' => '错误',
'sql' => 'SQL',
'debug' => '调试',
'user' => '用户',
],
];
base 和 file 是系统内置的,其它的选项都属于日志的等级(user是用户自定义的日志等级)。
也可以把某几个选项卡合并,例如:
php
return [
'type' => 'Html',
'tabs' => [
'base' => '基本',
'file' => '文件',
'error|notice|warning' => '错误',
'sql' => 'SQL',
'debug|info' => '调试',
],
];
更改后的Trace显示效果如图所示:

如果需要更改页面Trace输出的样式,可以自定义模板文件(可以复制内置模板文件 vendor/topthink/think-trace/src/tpl/page_trace.tpl 的内容),然后在 trace.php 中配置 file 参数指定模板文件位置。
php
'file' => '..\app\trace\page_trace.html',
2.2、浏览器控制台输出
Trace功能支持在浏览器的 console 中直接输出,这样可以方便没有页面输出的操作功能调试,只需要在配置文件中设置:
php
// 使用浏览器console输出trace信息
'type' => 'console',
运行后打开浏览器的 console 控制台可以看到如图所示的信息:

浏览器Trace输出同样支持 tabs 设置。
3、SQL调试
3.1、查看页面Trace
通过查看页面Trace信息可以看到当前请求所有执行的SQL语句,例如:

3.2、查看SQL日志
日志文件记录了详细的SQL执行记录,我们可以通过查看日志文件来了解SQL的执行。
通常建议把SQL日志级别写入到单独的日志文件中。
下面是一个典型的SQL日志:
php
[sql] CONNECT:[ UseTime:0.070333s ] mysql:host=127.0.0.1;port=3306;dbname=test;charset=utf8
[sql] SHOW FULL COLUMNS FROM `user` [ RunTime:0.016164s ]
[sql] SELECT * FROM `user` [ RunTime:0.001109s ]
3.3、调试执行的SQL语句
在模型操作中,为了更好的查明错误,经常需要查看最近使用的SQL语句,此时可以用 getLastsql 方法来输出上次执行的SQL语句。例如:
php
User::find(1);
echo User::getLastSql();
输出结果是
sql
SELECT * FROM user WHERE id = 1 LIMIT 1
getLastSql 方法只能获取最后执行的SQL语句。
也可以使用 fetchSql 方法直接返回当前的查询SQL而不执行,例如:
php
echo User::fetchSql()->find(1);
输出的结果是一样的。
4、变量调试
输出某个变量是开发过程中经常会用到的调试方法,除了使用 php 内置的 var_dump 和 print_r 方法之外,框架内置还了一个对浏览器友好的 dump 方法,用于输出变量的信息到浏览器查看。
dump 的用法和PHP内置的 var_dump 方法一样:
php
dump($var1, ...$varN)
示例
php
$blog = Db::name('blog')->where('id', 3)->find();
$user = User::find();
dump($blog, $user);
如果需要在调试变量输出后中止程序的执行,可以使用 halt 函数,例如:
php
$blog = Db::name('blog')->where('id', 3)->find();
$user = User::find();
halt($blog, $user);
echo '这里的信息是看不到的';
执行后会输出同样的结果并中止执行后续的程序。
三、杂项
1、缓存
ThinkPHP使用 think\Cache 类(实际使用 think\facade\Cache 类即可)提供缓存功能支持。内置支持的缓存类型包括 file、memcache、wincache、sqlite、redis。
1.1、设置
全局的缓存配置直接修改配置目录(config)下面的 cache.php 文件。
新版的缓存支持多通道,可以事先定义好所有的缓存类型及配置参数,然后在使用的时候可以随时切换。默认使用的是文件缓存类型,也可以添加 redis 缓存支持,例如:
php
return [
'default' => 'file',
'stores' => [
// 文件缓存
'file' => [
// 驱动方式
'type' => 'file',
// 设置不同的缓存保存目录
'path' => '../runtime/file/',
],
// redis缓存
'redis' => [
// 驱动方式
'type' => 'redis',
// 服务器地址
'host' => '127.0.0.1',
],
],
];
缓存参数根据不同的缓存方式会有所区别,通用的缓存参数如下所示:
|-------------|----------------------|
| 参数 | 描述 |
| type | 缓存类型(或自定义驱动类名) |
| expire | 缓存有效期 (默认为 0 表示永久缓存) |
| prefix | 缓存前缀(默认为空) |
| serialize | 缓存序列化和反序列化方法 |
| fail_delete | 获取缓存失败后是否强制删除 |
如果是自定义驱动,type 的值则为自定义驱动的类名(包含命名空间)
1.2、使用
Cache::set 方法设置缓存有效期。
php
// 缓存在3600秒之后过期
Cache::set('name', $value, 3600);
可以使用 DateInterval 对象设置缓存多久以后过期。
php
Cache::set('name', $value, DateInterval::createFromDateString('1 day'));
设置成功返回 true,否则返回 false。
Cache::get 方法获取缓存数据。
php
Cache::get('name');
如果 name 值不存在,则返回 null。
get 方法支持指定默认值,例如:
php
Cache::get('name', '');
表示如果 name 值不存在,则返回空字符串。
也支持传入闭包作为默认值获取
php
Cache::get('name', function(){
// 动态返回数据
});
如果在获取缓存的时候发生异常,也会返回默认值,可以配置开启 fail_delete 参数,开启后,发生异常后会强制删除该缓存标识。
如果缓存数据是一个数组,可以通过 Cache::push 方法追加一个数据。
php
Cache::set('name', [1, 2, 3]);
Cache::push('name', 4);
Cache::get('name'); // [1, 2, 3, 4]
Cache::delete 方法删除缓存。
php
Cache::delete('name');
Cache::pull 方法用于获取指定缓存后并删除该缓存。
php
Cache::pull('name');
如果 name 值不存在,则返回 null,支持指定默认值
php
Cache::pull('name', '');
Cache::clear 方法清空所有缓存。
php
Cache::clear();
Cache::remember 方法在指定键名缓存数据不存在时写入缓存数据并返回。
php
Cache::remember('start_time', time());
如果 start_time 缓存数据不存在,则会设置缓存数据为当前时间。
第二个参数可以使用闭包方法获取缓存数据,并支持依赖注入。
php
Cache::remember('start_time', function(Request $request){
return $request->time();
});
remember 方法的第三个参数用于设置缓存有效期。
Cache::tag 方法用于给缓存数据打标签,例如:
php
Cache::tag('tag')->set('name1', 'value1');
Cache::tag('tag')->set('name2', 'value2');
// 清除tag标签的缓存数据
Cache::tag('tag')->clear();
缓存标签不会改变缓存的读取操作,所以获取方式依然是使用 get 方法:
php
Cache::get('name1');
支持同时指定多个缓存标签操作
php
Cache::tag(['tag1', 'tag2'])->set('name1', 'value1');
Cache::tag(['tag1', 'tag2'])->set('name2', 'value2');
// 清除多个标签的缓存数据
Cache::tag(['tag1','tag2'])->clear();
使用 append 方法可以追加某个缓存标识到标签
php
Cache::tag('tag')->append('name3');
Cache::getTagItems 方法获取标签的缓存标识列表
php
Cache::getTagItems('tag');
ThinkPHP 对缓存操作提供了助手函数 cache,用法如下:
php
// 设置缓存数据
cache('name', $value, 3600);
// 获取缓存数据
var_dump(cache('name'));
// 删除缓存数据
cache('name', NULL);
// 返回缓存对象实例
$cache = cache();
1.3、切换缓存类型
没有指定缓存类型的话,默认读取的是 default 缓存配置,可以受用 store 方法进行动态切换
php
// 使用文件缓存
Cache::set('name', 'value', 3600);
Cache::get('name');
// 使用Redis缓存
Cache::store('redis')->set('name', 'value', 3600);
Cache::store('redis')->get('name');
// 切换到文件缓存
Cache::store('default')->set('name', 'value', 3600);
Cache::store('default')->get('name');
2、Session
直接使用 think\facade\Session 类操作 Session。
新版本不支持操作原生 $SESSION 数组和所有 session 开头的函数,只能通过 Session 类(或者助手函数)来操作。会话数据统一在当前请求结束的时候统一写入,所以不要在 session 写入操作之后执行 exit 等中断操作,否则会导致 session 数据写入失败。
2.1、开启Session
Session 功能默认是没有开启的(API应用通常不需要使用 Session),如果需要使用 Seesion,需要在全局的中间件定义文件中加上下面的中间件定义(在上一篇文章中表单令牌就配置了该中间件):
php
'think\middleware\SessionInit'
2.2、Session初始化
系统会自动按照 config\session.php 配置的参数自动初始化 Session。
默认支持的 session 设置参数包括:
|----------------|--------------------------|
| 参数 | 描述 |
| type | session类型(File或者Cache) |
| store | 当type设置为cache类型的时候指定存储标识 |
| expire | session过期时间(秒)必须大于0 |
| var_session_id | 请求session_id变量名 |
| name | session_name |
| prefix | session前缀 |
| serialize | 序列化方法 |
无需任何操作就可以直接调用 Session 类的相关方法,例如:
php
Session::set('name', 'test');
Session::get('name');
会话数据保存(请求结束)的时候会自动序列化,并在读取的时候自动反序列化,默认使用 serialize / unserialize 进行序列化操作。
也可以自定义序列化机制,例如在配置文件中设置使用 JSON 序列化:
php
'serialize' => ['json_encode', 'json_decode'],
2.3、基础用法
Session::set 方法赋值
php
Session::set('name', 'test');
Session::has 方法判断指定键名数据是否存在
php
Session::has('name');
Session::get 方法取值
php
// 如果值不存在,返回null
Session::get('name');
// 如果值不存在,返回空字符串
Session::get('name', '');
// 获取全部数据
Session::all();
Session::delete 方法删除指定键名数据
php
Session::delete('name');
Session::pull 方法获取指定键名数据并删除
php
// 取值并删除
Session::pull('name');
如果 name 的值不存在,返回 null。
Session::clear 方法清空 Session 内所有数据
php
Session::clear();
2.4、助手函数
ThinkPHP 也提供了助手函数 session 完成相同的功能,例如:
php
// 赋值
session('name', 'test');
// 判断是否赋值
session('?name');
// 取值
session('name');
// 删除
session('name', null);
// 清除session
session(null);
2.5、Request对象中读取Session
可以在 Request 对象中读取 Session 数据
php
public function index(Request $request) {
// 读取某个session数据
$request->session('user.name', '');
// 获取全部session数据
$request->session();
}
但 Request 类中不支持 Session 写入操作。
3、Cookie
ThinkPHP 采用 think\facade\Cookie 类提供 Cookie 支持。
3.1、配置
配置文件位于配置目录(config)下的 cookie.php 文件,无需手动初始化,系统会在调用之前自动进行 Cookie 初始化工作。
支持的参数及默认值如下:
php
// cookie 保存时间
'expire' => 0,
// cookie 保存路径
'path' => '/',
// cookie 有效域名
'domain' => '',
// cookie 启用安全传输
'secure' => false,
// httponly设置
'httponly' => false,
// 是否使用 setcookie
'setcookie' => true,
// samesite 设置,支持 'strict' 'lax'
'samesite' => '',
3.2、基本操作
Cookie::set 方法赋值
php
// 设置Cookie有效期为3600秒
Cookie::set('name', 'value', 3600);
Cookie 数据不支持数组,如果需要请自行序列化后存入。
Cookie::forever 方法可用于永久保存 Cookie
php
// 永久保存Cookie
Cookie::forever('name', 'value');
Cookie::delete 方法删除指定键名数据
php
//删除cookie
Cookie::delete('name');
Cookie::get 方法取值
php
// 读取某个cookie数据
Cookie::get('name');
// 获取全部cookie数据
Cookie::get();
3.3、助手函数
ThinkPHP 提供了 cookie 助手函数用于基本的 Cookie 操作,例如:
php
// 设置
cookie('name', 'value', 3600);
// 获取
echo cookie('name');
// 删除
cookie('name', null);
4、上传
4.1、上传文件
创建上传文件表单,假设代码如下:
html
<form action="/index/upload" enctype="multipart/form-data" method="post">
<input type="file" name="image" /> <br>
<input type="submit" value="上传" />
</form>
然后在控制器中添加文件上传的代码:
php
public function upload(){
// 获取表单上传文件 例如上传了001.jpg
$file = request()->file('image');
// 上传到本地服务器,topic是文件上传保存的目录
$savename = \think\facade\Filesystem::putFile('topic', $file);
// savename:文件上传到本地服务器的文件名
}
$file 变量是一个 \think\File 对象,可以获取相关的文件信息。
4.2、上传规则
文件上传默认情况是上传到本地服务器,会在 runtime/storage 目录下面生成以当前日期为子目录,以微秒时间的 md5 编码为文件名的文件,例如上面生成的文件名可能是:
php
runtime/storage/topic/20260124/5d92a54ff5138761f7a45603e6d7212a.png
可以在 config/filesystem.php 配置文件中配置上传根目录及上传规则,例如:
php
return [
'default' => 'local',
'disks' => [
'local' => [
'type' => 'local',
'root' => app()->getRuntimePath() . 'storage',
],
'public' => [
'type' => 'local',
'root' => app()->getRootPath() . 'public/storage',
'url' => '/storage',
'visibility' => 'public',
],
// 更多的磁盘配置信息
],
];
系统默认根据日期和微秒数生成文件命名规则,为了避免并发导致的冲突可能,我们可以指定上传文件的命名规则,例如:
php
$savename = \think\facade\Filesystem::putFile('topic', $file, 'md5');
最终生成的文件名类似于:
php
runtime/storage/topic/29/2c3da4184b4e5c81ff0ca87ce6689c.png
如果希望以指定的文件名保存,可使用 putFileAs 方法,例如:
php
$savename = \think\facade\Filesystem::putFileAs('topic', $file, 'test.jpg');
4.3、多文件上传
同样地,也可以进行多文件上传,例如:
html
<form action="/index/index/upload" enctype="multipart/form-data" method="post">
<input type="file" name="image[]" /> <br>
<input type="file" name="image[]" /> <br>
<input type="file" name="image[]" /> <br>
<input type="submit" value="上传" />
</form>
控制器代码修改为:
php
public function upload(){
// 获取表单上传文件,此时为数组
$files = request()->file('image');
$savename = [];
foreach($files as $file){
$savename[] = \think\facade\Filesystem::putFile('topic', $file);
}
}
4.4、上传验证
支持使用验证类对上传文件的验证,包括文件大小、文件类型和后缀:
php
public function upload(){
// 获取表单上传文件
$files = request()->file();
try {
// 对上传文件进行验证
validate(['image'=>'fileSize:10240|fileExt:jpg|image:200,200,jpg'])->check($files);
$savename = [];
foreach($files as $file) {
$savename[] = \think\facade\Filesystem::putFile( 'topic', $file);
}
} catch (\think\exception\ValidateException $e) {
echo $e->getMessage();
}
}
|----------|----------------------|
| 验证参数 | 说明 |
| fileSize | 上传文件的最大字节 |
| fileExt | 文件后缀,多个用逗号分割或者数组 |
| fileMime | 文件MIME类型,多个用逗号分割或者数组 |
| image | 验证图像文件的尺寸和类型 |