图像处理功能
- 图像处理功能不是系统内置的功能了,需要通过
composer
引入进来;
composer require topthink/think-image
- 引入进来之后,首先创建图像处理对象;
$image = Image::open('image.png');
- 获得了图像处理对象后,可以得到这张图片的各种属性;
//图片宽度
echo $image->width();
//图片高度
echo $image->height();
//图片类型
echo $image->type();
//图片 mime
echo $image->mime();
//图片大小
dump($image->size());
- 使用
crop()
方法可以裁剪图片,并使用save()
方法保存到指定路径; - 可以点击追踪方法内部,参看源码参数,了解更多的传值方法;
//裁剪图片
$image->crop(550,400)->save('crop1.png');
- 使用
thumb()
方法,可以生成缩略图,配合save()
把缩略图保存下来;
//生成缩略图
$image->thumb(500,500)->save('thumb1.png');
- 这里要注意一个问题,虽然设置了宽和高,但高度变成了282,说明是等比例的;
- 可以点击追踪方法内部,第三个参数默认为:
$type = self::THUMB_SCALING
; - 而这个常量设置的定义如下:
/* 缩略图相关常量定义 */
const THUMB_SCALING = 1; //常量,标识缩略图等比例缩放类型
const THUMB_FILLED = 2; //常量,标识缩略图缩放后填充类型
const THUMB_CENTER = 3; //常量,标识缩略图居中裁剪类型
...
- 使用
rotate()
方法,可以旋转图片,默认是 90 度,参数可以设置;
$image->rotate(180)->save('rotate1.png');
- save()方法可以配置的参数除了保存文件名的路径,还有以下几个:
- save('路径',['类型','质量','是否隔行扫描']),追踪到方法查看;
save($pathname, $type = null, $quality = 80, $interlace = true)
water()
方法,可以给图片增加一个图片水印,默认位置为右下角,可看源码常量;
$image->water('mr.lee.png',5,50)->save('water1.png');
text()
方法,可以给图片增加一个文字水印,具体如下:
$image->text('Mr.Lee',getcwd().'/1.ttf',20,'#ffffff')->save('text1.png');
异常处理
- 系统输出的异常信息比PHP原生的要人性化的多,但需要开启调试模式;
- 如果你想更改异常页面的样式、布局之类的,可以修改这个页面:
vendor/topthink/framework/src/tpl/think_exception.tpl
- 如果你想要直接替换掉异常页面,换成别的,可以在
app.php
中进行设置:
// 异常页面的模板文件
'exception_tmpl' => app()->getThinkPath() . 'tpl/think_exception.tpl',
- 系统的异常抛出是自动进行的,并不需要你手动抛出,但也支持手动;
throw new Exception('异常消息', 10086);
- 我们可以使用
try{}catch(){}
对可能发生的异常进行手动捕获或抛出;
try {
echo 0/0;
} catch (ErrorException $e)
{
echo '发生错误:'.$e->getMessage();
}
- 我们可以抛出HTTP异常,所谓HTTP异常比如404错误,500错误之类;
throw new HttpException(404, '页面不存在');
- 系统提供了一个助手函数
abort()
方法简化HTTP异常抛出;
abort(404, '页面不存在');
- 如果系统关闭了调试模式,进入部署环境下,可以设置HTTP错误页面,比如404;
'http_exception_template' => [
// 定义 404 错误的模板文件地址
404 => \think\facade\App::getAppPath() . '404.html',
]
日志处理
- 日志处理的操作由
Log
类完成,它记录着所有程序中运行的错误记录; - 在
config\log.php
配置文件,用于设置日志信息; - 我们在
runtime
目录下后一个log
文件夹,里面按照日期排好了每月的日志; - 使用
record()
方法,记录一条测试日志;
Log::record('测试日志!');
- 我们在
log
日志文件夹里找到最新生成的日志,可以看到生成的日志信息; - 系统提供了不同日志级别,默认
info
级别,从低到高排列如下: debug/info/notice/warning/error/critical/alert/emergency/sql
;- 一般记录就是 info 信息,我们也可以指定我们的信息级别;
Log::record('测试日志!', 'error');
record()
方法不是实时记录,需要等待程序完毕后决定是否写入日志;- 如果在写入方法后添加
close()
关闭写入,那么record()
方法则不写入;
Log::close();
- 系统还提供了一个
write()
方法,进行时时写入,不理会其它限制;
Log::write('测试日志信息', 'error');
- 系统发生异常后,会自动写入error日志,如果你想手动也可以;
try {
echo 0/0;
} catch (ErrorException $e)
{
echo '发生错误:'.$e->getMessage();
Log::record('被除数不得为零', 'error');
}
- 对于各种日志级别,系统提供了一些快捷方式和助手函数,比如:
Log::error('错误日志!'); //Log::record('错误日志!', 'error')
Log::info('信息日志!'); //Log::record('信息日志!', 'info')
trace('错误日志!', 'error');
trace('信息日志!', 'info');
- 系统默认并不记录HTTP异常,因为这种异常容易遭受攻击不断写入日志;
- 除了系统提供的几种类型,也可以自己定义日志类型;
Log::diy('自定义日志');
- 在配置文件
log.php
中,可以设置限定日志文件的级别,不属于的无法写入;
'level' => ['error','info'],
- 在配置文件
log.php
中,添加转换为json格式;
'json' => true
- 使用
::getLog()
方法,可以获取写入到内存中的日志;
$logs = Log::getLog();
dump($logs);
- 使用
::clear()
方法,可以清理掉内存中的日志;
Log::clear();
- 在配置文件log.php中,可以设置以单独文件存储的方式;
'single' => true,
中间件
定义中间件
- 中间件的主要用于拦截和过滤HTTP请求,并进行相应处理;
- 这些请求的功能可以是URL重定向,权限验证等等;
- 为了进一步了解中间件的用法,我们首先定义一个基础的中间件;
- 可以通过命令行模式,在应用目录下生成一个中间件文件和文件夹;
php think make:middleware Check
namespace app\middleware;
class Check
{
public function handle($request, \Closure $next)
{
if ($request->param('name') == 'index') {
return redirect('../');
}
return $next($request);
}
}
- 然后将这个中间件进行注册,在应用目录下创建
middleware.php
中间件配置;
return [
app\middleware\Check::class
];
- 中间件的入口执行方法必须是
handle()
方法,第一参数请求,第二参数是闭包; - 业务代码判断请求的
name
如果等于index
,就拦截住,执行中间件,跳转到首页; - 但如果请求的
name
是lee
,那需要继续往下执行才行,不能被拦死; - 那么就需要
$next($request)
把这个请求去调用回调函数; - 中间件
handle()
方法规定需要返回response
对象,才能正常使用; - 而
$next($request)
执行后,就是返回的respons
对象; - 为了测试拦截后,无法继续执行,可以
return response()
助手函数测试;
前/后置中间件
- 将
$next($request)
放在方法底部的方式,属于前置中间件; - 前置中间件就是请求阶段来进行拦截验证,比如登录判断,跳转,权限等;
- 而后置中间件就是请求完毕之后再进行验证,比如写入日志等等;
public function handle($request, \Closure $next)
{
//中间件代码,前置
return $next($request);
}
public function handle($request, \Closure $next)
{
$response = $next($request);
//中间件代码,后置
return $response;
}
结束调度
- 中间件提供了一个
end()
方法,可以在中间件执行到最后时执行;
注意,在
end()
方法里面不能有任何的响应输出,因为回调触发的时候请求响应输出已经完成了
public function end(Response $response)
{
// 注意,在end方法里面不能有任何的响应输出,因为回调触发的时候请求响应输出已经完成了
}
路由中间件
- 创建一个给路由使用的中间件,判断路由的ID值实现相应的验证;
class Check
{
public function handle($request, \Closure $next)
{
if ($request->param('id') == 10) {
echo '是管理员!';
}
return $next($request);
}
}
- 路由方法提供了一个
middleware()
方法,让指定的路由采用指定的中间件;
Route::rule('ar/:id', 'Address/read')->middleware(\app\middleware\Auth::class);
Route::rule('ar/:id', 'Address/read')->middleware([Auth::class, Check::class]); //支持多个中间件,这里 use 了
- 也可以在
config/middleware.php
配置文件加中,配置别名支持;
'alias' => [
'Auth' => app\middleware\Auth::class,
'Check' => app\middleware\Check::class,
],
Route::rule('ar/:id', 'Address/read')->middleware(['Auth', 'Check']);
- 可以给中间件传递额外参数,通过中间件入口方法的第三个参数接收;
->middleware(Auth::class, 'ok');
public function handle($request, \Closure $next, $param, $param = '')
- 中间件也支持分组路由,闭包路由等;
Route::group('ar', function () {
Route::rule(':id', 'Address/read')
})->middleware(Auth::class);
Route::rule('ar/:id', 'Address/read')->middleware(function ($request, \Closure $next) {
if ($request->param('id') == 10) {
echo '是管理员!';
}
return $next($request);
});
控制器中间件
- 可以让中间件在控制器里注册,让这个控制器执行的时候执行中间件;
protected $middleware = ['Check'];
- 默认情况下,控制器中间件对所有操作方法有效,支持做限制;
protected $middleware = [
'Auth' => ['only' =>['index']],
'Check' => ['except' =>['read']],
];
- 中间件给控制器传递参数,通过
Request
对象实现;
$request->name = 'Mr.Lee';
服务系统
- 服务系统,可以将一个类的对象注册到容器中去,方便调用执行;
- 服务的执行优先级较高,在执行主体程序前就已经完成依赖注入;
- 它的作用可以做一些初始化,配置一些参数,扩展插件等等均可;
- 验证码扩展类就使用了服务系统,我们自己创建一个简单的服务;
- 在
common
目录下创建一个Shut.php
类,这个类是被服务的类;
//定义一个属性字段
protected static $name = 'Mr.Lee';
//设置
public static function setName($name)
{
self::$name = $name;
}
//获取
public function run()
{
halt(self::$name.'提醒您,系统已关闭...');
}
- 使用命令行,生成一个对
Shut.php
服务的服务类ShutSerice.php
;
php think make:service ShutService
- 服务类有两个方法,一个是服务注册
register()
,一个服务启动boot()
;
public function register()
{
//绑定到容器,将被服务的类注册到容器中去
$this->app->bind('shut', Shut::class);
}
public function boot()
{
//执行
Shut::setName('Mr.Wang');
}
- 最后一部,将系统服务配置到全局定义文件里,
service.php
;
return [
\app\service\ShutService::class,
];
- 最后在任意控制器测试即可,可以容易依赖注入或容器标识执行系统服务;
public function index(Shut $shut)
{
//依赖注入调用
$shut->run();
//容器标识调用
$this->app->shut->run();
return 'index';
}
事件
- 事件和中间件有一点相似,只不过事件更加的精准定位更细腻的业务场景;
- 事件可定义:事件类、事件监听类、事件订阅类;
- 我们先手动创建一个测试类;
public function __construct()
{
//注册监听器
Event::listen('TestListen', function ($param) {
echo '我是监听器,我被触发了!'.$param;
});
}
public function info()
{
echo '登录前准备!';
Event::trigger('TestListen', 'ok'); //触发监听器
event('TestListen'); //助手函数触发
}
- 我们也可以使用监听类来设计监听器,使用命令行创建;
php think make:listener TestListen
public function info()
{
echo '登录前准备!';
Event::listen('TestListen', TestListen::class); //这句可以定义到配置文件
Event::trigger('TestListen');
}
- 在
app/event.php
中,listen是配置监听类的,配置方式如下:
'listen' => [
'TestListen' => [\app\listener\TestListen::class]
],
-
而监听类被触发会自动执行
handle()
方法,实现监听功能;public function handle($event) { echo '我是监听类!'.$event; }
-
系统还内置了系统触发的事件,只要满足条件就会自动触发;
-
事件监听类,可以同时监听多个监听类,只要绑定到一个标识符即可;
'TestListen' => [ \app\listener\TestListen::class, \app\listener\TestOne::class, \app\listener\TestTwo::class ]
-
对于需要多个监听,监听类不够灵活,而且类会创建很多,可以使用订阅类;
-
订阅类就是将监听事件作为内部的方法用
on+方法名
来实现;
php think make:subscribe UserSub
class UserSub
{
public function onUserLogin(){
echo '处理登录后的监听!';
}
public function onUserLogout(){
echo '处理退出后的监听!';
}
}
-
然后,我们直接去
app/event.php
注册一下;'subscribe' => [ 'UserSub' => \app\subscribe\UserSub::class, ],
-
然后,两个方法分别监听两个事件方法,直接调用方法名即可;
public function login(){
echo '登录成功!';
Event::trigger('UserLogin');
}
public function logout(){
echo '退出成功!';
Event::trigger('UserLogout');
}
- 对于事件类,很少有场景需要使用它,毕竟系统提供的各种精确方案较多;
php think make:event UserEvent
class UserEvent
{
public function __construct()
{
echo '我是事件类!';
}
}
Event::trigger(new UserEvent());
多应用模式
- 由于多应用模式属于扩展,我们需要额外安装;
composer require topthink/think-multi-app
- 安装后,创建
index
和admin
两个应用目录文件夹; - 只要将
controller
和model
移入即可,修改相应的命名空间; - 将
view
也增加index
和admin
两个应用目录文件夹,移入相应文件夹; - 默认的应用为
index
,在app.php
修改即可;
// 默认应用
'default_app' => 'index',
- 我们可以做应用映射,比如将
admin
目录映射为think
,admin
废弃;
// 应用映射(自动多应用模式有效)
'app_map' => [
'think' => 'admin'
],
- 我们也可以做域名绑定,比如,后台用域名绑定,直接访问;
// 域名绑定(自动多应用模式有效)
'domain_bind' => [
'news.abc.com' => 'admin',
'*' => 'index'
],
- 路由修改:需要在应用目录单独建立路由,内部编码不需要更改;
最后一次更新于2020-09-12 08:20
0 条评论