ThinkPHP--基础

主要特性

  • 引入容器和Facade支持

  • 依赖注入完善和支持更多场景

  • 重构的(对象化)路由

  • 支持注解路由

  • 跨域请求支持

  • 配置和路由目录独立

  • 取消系统常量

  • 助手函数增强

  • 类库别名机制

  • 模型和数据库增强

  • 验证类增强

  • 模板引擎改进

  • 支持PSR-3日志规范

  • 中间件支持(V5.1.6+

  • 支持Swoole/Workerman运行(V5.1.18+

安装

环境要求

  • PHP >= 5.6.0
  • PDO PHP Extension
  • MBstring PHP Extension

Composer安装

第一次安装

composer create-project topthink/think=5.1.* tp5

已经安装过(切换到应用根目录下面)

composer update topthink/framework

* 修改composer.json中"topthink/framework": "5.1.*",再执行,否则报Nothing to modify in lock file,无法更新

命名规范

ThinkPHP5.1遵循PSR-2命名规范和PSR-4自动加载规范

PSR(PHP Standard Recommendations),由PHP FIG(Framework Interoperability Group)组织制定的 PHP 规范,是 PHP 开发的实践标准。

PSR-2

  • 代码必须遵循 PSR-1 中的编码规范 。

  • 代码必须使用4个空格符而不是 tab键 进行缩进。

    所有PHP文件必须以一个空白行作为结束。

    纯PHP代码文件必须省略最后的 ?> 结束标签。

  • 每行的字符数应该软性保持在80个之内, 理论上一定不可多于120个, 但一定不能有硬性限制。

    PHP所有 关键字必须全部小写。

    常量 true 、false 和 null 也必须全部小写。

  • 每个 namespace 命名空间声明语句和 use 声明语句块后面,必须插入一个空白行。

  • 类的开始花括号({)必须写在函数声明后自成一行,结束花括号(})也必须写在函数主体后自成一行。

  • 方法的开始花括号({)必须写在函数声明后自成一行,结束花括号(})也必须写在函数主体后自成一行。

    方法名称后一定不能有空格符,参数左括号后和右括号前一定不能有空格。

    参数列表中,每个参数后面必须要有一个空格,而前面一定不能有空格。

    有默认值的参数,必须放到参数列表的末尾。

  • 类的属性和方法必须添加访问修饰符(privateprotected 以及 public), abstract 以及 final必须声明在访问修饰符之前,而 static必须声明在访问修饰符之后。

  • 控制结构的关键字后必须要有一个空格符,而调用方法或函数时则一定不能有。

  • 控制结构的开始花括号({)必须写在声明的同一行,而结束花括号(})必须写在主体后自成一行。

  • 控制结构的开始左括号后和结束右括号前,都一定不能有空格符。

PSR-4

  • 完整的类名必须要有一个顶级命名空间,被称为 "vendor namespace";

  • 完整的类名可以有一个或多个子命名空间;

  • 完整的类名必须有一个最终的类名;

  • 完整的类名中任意一部分中的下滑线都是没有特殊含义的;

  • 完整的类名可以由任意大小写字母组成;

  • 所有类名都必须是大小写敏感的。

  • 当根据完整的类名载入相应的文件……

  • 完整的类名中,去掉最前面的命名空间分隔符,前面连续的一个或多个命名空间和子命名空间,作为“命名空间前缀”,其必须与至少一个“文件基目录”相对应;

  • 紧接命名空间前缀后的子命名空间必须与相应的”文件基目录“相匹配,其中的命名空间分隔符将作为目录分隔符。

  • 末尾的类名必须与对应的以 .php 为后缀的文件同名。

  • 自动加载器(autoloader)的实现一定不能抛出异常、一定不能触发任一级别的错误信息以及不应该有返回值。

目录和文件

  • 目录使用小写+下划线;
  • 类库、函数文件统一以.php为后缀;
  • 类的文件名均以命名空间定义,并且命名空间的路径和类库文件所在路径一致;
  • 类文件采用驼峰法命名(首字母大写),其它文件采用小写+下划线命名;
  • 类名和类文件名保持一致,统一采用驼峰法命名(首字母大写);

函数和类、属性命名

  • 类的命名采用驼峰法(首字母大写),例如 UserUserType,默认不需要添加后缀,例如UserController应该直接命名为User
  • 函数的命名使用小写字母和下划线(小写字母开头)的方式,例如 get_client_ip
  • 方法的命名使用驼峰法(首字母小写),例如 getUserName
  • 属性的命名使用驼峰法(首字母小写),例如 tableNameinstance
  • 特例:以双下划线__打头的函数或方法作为魔术方法,例如 __call 和 __autoload

常量和配置

  • 常量以大写字母和下划线命名,例如 APP_PATH
  • 配置参数以小写字母和下划线命名,例如 url_route_on 和url_convert
  • 环境变量定义使用大写字母和下划线命名,例如APP_DEBUG

数据表和字段

  • 数据表和字段采用小写加下划线方式命名,并注意字段名不要以下划线开头,例如 think_user 表和 user_name字段,不建议使用驼峰和中文作为数据表及字段命名。

目录结构

5.1版本目录结构的主要变化是配置目录和路由定义目录独立出来,不再放入应用类库目录(并且不可更改)。

www  WEB部署目录(或者子目录)
├─application           应用目录
│  ├─common             公共模块目录(可以更改)
│  ├─module_name        模块目录
│  │  ├─common.php      模块函数文件
│  │  ├─controller      控制器目录
│  │  ├─model           模型目录
│  │  ├─view            视图目录
│  │  ├─config          配置目录
│  │  └─ ...            更多类库目录
│  │
│  ├─command.php        命令行定义文件
│  ├─common.php         公共函数文件
│  └─tags.php           应用行为扩展定义文件
│
├─config                应用配置目录
│  ├─module_name        模块配置目录
│  │  ├─database.php    数据库配置
│  │  ├─cache           缓存配置
│  │  └─ ...            
│  │
│  ├─app.php            应用配置
│  ├─cache.php          缓存配置
│  ├─cookie.php         Cookie配置
│  ├─database.php       数据库配置
│  ├─log.php            日志配置
│  ├─session.php        Session配置
│  ├─template.php       模板引擎配置
│  └─trace.php          Trace配置
│
├─route                 路由定义目录
│  ├─route.php          路由定义
│  └─...                更多
│
├─public                WEB目录(对外访问目录)
│  ├─index.php          入口文件
│  ├─router.php         快速测试文件
│  └─.htaccess          用于apache的重写
│
├─thinkphp              框架系统目录
│  ├─lang               语言文件目录
│  ├─library            框架类库目录
│  │  ├─think           Think类库包目录
│  │  └─traits          系统Trait目录
│  │
│  ├─tpl                系统模板目录
│  ├─base.php           基础定义文件
│  ├─convention.php     框架惯例配置文件
│  ├─helper.php         助手函数文件
│  └─logo.png           框架LOGO文件
│
├─extend                扩展类库目录
├─runtime               应用的运行时目录(可写,可定制)
├─vendor                第三方类库目录(Composer依赖库)
├─build.php             自动生成定义文件(参考)
├─composer.json         composer 定义文件
├─LICENSE.txt           授权说明文件
├─README.md             README 文件
├─think                 命令行入口文件

* 由于5.1版本取消了系统路径的常量定义,因此系统的目录名称不可更改。如果需要更改应用目录或者入口文件位置,参考架构章节的入口文件部分。

配置

配置基础

ThinkPHP遵循惯例重于配置的原则,系统会按照下面的顺序来加载配置文件(配置的优先顺序从右到左)。

惯例配置->应用配置->模块配置->动态配置

├─config(应用配置目录)
│  ├─app.php            应用配置
│  ├─cache.php          缓存配置
│  ├─cookie.php         Cookie配置
│  ├─database.php       数据库配置
│  ├─log.php            日志配置
│  ├─session.php        Session配置
│  ├─template.php       模板引擎配置
│  ├─trace.php          Trace配置
│  └─ ...               更多配置文件
│  
├─route(路由目录)
│  ├─route.php          路由定义文件
│  └─ ...               更多路由定义文件
│  
├─application (应用目录)
│  └─module (模块目录)
│     └─config(模块配置目录)
│     	 ├─app.php            应用配置
│     	 ├─cache.php          缓存配置
│     	 ├─cookie.php         Cookie配置
│     	 ├─database.php       数据库配置
│     	 ├─log.php            日志配置
│     	 ├─session.php        Session配置
│     	 ├─template.php       模板引擎配置
│     	 ├─trace.php          Trace配置
│  	 └─ ...               更多配置文件
│  

* 5.1没有config.php配置文件,默认配置都在app.php配置文件,并且配置参数区分大小写

想要统一管理所有的配置文件,可以把模块目录下面的config目录移动到应用配置目录下面改为模块子目录的方式,调整后的配置目录的结构如下:

├─application(应用目录)
├─config(配置目录)
│  ├─module (模块配置目录)
│  │  ├─database.php    数据库配置
│  │  ├─cache           缓存配置
│  │  └─ ...            
│  │
│  ├─app.php            应用配置
│  ├─cache.php          缓存配置
│  ├─cookie.php         Cookie配置
│  ├─database.php       数据库配置
│  ├─log.php            日志配置
│  ├─session.php        Session配置
│  ├─template.php       模板引擎配置
│  └─trace.php          Trace配置
│  
├─route(路由配置目录)
│  ├─route.php          路由定义文件
│  └─ ...               更多路由定义文件

配置定义

可以直接在相应的应用或模块配置文件中修改或者增加配置参数,如果你要增加额外的配置文件,直接放入应用或模块配置目录即可(文件名小写)。

另外涉及到配置参数的定义有效性问题,下列配置参数在模块配置中定义(包括动态配置)无效,而必须在应用配置中设置

配置参数描述
app_debug应用调试模式(支持环境变量配置)
app_trace应用trace(支持环境变量配置)
class_suffix类后缀
default_filter默认过滤机制
root_namespace根命名空间
pathinfo_deprPATH_INFO分隔符
url_route_must路由强制模式
auto_bind_module自动绑定模块
default_lang默认语言
lang_switch_on多语言切换

由于架构设计原因,下面的配置只能在环境变量中修改。

配置参数描述
app_namespace应用命名空间
config_ext配置文件后缀

环境变量定义

在应用的根目录下定义一个特殊的.env环境变量文件,用于在开发过程中模拟环境变量配置(该文件建议在服务器部署的时候忽略),.env文件中的配置参数定义格式采用ini方式

APP_DEBUG =  true
APP_TRACE =  true

* 如果部署环境单独配置了环境变量( 环境变量的前缀使用PHP_),需要删除.env配置文件,避免冲突。

环境变量配置的参数会全部转换为大写,值为 nullno 和 false 等效于 "",值为 yes 和 true 等效于 "1"

环境变量不支持数组参数,如果需要使用数组参数可以,使用下划线分割定义配置参数名:

DATABASE_USERNAME =  root
DATABASE_PASSWORD =  123456

获取环境变量的值使用下面的方式:

Env::get('database_username');
Env::get('database_password');

如果使用

[DATABASE]
USERNAME =  root
PASSWORD =  123456

获取环境变量的值可以使用下面的方式获取:

Env::get('database.username');
Env::get('database.password');

* 要使用Env类,必须先引入think\facade\Env或者\Env。环境变量的获取不区分大小写

支持默认值,例如:

// 获取环境变量 如果不存在则使用默认值root
Env::get('database.username','root');

可以直接在配置文件中使用环境变量进行本地环境和服务器的自动配置,例如:

return [
    'hostname'  =>  Env::get('hostname','127.0.0.1'),
];

环境变量中设置的APP_DEBUGAPP_TRACE参数会自动生效(优先于应用的配置文件),其它参数则必须通过Env::get方法才能读取。

支持获取的系统路径变量包括:

系统路径Env参数名称
应用根目录root_path
应用目录app_path
框架目录think_path
配置目录config_path
扩展目录extend_path
composer目录vendor_path
运行缓存目录runtime_path
路由目录route_path
当前模块目录module_path

配置获取

要使用Config类,首先需要在你的类文件中引入

use think\facade\Config;

或者(因为系统做了类库别名,其实就是调用think\facade\Config

use Config;

然后就可以使用下面的方法读取某个配置参数的值:

echo Config::get('配置参数1');

如果你需要读取某个一级配置的所有配置参数,可以使用

Config::pull('app');

或者使用

Config::get('app.');

读取所有的配置参数:

dump(Config::get());

判断是否存在某个设置参数:

Config::has('配置参数2');

助手函数

系统定义了一个助手函数config,以上可以简化为:

echo config('配置参数1');

5.1的配置参数全部采用二级配置的方式(默认一级配置为app),所以当你使用config('name')的时候其实相当于使用:

config('app.name')

支持获取多级配置参数值,直接使用(必须从一级开始写)

config('app.name1.name2')

获取某个一级配置的所有参数可以使用

config('app.');

读取所有的配置参数:

dump(config());

或者你需要判断是否存在某个设置参数:

config('?配置参数2');

架构

入口文件

5.1默认的应用入口文件位于public/index.php

// [ 应用入口文件 ]
namespace think;

// 加载基础文件
require __DIR__ . '/../thinkphp/base.php';

// 执行应用并响应
Container::get('app')->run()->send();

更改应用目录和入口位置

新版框架默认不再支持改变应用目录(application)和入口文件位置,如果你需要更改,需要自己重新定义入口文件。

<?php
namespace think;

// 定义应用目录
define('APP_PATH', __DIR__ . '/app/');
// 加载框架基础引导文件
require __DIR__ . '/thinkphp/base.php';
// 添加额外的代码
// ...

// 执行应用并响应
Container::get('app', [APP_PATH])->run()->send();

如果是V5.1.2+版本,上面的最后一行代码可以使用下面的替代:

Container::get('app')->path(APP_PATH)->run()->send();

* 更改应用目录名称和位置可能导致默认的命令行操作失效,你需要同步自定义根目录下面的think文件。

模块设计

├─application           应用目录(可设置)
│  ├─common             公共模块目录(可选)
│  ├─module1            模块1目录
│  │  ├─common.php      模块函数文件
│  │  ├─config	      	模块配置目录(可选)
│  │  ├─controller      控制器目录
│  │  ├─model           模型目录(可选)
│  │  ├─view            视图目录(可选)
│  │  └─ ...            更多类库目录
│  │ 
│  ├─module2            模块2目录
│  │  ├─common.php      模块函数文件
│  │  ├─config	      	模块配置目录(可选)
│  │  ├─controller      控制器目录
│  │  ├─model           模型目录(可选)
│  │  ├─view            视图目录(可选)
│  │  └─ ...            更多类库目录

模块的配置目录也可以放到外面的config目录的模块子目录下面。

* 其中common模块是一个特殊的模块,默认是禁止直接访问的,一般用于放置一些公共的类库用于其他模块的继承。


命名空间

如果需要调用PHP内置的类库,或者第三方没有使用命名空间的类库,记得在实例化类库的时候加上 \

根命名空间(类库包)

系统内置的几个根命名空间(类库包)如下:

名称描述类库目录
think系统核心类库thinkphp/library/think
traits系统Trait类库thinkphp/library/traits
app应用类库application

 

应用类库包

为了避免和Composer自动加载的类库存在冲突 ,应用类库的命名空间的根都统一以app命名

如果觉得app根命名空间不合适或者有冲突,可以更改环境变量APP_NAMESPACE,如果你定义了.env文件的话,可以在里面添加:

APP_NAMESPACE =  application

定义后,应用类库的命名空间改为:

<?php
namespace application\index\model;

class User extends \think\Model
{
}

 

容器和依赖注入

容器类的工作由think\Container类完成,但大多数情况我们只需要通过app助手函数即可完成大部分操作。

依赖注入其实本质上是指对类的依赖通过构造器完成自动注入,例如在控制器架构方法和操作方法中一旦对参数进行对象类型约束则会自动触发依赖注入,由于访问控制器的参数都来自于URL请求,普通变量就是通过参数绑定自动获取,对象变量则是通过依赖注入生成。

依赖注入的对象参数支持多个,并且和顺序无关。

支持使用依赖注入的场景包括(但不限于):

  • 控制器架构方法;
  • 控制器操作方法;
  • 数据库和模型事件方法;
  • 路由的闭包定义;
  • 行为类的方法;

在ThinkPHP的设计中,think\App类虽然自身不是容器,但却是一个容器管理类,可以完成容器的所有操作。

V5.1.14+版本开始,应用类自身就是一个容器实例。

绑定

依赖注入的类统一由容器进行管理,大多数情况下是在自动绑定并且实例化的。不过你可以随时进行手动绑定类到容器中,支持多种绑定方式。

绑定类标识

可以对已有的类库绑定一个标识(唯一),便于快速调用。

// 绑定类库标识
bind('cache','think\Cache');
// 快速调用(自动实例化)
$cache = app('cache');

 

调用和绑定的标识必须保持一致(包括大小写)

系统已经内置绑定了核心常用类库,无需重复绑定,内置绑定到容器中的类库包括

系统类库容器绑定标识
think\Buildbuild
think\Cachecache
think\Configconfig
think\Cookiecookie
think\Debugdebug
think\Envenv
think\Hookhook
think\Langlang
think\Loglog
think\Requestrequest
think\Responseresponse
think\Routeroute
think\Sessionsession
think\Urlurl
think\Validatevalidate
think\Viewview

解析

助手函数方式

使用app助手函数进行容器中的类解析调用,对于已经绑定的类标识,会自动快速实例化

app('cache');

上面的app助手函数相当于调用了

Container::get('cache');

带参数实例化调用

app('cache',['file']);

对于没有绑定的类,也可以直接解析

app('org\utils\ArrayItem');

对象化调用

使用app助手函数获取容器中的对象实例(支持依赖注入)。

$app = app();
// 判断对象实例是否存在
isset($app->cache);

// 注册容器对象实例
$app->cache = think\Cache::class;

// 获取容器中的对象实例
$cache = $app->cache;

不带任何参数调用app助手函数其实是实例化think\App类,可以方便的操作容器、绑定和调用对象实例。

// 绑定类到容器
app()->test = new Test;
// 实例调用
$test = app()->test;

也就是说,你可以在任何地方使用app()方法调用容器中的任何类

// 调用配置类
app()->config->get('app_name');
// 调用session类
app()->session->get('user_name');

门面(Facade

门面为容器中的类提供了一个静态调用接口,相比于传统的静态方法调用, 带来了更好的可测试性和扩展性,你可以为任何的非静态类库定义一个facade类。\

说的直白一点,Facade功能可以让类无需实例化而直接进行静态方式调用。

核心Facade类库

系统给内置的常用类库定义了Facade类库,还给这些常用的核心类库的Facade类注册了类库别名,当进行静态调用的时候可以直接使用简化的别名进行调用。

(动态)类库Facade类别名类
think\Appthink\facade\AppApp
think\Buildthink\facade\BuildBuild
think\Cachethink\facade\CacheCache
think\Configthink\facade\ConfigConfig
think\Cookiethink\facade\CookieCookie
 think\DbDb
think\Debugthink\facade\DebugDebug
think\Envthink\facade\EnvEnv
think\Hookthink\facade\HookHook
think\Langthink\facade\LangLang
think\Logthink\facade\LogLog
think\Middlewarethink\facade\MiddlewareMiddleware
think\Requestthink\facade\RequestRequest
think\Responsethink\facade\ResponseResponse
think\Routethink\facade\RouteRoute
think\Sessionthink\facade\SessionSession
think\Urlthink\facade\UrlUrl
think\Validatethink\facade\ValidateValidate
think\Viewthink\facade\ViewView

无需进行实例化就可以很方便的进行方法调用,例如:

use think\facade\Cache;

Cache::set('name','value');
echo Cache::get('name');

前面的代码可以改成

\Cache::set('name','value');
echo \Cache::get('name');

Facade类定义了一个实例化的instance方法,如果你的类也有定义的话将会失效。

think\Db类的实现本来就类似于Facade机制,所以不需要再进行静态代理就可以使用静态方法调用(确切的说Db类是没有方法的,都是调用的Query类的方法)。

在进行依赖注入的时候,请不要使用Facade类作为类型约束,而是建议使用原来的动态类。

钩子和行为

ThinkPHP中的行为是一个比较抽象的概念,你可以把行为想象成在应用执行过程中的一个动作。在框架的执行流程中,例如路由检测是一个行为,静态缓存是一个行为,用户权限检测也是行为,大到业务逻辑,小到浏览器检测、多语言检测等等都可以当做是一个行为,甚至说你希望给你的网站用户的第一次访问弹出Hello,world!这些都可以看成是一种行为,把这些行为抽离出来的目的是为了让你无需改动框架和应用,而在外围通过扩展或者配置来改变或者增加一些功能。

而不同的行为之间也具有位置共同性,比如,有些行为的作用位置都是在应用执行前,有些行为都是在模板输出之后,我们把这些行为发生作用的位置称之为钩子,当应用程序运行到这个钩子的时候,就会被拦截下来,统一执行相关的行为,类似于AOP编程中的“切面”的概念,给某一个钩子绑定相关行为就成了一种类AOP编程的思想。

一个钩子可以注册多个行为,执行到某个钩子位置后,会按照注册的顺序依次执行相关的行为。但在某些特殊的情况下,你可以设置某个钩子只能执行一次行为,又或者你可以在一个钩子的某个行为中返回false来强制终止后续的行为执行;一个行为可以同时注册到多个不同的钩子上,完全看应用的需求来设计。

钩子的位置必须是事先设计好的,无论是框架还是应用的,要设置一个钩子,只需要在相关的位置添加一行代码(事先需要引入think\facade\Hook类):

Hook::listen('钩子名称','参数','是否只有一次有效返回值');

除了钩子名称之外,其它参数都是可选的,注意5.1版本第二个参数不支持引用传值。

系统核心设计提供了一些可能会需要的钩子(位置),尽可能的方便应用的扩展而不必改动框架核心,按照执行顺序依次如下:

钩子描述参数
app_init应用初始化标签位
app_dispatch应用调度标签位
app_begin应用开始标签位
module_init模块初始化标签位
action_begin控制器开始标签位当前的callback参数
view_filter视图输出过滤标签位当前模板渲染输出内容
app_end应用结束标签位当前响应对象实例
log_write日志write方法标签位当前写入的日志信息
log_level日志写入标签位包含日志类型和日志信息的数组(V5.1.25+
response_send响应发送标签位当前响应对象
response_end输出结束标签位当前响应对象实例

其中log_write钩子仅在调用Log::write方法的时候执行。

view_filter钩子v5.1.3+版本中已经废除,改用视图类的filter方法过滤。

行为定义

行为类的定义很简单,一般来说只需要定义一个行为入口方法run即可,例如:

namespace app\index\behavior;

class Test 
{
    public function run($params)
    {
        // 行为逻辑
    }
}

可以在行为方法中使用依赖注入,例如:

namespace app\index\behavior;

use think\Request;

class Test 
{
    public function run(Request $request, $params)
    {
        // 行为逻辑
    }
}

行为的入口方法名称支持自定义,如果需要更改在应用公共文件中添加下面的代码即可:

Hook::portal('portal');

入口方法名称就变成了portal

行为类并不需要继承任何类,相对比较灵活。如果行为类需要绑定到多个钩子,可以采用如下定义:

namespace app\index\behavior;

class Test 
{
    public function appInit($params)
    {

    }
    
    public function appEnd($params)
    {

    }    
}

该行为绑定到app_initapp_end钩子后 就会调用相关的方法,方法名就是钩子名称的驼峰命名(首字母小写)。

行为绑定

行为定义完成后,就需要绑定到某个标签位置才能生效,否则是不会执行的。

使用think\facade\Hook类的add方法注册行为,例如:

// 注册 app\index\behavior\CheckLang行为类到app_init标签位
Hook::add('app_init','app\\index\\behavior\\CheckLang'); 
//注册 app\admin\behavior\CronRun行为类到app_init标签位
Hook::add('app_init','app\\admin\\behavior\\CronRun');

如果要批量注册行为的话,可以使用:

Hook::add('app_init',['app\\index\\behavior\\CheckAuth','app\\index\\behavior\\CheckLang','app\\admin\\behavior\\CronRun']);

当应用运行到app_init标签位的时候,就会依次调用app\index\behavior\CheckAuthapp\index\behavior\CheckLangapp\admin\behavior\CronRun行为。如果其中一个行为中有中止代码的话则后续不会执行,如果返回false则当前标签位的后续行为将不会执行,但应用将继续运行。

我们也可以直接在应用目录下面或者模块的目录下面定义tags.php文件来统一定义行为,定义格式如下:

return [
    'app_init'=> [
        'app\\index\\behavior\\CheckAuth',
        'app\\index\\behavior\\CheckLang'
    ],
    
    'app_end'=> [
        'app\\admin\\behavior\\CronRun'
    ]
]

如果应用目录下面和模块目录下面的tags.php都定义了app_init的行为绑定的话,会采用合并模式,如果希望覆盖,那么可以在模块目录下面的tags.php中定义如下:

return [
    'app_init'=> [
        'app\\index\\behavior\\CheckAuth',
        '_overlay'=>true
    ],
    'app_end'=> [
        'app\\admin\\behavior\\CronRun'
    ]
]

如果某个行为标签定义了'_overlay' =>true 就表示覆盖之前的相同标签下面的行为定义。

中间件

中间件主要用于拦截或过滤应用的HTTP请求,并进行必要的业务处理。

定义中间件

可以通过命令行指令快速生成中间件

php think make:middleware Check

这个指令会 application/http/middleware目录下面生成一个Check中间件。

<?php

namespace app\http\middleware;

class Check
{
    public function handle($request, \Closure $next)
    {
        if ($request->param('name') == 'think') {
            return redirect('index/think');
        }

        return $next($request);
    }
}

中间件的入口执行方法必须是handle方法,而且第一个参数是Request对象,第二个参数是一个闭包。

中间件handle方法的返回值必须是一个Response对象。

在这个中间件中我们判断当前请求的name参数等于think的时候进行重定向处理。否则,请求将进一步传递到应用中。要让请求继续传递到应用程序中,只需使用 $request 作为参数去调用回调函数 $next 。

在某些需求下,可以使用第三个参数传入额外的参数。

<?php

namespace app\http\middleware;

class Check
{
    public function handle($request, \Closure $next, $name)
    {
        if ($name == 'think') {
            return redirect('index/think');
        }

        return $next($request);
    }
}

前置/后置中间件

中间件是在请求具体的操作之前还是之后执行,完全取决于中间件的定义本身。

下面是一个前置行为的中间件

<?php

namespace app\http\middleware;

class Before
{
    public function handle($request, \Closure $next)
    {
        // 添加中间件执行代码

        return $next($request);
    }
}

下面是一个后置行为的中间件

<?php

namespace app\http\middleware;

class After
{
    public function handle($request, \Closure $next)
    {
		$response = $next($request);

        // 添加中间件执行代码

        return $response;
    }
}

来个比较实际的例子,我们需要判断当前浏览器环境是在微信或支付宝

namespace app\http\middleware;

/**
 * 访问环境检查,是否是微信或支付宝等
 */
class InAppCheck
{
    public function handle($request, \Closure $next)
    {
        if (preg_match('~micromessenger~i', $request->header('user-agent'))) {
            $request->InApp = 'WeChat';
        } else if (preg_match('~alipay~i', $request->header('user-agent'))) {
            $request->InApp = 'Alipay';
        }
        return $next($request);
    }
}

然后在你的移动版的module里添加一个middleware.php文件
例如:/path/application/mobile/middleware.php

return [
    app\http\middleware\InAppCheck::class,
];

然后在你的controller中可以通过$this->request->InApp获取相关的值

注册中间件

路由中间件

最常用的中间件注册方式是注册路由中间件

使用闭包定义中间件

你不一定要使用中间件类,在某些简单的场合你可以使用闭包定义中间件,但闭包函数必须返回Response对象实例。

Route::group('hello', function(){
	Route::rule('hello/:name','hello');
})->middleware(function($request,\Closure $next){
    if ($request->param('name') == 'think') {
        return redirect('index/think');
    }
    
	return $next($request);
});

全局中间件

你可以在应用目录下面定义middleware.php文件,使用下面的方式:

<?php

return [
	\app\http\middleware\Auth::class,
    'Check',
    'Hello',
];

中间件的注册应该使用完整的类名,如果没有指定命名空间则使用app\http\middleware作为命名空间。

全局中间件的执行顺序就是定义顺序。可以在定义全局中间件的时候传入中间件参数,支持两种方式传入。

<?php

return [
	[\app\http\middleware\Auth::class, 'admin'],
    'Check',
    'Hello:thinkphp',
];

上面的定义表示 给Auth中间件传入admin参数,给Hello中间件传入thinkphp参数。

模块中间件

V5.1.8+版本开始,支持模块中间件定义,你可以直接在模块目录下面增加middleware.php文件,定义方式和应用中间件定义一样,只是只会在该模块下面生效。

控制器中间件

V5.1.17+版本开始,支持为控制器定义中间件。首先你的控制器需要继承系统的think\Controller类,然后在控制器中定义middleware属性,如果需要设置控制器中间的生效操作,可以如下定义:

<?php
namespace app\index\controller;

use think\Controller;

class Index extends Controller
{
    protected $middleware = [ 
    	'Auth' 	=> ['except' 	=> ['hello'] ],
        'Hello' => ['only' 		=> ['hello'] ],
    ];

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

    public function hello()
    {
        return 'hello';
    }
}

中间件向控制器传参

可以通过给请求对象赋值的方式传参给控制器(或者其它地方),例如

<?php

namespace app\http\middleware;

class Hello
{
    public function handle($request, \Closure $next)
    {
        $request->hello = 'ThinkPHP';
        
        return $next($request);
    }
}

注意,传递的变量名称不要和param变量有冲突。

然后在控制器的方法里面可以直接使用

public function index(Request $request)
{
	return $request->hello; // ThinkPHP
}

控制器

控制器文件通常放在application/module/controller下面,类名和文件名保持大小写一致,并采用驼峰命名(首字母大写)。

定义

单一模块控制器

在应用配置文件app.php中设置

        // 是否支持多模块
        'app_multi_module'       => false,

输出转换

默认情况下,控制器的返回输出不会做任何的数据处理,但可以设置输出格式,并进行自动的数据转换处理,前提是控制器的输出数据必须采用return的方式返回。

// 默认输出类型
'default_return_type'   => 'json',

多级控制器

支持任意层次级别的控制器,并且支持路由。

控制器初始化

如果你的控制器类继承了系统控制器基类(\think\Controller)的话,可以定义控制器初始化方法initialize,该方法会在调用控制器的方法之前首先执行,如非必要,不建议直接修改控制器的架构函数。

前置操作

可以为某个或者某些操作指定前置执行的操作方法,设置 beforeActionList属性可以指定某个方法为其他方法的前置操作,数组键名为需要调用的前置方法名,无值的话为当前控制器下所有方法的前置方法。

['except' => '方法名,方法名']

表示这些方法不使用前置方法,

['only' => '方法名,方法名']

表示只有这些方法使用前置方法。

<?php
namespace app\index\controller;

use think\Controller;

class Index extends Controller
{
    protected $beforeActionList = [
        'first',
        'second' =>  ['except'=>'hello'],
        'three'  =>  ['only'=>'hello,data'],
    ];
    
    protected function first()
    {
        echo 'first<br/>';
    }
    
    protected function second()
    {
        echo 'second<br/>';
    }
    
    protected function three()
    {
        echo 'three<br/>';
    }

    public function hello()
    {
        return 'hello';
    }
    
    public function data()
    {
        return 'data';
    }
}

分层控制器

<?php
namespace app\index\event;

class Blog 
{
    public function insert()
    {
        return 'insert';
    }
    
    public function update($id)
    {
        return 'update:'.$id;
    }
    
    public function delete($id)
    {
        return 'delete:'.$id;
    }
}

调用

$event = \think\facade\App::controller('Blog', 'event');
echo $event->update(5); // 输出 update:5

controller助手函数直接实例化多层控制器

$event = controller('Blog', 'event');
echo $event->update(5); // 输出 update:5

直接调用分层控制器类的某个方法

echo \think\facade\App::action('Blog/update', ['id' => 5], 'event'); // 输出 update:5

助手函数action

echo action('Blog/update', ['id' => 5], 'event'); // 输出 update:5

控制器中间件 

V5.1.17+版本开始,支持为控制器定义中间件。首先你的控制器需要继承系统的think\Controller类,然后在控制器中定义middleware属性,例如:

<?php
namespace app\index\controller;

use think\Controller;

class Index extends Controller
{
    protected $middleware = [ 
    	'Auth' 	=> ['except' 	=> ['hello'] ],
        'Hello' => ['only' 		=> ['hello'] ],
    ];

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

    public function hello()
    {
        return 'hello';
    }
}

控制器传参

可以通过给请求对象赋值的方式传参给控制器(或者其它地方),例如

<?php

namespace app\http\middleware;

class Hello
{
    public function handle($request, \Closure $next)
    {
        $request->hello = 'ThinkPHP';
        
        return $next($request);
    }
}

注意,传递的变量名称不要和param变量有冲突。

然后在控制器的方法里面可以直接使用

public function index(Request $request)
{
	return $request->hello; // ThinkPHP
}

请求对象

构造方法注入

<?php

namespace app\index\controller;

use think\Request;

class Index 
{
    /**
     * @var \think\Request Request实例
     */
    protected $request;
    
    /**
     * 构造方法
     * @param Request $request Request对象
     * @access public
     */
    public function __construct(Request $request)
    {
		$this->request = $request;
    }
    
    public function index()
    {
		return $this->request->param('name');
    }    
}

如果你继承了系统的控制器基类think\Controller的话,系统已经自动完成了请求对象的构造方法注入了,你可以直接使用$this->request属性调用当前的请求对象。

<?php

namespace app\index\controller;

use think\Controller;

class Index extends Controller
{
    
    public function index()
    {
		return $this->request->param('name');
    }    
}

操作方法注入

另外一种选择是在每个方法中使用依赖注入。

<?php

namespace app\index\controller;

use think\Controller;
use think\Request;

class Index extends Controller
{
    
    public function index(Request $request)
    {
		return $request->param('name');
    }    
}

无论是否继承系统的控制器基类,都可以使用操作方法注入。

Facade调用

在没有使用依赖注入的场合,可以通过Facade机制来静态调用请求对象的方法(注意use引入的类库区别)。

<?php

namespace app\index\controller;

use think\Controller;
use think\facade\Request;

class Index extends Controller
{
    
    public function index()
    {
		return Request::param('name');
    }    
}

该方法也同样适用于依赖注入无法使用的场合。

助手函数

为了简化调用,系统还提供了request助手函数,可以在任何需要的时候直接调用当前请求对象。

<?php

namespace app\index\controller;

use think\Controller;

class Index extends Controller
{

    public function index()
    {
        return request()->param('name');
    }
}

无论是否继承系统的控制器基类,都可以使用

请求信息

方法含义
host当前访问域名或者IP
scheme当前访问协议
port当前访问的端口
remotePort当前请求的REMOTE_PORT
protocol当前请求的SERVER_PROTOCOL
contentType当前请求的CONTENT_TYPE
domain当前包含协议的域名
subDomain当前访问的子域名
panDomain当前访问的泛域名
rootDomain当前访问的根域名(V5.1.6+
url当前完整URL
baseUrl当前URL(不含QUERY_STRING)
query当前请求的QUERY_STRING参数
baseFile当前执行的文件
rootURL访问根地址
rootUrlURL访问根目录
pathinfo当前请求URL的pathinfo信息(含URL后缀)
path请求URL的pathinfo信息(不含URL后缀)
ext当前URL的访问后缀
time获取当前请求的时间
type当前请求的资源类型
method当前请求类型

输入变量

检测变量是否设置

Request::has('id','get');
Request::has('name','post');

变量获取

变量类型方法('变量名/变量修饰符','默认值','过滤方法')

方法描述
param获取当前请求的变量
get获取 $_GET 变量
post获取 $_POST 变量
put获取 PUT 变量
delete获取 DELETE 变量
session获取 $_SESSION 变量
cookie获取 $_COOKIE 变量
request获取 $_REQUEST 变量
server获取 $_SERVER 变量
env获取 $_ENV 变量
route获取 路由(包括PATHINFO) 变量
file获取 $_FILES 变量

获取部分变量

// 只获取当前请求的id和name变量
Request::only('id,name');

或者使用数组方式

// 只获取当前请求的id和name变量
Request::only(['id','name']);

V5.1.3+版本开始,only方法可以支持批量设置默认值,如下 

// 设置默认值
Request::only(['id'=>0,'name'=>'']);

默认获取的是当前请求参数(PARAM类型变量),如果需要获取其它类型的参数,可以在第二个参数传入,例如:

// 只获取GET请求的id和name变量
Request::only(['id','name'], 'get');
// 只获取POST请求的id和name变量
Request::only(['id','name'], 'post');

支持排除某些变量后获取,例如

// 排除id和name变量
Request::except('id,name');

或者使用数组方式

// 排除id和name变量
Request::except(['id','name']);

同样支持指定变量类型获取:

// 排除GET请求的id和name变量
Request::except(['id','name'], 'get');
// 排除POST请求的id和name变量
Request::except(['id','name'], 'post');

变量修饰符

Request::变量类型('变量名/修饰符');

修饰符作用
s强制转换为字符串类型
d强制转换为整型类型
b强制转换为布尔类型
a强制转换为数组类型
f强制转换为浮点类型

修改变量

V5.1.12+版本开始,可以(通常是在中间件里面)设置请求变量的值。

<?php

namespace app\http\middleware;

class Check
{
    public function handle($request, \Closure $next)
    {
        if ('think' == $request->name) {
        	$request->name = 'ThinkPHP';
        }

        return $next($request);
    }
}

助手函数

为了简化使用,还可以使用系统提供的input助手函数完成上述大部分功能。

判断变量是否定义

input('?get.id');
input('?post.name');

获取PARAM参数

input('param.name'); // 获取单个参数
input('param.'); // 获取全部参数
// 下面是等效的
input('name'); 
input('');

使用变量修饰符

input('get.id/d');
input('post.name/s');
input('post.ids/a');

获取请求类型

用途方法
获取当前请求类型method
判断是否GET请求isGet
判断是否POST请求isPost
判断是否PUT请求isPut
判断是否DELETE请求isDelete
判断是否AJAX请求isAjax
判断是否PJAX请求isPjax
判断是否为JSON请求isJson(V5.1.38+
判断是否手机访问isMobile
判断是否HEAD请求isHead
判断是否PATCH请求isPatch
判断是否OPTIONS请求isOptions
判断是否为CLI执行isCli
判断是否为CGI模式isCgi

HTTP头信息

可以使用Request对象的header方法获取当前请求的HTTP请求头信息,例如:

$info = Request::header();
echo $info['accept'];
echo $info['accept-encoding'];
echo $info['user-agent'];

也可以直接获取某个请求头信息,例如:

$agent = Request::header('user-agent');

HTTP请求头信息的名称不区分大小写,并且_会自动转换为-,所以下面的写法都是等效的:

$agent = Request::header('user-agent');
$agent = Request::header('User-Agent');
$agent = Request::header('User_Agent');
$agent = Request::header('USER_AGENT');

响应

设置数据

不需要关注Response对象本身,只需要在控制器的操作方法中返回数据即可。

Response基类提供了data方法用于设置响应数据。

response()->data($data);
json()->data($data);

如果要获取当前响应对象实例的实际输出数据可以使用getContent方法。

设置状态码

Response基类提供了code方法用于设置响应数据,但大部分情况一般我们是直接在调用助手函数的时候直接传入状态码,例如:

json($data,201);
view($data,401);

或者在后面链式调用code方法是等效的:

json($data)->code(201);

除了redirect函数的默认返回状态码是302之外,其它方法没有指定状态码都是返回200状态码。

如果要获取当前响应对象实例的状态码的值,可以使用getCode方法。

设置头信息

可以使用Response类的header设置响应的头信息

json($data)->code(201)->header(['Cache-control' => 'no-cache,must-revalidate']);

header方法支持两种方式设置,如果传入数组,则表示批量设置,如果传入两个参数,第一个参数表示头信息名,第二个参数表示头信息的值,例如:

// 单个设置
header('Cache-control', 'no-cache,must-revalidate');
// 批量设置
header([
    'Cache-control' => 'no-cache,must-revalidate',
    'Last-Modified' => gmdate('D, d M Y H:i:s') . ' GMT',
]);

除了header方法之外,Response基类还提供了常用头信息的快捷设置方法:

方法名作用
lastModified设置Last-Modified头信息
expires设置Expires头信息
eTag设置ETag头信息
cacheControl设置Cache-control头信息
contentType设置Content-Type头信息

你可以使用getHeader方法获取当前响应对象实例的头信息。

 

 

 

 

 

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值