兼容PHP8.4+
最新框架对PHP最新的8.4+版本做了兼容处理,确保可以正常运行。并增加了array_is_list、json_validate兼容助手函数,不依赖对应PHP版本。主要对路由和验证方面做了大量的改进,并且兼容PHP8.4+。
优化事件订阅者
现在可以支持给事件订阅者增加标识,提高事件响应效率(无需逐个注册事件监听和订阅),在事件定义文件使用:
return [// ...// 定义事件订阅者'subscribe' => ['user' => 'app\subscribe\UserSubscribe',],];
在UserSubscribe订阅类中定义相应的事件方法
<?phpnamespace app\observer;use app\model\User;class UserSubscribe{public function onUserLogin(User $user){// UserLogin事件响应处理}public function onUserLogout(User $user){// UserLogout事件响应处理}}
事件方法的参数支持依赖注入,可以添加额外的参数。
假如需要触发user_login事件,触发代码如下:
event('user.user_login');
出于方法命名规范考虑,对于小写和下划线的事件名会自动触发驼峰命名的事件方法名。
增加Macroable方法注入
增加Macroable支持,只需要在你需要实现Macroable的类引入Trait即可使用。
use think\helper\Macroable; 然后通过macro静态方法注入闭包方法实现,注意由于Macroable机制采用PHP的call以及callStatic方法实现,如果已经存在则无法支持。
ClassName::macro('test', function(...$parms) {// 判断是否已经定义if ( !static::hasMacro('test')) {// 方法逻辑实现}});
新的路由变量验证方法
增加了when方法用于指定某个路由变量的多个验证规则,当验证不通过的话当前路由规则将不会匹配。区别于pattern方法只能使用正则表达式验证,when方法可以支持所有的内置验证类规则。
Route::get('new/:name', 'News/read')->when('name', 'alphaNum');Route::get('new/:category', 'News/category')->when('category', CategoryEnum::class);
并且当验证规则为数字类型的话,最终的路由变量会自动转换为整型或浮点型。
路由可选变量支持默认值
支持对可选路由变量设置默认值,对于动态路由尤其有用。
Route::get('blog/[:category]', 'blog/:category')->default(['category' => 'all']);
路由中间件排除
在分组的路由规则中,可以排除或不使用中间件,使用下面的方式
Route::group('blog', function(){Route::get(':id/edit','blog/edit');Route::put(':id','blog/update');Route::delete(':id','blog/delete');Route::get(':id','blog/read')->withoutMiddleware(); // 无需鉴权})->middleware(\app\middleware\Auth::class);
withoutmiddleware也支持通过数组方式传入需要排除的部分中间件。
路由分组绑定
路由分组可以支持绑定到命名空间、控制器或某个类,当分组下的定义路由都没有匹配成功会自动按绑定规则进行默认URL调度(不受强制路由影响),优先级高于分组MISS路由,并且支持多级分组,分组相关参数包括中间件仍然有效。
绑定到命名空间
Route::group('blog', function () {Route::get(':id', 'blog/read');Route::post(':id', 'blog/update');Route::delete(':id', 'blog/delete');})->pattern(['id' => '\d+'])->namespace('app\controller\home');
分组绑定到指定命名空间后,对没有匹配的请求会自动执行app\controller\home 命名空间下面的类和方法。
绑定到控制器
Route::group('blog', function () {Route::get(':id', 'read');Route::post(':id', 'update');Route::delete(':id', 'delete');})->pattern(['id' => '\d+'])->controller('blog');
分组绑定到控制器(支持分级控制器)后,对没有匹配的请求会自动执行该控制器类下面的操作方法。
绑定到控制器分级
Route::group('admin', function () {Route::get('blog/:id', 'blog/read');})->pattern(['id' => '\d+'])->layer('admin');
分组绑定到某个控制器分级后,对没有匹配的请求会自动执行分级目录下的控制器和方法。
绑定到类
Route::group('blog', function () {Route::get(':id', 'read');Route::post(':id', 'update');Route::delete(':id', 'delete');})->pattern(['id' => '\d+'])->class(Blog::class);
分组绑定到类后,对没有匹配的请求会自动执行该类的方法。
简单绑定
如果你不需要定义分组路由,仅仅是需要进行分组绑定,可以直接使用下面的方式。
Route::group('blog')->layer('blog'); // 绑定到blog 控制器分级Route::group('blog')->controller('blog'); // 绑定到blog控制器Route::group('blog')->class(Blog::class); // 绑定到blog类Route::group('blog')->namespace('app\controller\blog'); // 绑定到命名空间
路由分组绑定在你需要增加或迁移URL路由规则的时候也非常有用。由于域名路由继承分组路由,所以,可以对域名进行类似的绑定操作。
自动URL调度
可以为某个分组开启自动URL调度(内部采用绑定机制,即绑定到控制器分级)。
Route::group('blog', function () {Route::get(':id', 'blog/read');Route::post(':id', 'blog/update');Route::delete(':id', 'blog/delete');})->pattern(['id' => '\d+'])->auto(); // 为blog分组启用自动自动URL调度
默认的URL调度规则为
https://domainName/groupName.../controllerName/actionName
其中groupName可能为多级分组,内部实现为多级控制器机制,groupName相当于控制器的多级子目录。
多模块URL自动调度
默认的URL解析规则为
https://domainName/controllerName/actionName
如需开启类似TP5的多模块访问URL的话,可以在路由定义文件的最后添加下面的一行代码
// 开启多模块URL自动解析Route::auto();
如果没有匹配到任何路由规则的话,会按照下面的默认URL规则解析
https://domainName/moduleName/controllerName/actionName
如果访问下面的URL地址
https://tp.com/admin/index/hello
则会自动调用 app\controller\admin\Index 控制器的hello操作方法。
如果你的默认URL解析规则需要根据业务做一些调整,例如加上接口的版本定义,也可以自定义默认规则
// 自定义URL解析规则Route::auto('v<version>/[:controller]/[:action]', 'v<version>/:controller/:action')->pattern(['version' => '\d+']);
可以在auto方法后调用路由的设置方法。
还可以在开启自动多模块路由的情况下,开启自动中间件加载。
Route::auto(middleware:true);
会自动加载和模块名同名的中间件别名(你可以通过别名定义一系列模块中间件)。
中间件别名定义在应用目录下的middleware.php文件,添加类似于下面的模块别名即可:
return ['alias' => [// 为admin模块定义中间件别名'admin' => [app\middleware\Auth::class,app\middleware\Check::class,],...],];
资源路由扩展规则
可以在资源路由的默认规则之外,额外注册路由,比如需要使用下面的路由定义:
// 注册资源路由Route::resource('space.bot', 'bot/index');// 在资源路径下追加注册相关路由Route::post('space/:space_id/bot/:id/chat', 'bot.chat/index');Route::post('space/:space_id/bot/:id/chat/upload', 'bot.chat/upload');Route::post('space/:space_id/bot/:id/chat/suggestion', 'bot.chat/suggestion');Route::post('space/:space_id/bot/:id/chat/speech', 'bot.chat/speech');
我们可以使用资源路由的扩展路由定义方法来简化注册(用法类似于分组路由的定义,仅支持使用闭包定义)。
Route::resource('space.bot', 'bot/index', function() {Route::post('chat', 'bot.chat/index');Route::post('chat/upload', 'bot.chat/upload');Route::post('chat/suggestion', 'bot.chat/suggestion');Route::post('chat/speech', 'bot.chat/speech');});
或者使用extend方法注册,效果一致。
Route::resource('space.bot', 'bot/index')->extend(function() {Route::post('chat', 'bot.chat/index');Route::post('chat/upload', 'bot.chat/upload');Route::post('chat/suggestion', 'bot.chat/suggestion');Route::post('chat/speech', 'bot.chat/speech');});
并且一样可以支持分组,例如可以进一步简化成
Route::resource('space.bot', 'bot/index', function() {Route::group('chat', function() {Route::post('/', 'bot.chat/index');Route::post('upload', 'bot.chat/upload');Route::post('suggestion', 'bot.chat/suggestion');Route::post('speech', 'bot.chat/speech');});});
通过资源路由的扩展路由注册的优势是在路由匹配上效率更高,另外资源路由采用完整匹配模式,所以下面的扩展路由也会自动采用完整匹配模式。
数组数据验证
最新的验证类增加了数组数据的验证,可以对多维数组的数据指定相关验证规则。
<?phpnamespace app\validate;use think\Validate;class User extends Validate{protected $rule = ['name' => 'require|max:25','age' => 'number|between:1,120',// info是一个索引数组'info.email' => 'email','info.score' => 'number',];}
并且支持多维数组验证
<?phpnamespace app\validate;use think\Validate;class User extends Validate{protected $rule = ['name' => 'require|max:25','age' => 'number|between:1,120',// info是一个二维数组'info.*.email' => 'email','info.*.score' => 'number',];}
必须验证字段
如果你的必须验证字段比较多,可以无需在验证规则中一一定义,只需要在验证类中设置must属性即可。
例如,下面定义了name和email为必须验证。
<?phpnamespace app\validate;use think\Validate;class User extends Validate{protected $must = ['name', 'email'];protected $rule = ['name' => 'max:25','age' => 'number|between:1,120','email' => 'email',];}
支持枚举验证
新版可以支持对枚举类进行验证,验证某个字段的值是否在允许的枚举值范围(支持普通枚举、回退枚举和自定义枚举类),例如:
<?phpnamespace app\validate;use think\Validate;use app\enum\StatusEnum;class User extends Validate{protected $rule = ['name' => 'max:25','age' => 'number|between:1,120','status' => StatusEnum::class,];}
如果是自定义枚举类,需要实现think\contract\Enumable接口。或者使用下面的方式定义:
<?phpnamespace app\validate;use think\Validate;use app\enum\StatusEnum;class User extends Validate{protected $rule = ['name' => 'max:25','age' => 'number|between:1,120','status' => ['enum' => StatusEnum::class],];}
增加验证规则
增加accepted、acceptedIf、declined、declinedIf、multipleOf等验证规则。
请求对象增加layer方法
layer方法用于获取当前的控制器分级,当控制器分级为多级的情况,layer方法的返回值会包含多级目录。
request()->layer();
当存在多级控制器的时候,controller方法也会包含控制器分级,如仅需获取当前的控制器类名,可以使用下面的方式:
request()->controller(); // 返回 admin/Blogrequest()->controller(base:true); // 仅返回 Blog
操作方法参数绑定
支持对控制器的操作方法(包括路由到类的方法)的参数绑定范围支持设置,默认情况下仅包含当前请求的GET变量和路由变量,如果你需要包含其它变量(例如POST变量),可以在路由配置文件中设置
'action_bind_param' => 'param',
该参数支持设置的值包括route(仅支持绑定路由变量)、get(支持绑定get请求变量和路由变量,默认为get)、param(支持请求变量和路由变量)。
增加配置获取器功能
支持配置获取器功能用于远程配置中心,你可以注册一个配置获取器
think\facade\Config::hook(function($name, $value) {// 对配置参数和值进行处理后返回最终的配置值// ...return $value;});
公共环境变量文件
可以设置一个公共环境变量文件,会优先加载,然后在公共环境变量文件中定义部署的环境变量名称,完成动态切换不同的预设环境变量文件。
// 执行HTTP应用并响应$http = (new App())->setBaseEnvName('base')->http;$response = $http->run();$response->send();$http->end($response);
日志备份文件规则调整
对日志备份文件的命名规则做了适当调整,当设置了max_files后,不影响过期日志文件的删除。
Cookie设置实时生效
现在对Cookie对象的设置将会实时生效,而不是下次请求才会有效。
Cookie::set('name', 'value');// 马上获取数据Cookie::get('name'); // 正常获取
改进依赖注入的对象默认值处理
如果对依赖注入的对象参数设置了默认值(包括Null),那么该对象在注入的时候不会自动创建新的实例。
缓存获取默认值支持闭包
对缓存get方法或pull方法支持传入闭包作为默认值参数。
Cache::get('name',function(){// 动态返回数据});
其它更新
- 优化异常处理对json的判断
- 改成系统初始化阶段的异常处理
- 改进验证场景处理
- 调整invokeAfter位置
- 改进缓存反序列化的异常处理
- Request only方法支持强制类型转换
- 缓存增加fail_delete配置参数 用于在获取缓存发生异常的时候是否强制删除
- 优化验证类的验证规则判断
- 改进缓存serialize/unserialize方法
