ThinkPHP是一个支持MVC模式的、免费开源的轻量级PHP开发框架,使用Apache2开源许可证。
ThinkPHP支持Composer安装:
$ composer create-project topthink/think=5.0.* thinkphp
$ ls
application composer.json LICENSE.txt runtime vendor
build.php composer.lock public think
CHANGELOG.md extend README.md thinkphp
$ php -S localhost:8080 -t public
TODO:?????????????
配置加载顺序如下,优先级越来越低:
-
框架配置/惯例配置:框架自带的默认配置
ThinkPHP通过
\think\Config::get()
和\think\Config::set()
来获取和更改属性值。下面我们尝试在ThinkPHP框架中调用该函数:/* 更改application/index/controller/index.php */ <?php namespace app\index\controller; use think\Config; class Index{ public function index(){ echo json_encode(Config::get(),JSON_PRETTY_PRINT); } }
$ curl localhost:8080 { "app_debug": true, "app_trace": false, "app_status": "", "app_multi_module": true, "auto_bind_module": false, "root_namespace": [], "extra_file_list": [ "C:\\WWW\\thinkphp\\helper.php" ], "default_return_type": "html", "default_ajax_return": "json", "default_jsonp_handler": "jsonpReturn", "var_jsonp_handler": "callback", "default_timezone": "PRC", "lang_switch_on": false, "default_filter": "", "default_lang": "zh-cn", "class_suffix": false, "controller_suffix": false, "default_module": "index", "deny_module_list": [ "common" ], "default_controller": "Index", "default_action": "index", "default_validate": "", "empty_controller": "Error", "use_action_prefix": false, "action_suffix": "", "controller_auto_search": false, "var_pathinfo": "s", "pathinfo_fetch": [ "ORIG_PATH_INFO", "REDIRECT_PATH_INFO", "REDIRECT_URL" ], "pathinfo_depr": "\/", "url_html_suffix": "html", "url_common_param": false, "url_param_type": 0, "url_route_on": true, "route_config_file": [ "route" ], "route_complete_match": false, "url_route_must": false, "url_domain_deploy": false, "url_domain_root": "", "url_convert": true, "url_controller_layer": "controller", "var_method": "_method", "var_ajax": "_ajax", "var_pjax": "_pjax", "request_cache": false, "request_cache_expire": null, "request_cache_except": [], "template": { "type": "Think", "view_path": "", "view_suffix": "html", "view_depr": "\\", "tpl_begin": "{", "tpl_end": "}", "taglib_begin": "{", "taglib_end": "}" }, "view_replace_str": [], "dispatch_success_tmpl": "C:\\WWW\\thinkphp\\tpl\\dispatch_jump.tpl", "dispatch_error_tmpl": "C:\\WWW\\thinkphp\\tpl\\dispatch_jump.tpl", "exception_tmpl": "C:\\WWW\\thinkphp\\tpl\\think_exception.tpl", "error_message": "页面错误!请稍后再试~", "show_error_msg": false, "exception_handle": "", "log": { "type": "File", "path": "C:\\WWW\\runtime\\log\\", "level": [] }, "trace": { "type": "Html" }, "cache": { "type": "File", "path": "C:\\WWW\\runtime\\cache\\", "prefix": "", "expire": 0 }, "session": { "id": "", "var_session_id": "", "prefix": "think", "type": "", "auto_start": true }, "cookie": { "prefix": "", "expire": 0, "path": "\/", "domain": "", "secure": false, "httponly": "", "setcookie": true }, "database": { "type": "mysql", "dsn": "", "hostname": "127.0.0.1", "database": "", "username": "root", "password": "", "hostport": "", "params": [], "charset": "utf8", "prefix": "", "debug": true, "deploy": 0, "rw_separate": false, "master_num": 1, "slave_no": "", "fields_strict": true, "resultset_type": "array", "auto_timestamp": false, "datetime_format": "Y-m-d H:i:s", "sql_explain": false }, "paginate": { "type": "bootstrap", "var_page": "page", "list_rows": 15 }, "queue": { "connector": "Sync" } }
-
全局配置:
application/config.php
编辑
application/config.php
:/* 编辑application/config.php */ <?php return [ ...... "YanerMessage" => "Hello, World", ...... ]
刷新页面即可看到全局配置:
$ curl localhost:8080 { ...... "yanermessage": "Hello, World", ...... }
-
扩展配置:
application/extra/*
在
application/extra
目录下新建一个amqp.php
,用于模拟AMQP协议:/* 新建application/extra/amqp.php */ <?php return [ 'conn' => 'amqp://localhost:5672' ];
在
application/index/controller/index.php
中输出框架配置:/* 查看application/index/controller/index.php */ ...... public function index(){ echo json_encode(Config::get(),JSON_PRETTY_PRINT); } ......
即可在框架配置中查看
amqp.php
扩展的配置:$ curl localhost:8080 { ...... "amqp": { "conn": "amqp:\/\/localhost:5672" }, ...... }
-
场景配置:
app_status
常量先根据第3条设置“扩展配置”,然后在
application/
目录下新建home.php
:/* 新建application/home.php */ <?php return [ 'amqp' => [ 'conn' => 'I am in home now!' ] ];
将
application/config.php
的app_status
属性更改为home
,刷新页面即可看到amqp
扩展的输出发生变化:$ curl localhost:8080 { ...... "amqp": { "conn": "I am in home now!" }, ...... }
也可以接着在
application/index
目录下新建home.php
:/* 新建application/index/home.php */ <?php return [ 'amqp' => [ 'conn' => 'I am index module home config' ] ];
刷新页面即可看到变化:
$ curl localhost:8080 { ...... "amqp": { "conn": "I am index module home config" }, ...... }
-
模块配置:
application/模块名/config.php
在
application/index
目录下新建config.php
:/* 新建application/index/config.php */ <?php return [ 'amqp' => [ 'conn' => 'I am in index module now!' ] ];
刷新页面即可看到变化:
$ curl localhost:8080 { ...... "amqp": { "conn": "I am in index module now!" }, ...... }
-
动态配置:自定义
Config
类编辑
application/index/controller
目录下的index.php
:/* 编辑application/index/controller/index.php */ class Index { public function index(){ ...... } public function configTest(){ var_dump(Config::get("test")); Config::set("test","Hello World"); var_dump(Config::get("test")); } }
访问
localhost:8080/index/index/coonfigTest
:$ curl localhost:8080/index/index/configTest C:\WWW\application\index\controller\Index.php:13:null C:\WWW\application\index\controller\Index.php:15:string 'Hello World' (length=11)
ThinkPHP 5支持PATHINFO规则进行路由:
http://server/module/controller/action/param/value/
ThinkPHP 5通过application/config.php
中的url_route_on
和url_route_must
这两个布尔参数控制陆路由的行为,由此可以延伸出三种路由模式——普通模式、混合模式、强制模式:
路由模式 | url_route_on 参数值 |
url_route_must 参数值 |
含义 |
---|---|---|---|
普通模式(PATHINFO模式) | false |
只执行PATHINFO规则 | |
混合模式(默认) | true |
false |
优先执行自定义路由,然后PATHINIFO规则 |
强制模式 | true |
true |
强制要求所有请求必须设置路由 |
编码定义指的是在application/route.php
中以硬编码的方式定义路由:
/* 编辑applicationi/route.php */
// \think\Route::rule(路由表达式,路由地址,请求方法,路由条件,变量规则);
\think\Route::rule('news/:id','index/News/read','GET|POST');
// 将GET或POST方法访问news/123视作访问index模块的News控制器的read方法
ThinkPHP 5还支持单独指定HTTP请求方法:
\think\Route::get ('news/:id','index/News/read'); // 匹配GET方法
\think\Route::post ('news/:id','index/News/read'); // 匹配POST方法
\think\Route::put ('news/:id','index/News/read'); // 匹配PUT方法
\think\Route::delete('news/:id','index/News/read'); // 匹配DELETE方法
\think\Route::any ('news/:id','index/News/read'); // 匹配任意方法
编码定义指的是在application/route.php
中以返回数组的方式定义路由:
<?php
return [
'news/:id' => 'index/News/read', // 定义必选变量
'news/[:id]' => 'index/News/read', // 定义可选变量
'news/:id$' => 'index/News/read', // 定义完全匹配
'news/:id' => 'index/News/read?status=1', // 定义隐式传参
'news/:id' => [ // 限制变量类型
'index/News/read',
['ext'=>'html'],
['id'=>'\d{4}']
]
]
在满足路由定义的前提下,路由条件负责是否接受/拒绝该请求:
路由参数名称 | 数据类型 | 作用 |
---|---|---|
method |
string |
允许的HTTP请求方法(支持` |
ext |
string |
允许的URL后缀(支持` |
deny_ext |
string |
禁止的URL后缀(支持` |
https |
bool |
是否接受https请求 |
domain |
string |
允许的域名 |
before_behaviour |
function |
前置行为检测,通过返回的布尔值决定是否接受 |
callback |
function |
自定义条件函数,通过返回的布尔值决定是否接受 |
merge_extra_vars |
bool |
合并额外参数 |
bind_model |
array |
绑定模型 |
cache |
integer |
当前路由缓存的保存时长(秒) |
param_depr |
string |
路由参数分隔符 |
ajax |
bool |
是否接受ajax请求 |
pjax |
bool |
是否接受pjax请求 |
/* application/route.php */
return [
'news/:id' => [
'news/show/:name$',
[
'method' => 'get|post', // 只允许GET和POST方法
'ext' => 'shtml', // 只允许shtml后缀
'deny_ext' => 'shtml', // 不允许shtml后缀
'https' => true, // 只允许https
'domain' => 'www.example.com', // 只允许www.wxample.com域名
'before_behaviour' => 'app\index\behaviour\before', // 调用index模块的before()方法,根据布尔返回值决定是否匹配
'callback' => function(){return true;}, // 根据布尔返回值决定是否匹配
'merge_extra_vars' => true, // 合并额外参数,例如访问/news/show/a/b/c,得到的name仅为a/b/c
'bind_model' => ['User','name'], // 将绑定到User模型的name属性
'cache' => 3600, // 缓存当前路由一小时
'param_depr' => '///', // 使用///作为分隔符,而不是默认的/
'ajax' => true, // 允许ajax请求
'pjax' => true // 允许pjax请求
]
]
]
路由地址指的是路由匹配成功后执行的操作,分为以下五种:
控制器定义如下:
$ tree ./application
│ ......
├─index
│ └ ......
└─news
└─controller
News.php
/* 新建application/news/controller/News.php */
<?php
namespace app\news\controller; // 注意命名空间!
class News{
public function read($id){
echo '您访问的是第'.$id.'号新闻';
}
}
测试可以正常访问:
$ curl localhost:8080/news/news/show/id/000001
您访问的是第000001号新闻
现在设置路由:
/* 修改application/route.php */
'news/:id' => 'index/news/read'
测试路由:
$ curl localhost:8080/news/000001
您访问的是第000001号新闻
这种方式会向浏览器发送301或302的HTTP状态码,路由配置如下:
/* 修改application/route.php */
'news/:id' => 'news/show/:id.html' // 站内跳转
'news/:id' => 'http://www.example.com/news/show/:id.html' // 站外跳转
这种方式不需要按照PATHINFO模式走一遍,也不用实例化控制器类,而是直接执行调用方法。因此不能获取到当前模块名、控制器名、方法名,因为ThinkPHP框架没有初始化这些变量,获取时会报错:
/* 修改application/route.php */
'news/:id' => '@news/news/show/'
测试路由:
$ curl localhost:8080/news/000001
您访问的是第000001号新闻
这种方式允许访问包括控制器类在内的任何类的静态方法:
/* 修改application/route.php */
'news/:id' => 'app\index\controller\News::show'
这种方式允许直接在application/router.php
直接定义路由的内容:
/* 修改application/route.php */
Route::get(
'news/:id',
function($id){
return '您访问的是第'.$id.'号新闻';
}
)
我们可以将多个路由合并到一个分组中:
/* 修改application/route.php */
// 两条独立的路由
'news/:id' => ['index/news/show',['method'=>'get']],
'news/post/:id' => ['index/news/post',['method'=>'post']]
// 一个路由分组
'[news]' => [
':id' => ['index/news/show',['method'=>'get']],
'post/:id' => ['index/news/post',['method'=>'post']]
]
404路由__miss__
用于处理所有路由规则不匹配的情况:
/* 修改application/route.php */
'[news]' => [
':id' => ['index/news/show',['method'=>'get']],
'__miss__' => 'index/index/notfound' // 局部404路由
],
'__miss__' => 'index/index/notfound' // 全局404路由
如果当前的入口文件只使用一个模块,则可以使用路由绑定以简化路由定义,不用每次都声明模块名:
/* 修改application/route.php */
Route::bind('index'); // 绑定index模块
'news/:id' => 'news/show'
可以通过Url::build()
或url()
生成路由:
/* 修改application/route.php */
Url::build(路由地址,参数,伪静态后缀,是否加上域名);
url(路由地址,参数,伪静态后缀,是否加上域名);
'news/:id' => 'news/show'
url('news/show',['id'=>1],'html'); // 生成的URL为"/news/1.html"
控制器(Controller)是MVC模式中负责协调视图和模型的一层。ThinkPHP 3要求所有控制器类必须继承Controller
类,而ThinkPHP 5开始不再强制要求。
我们之前已经接触过控制器了,最简单的例子在application/index/controller/index.php
:
/* 查看application/index/controller/index.php */
<?php
namespace app\index\controller;
class Index{
public function index(){
return "......ThinkPHP 5 由亿速云提供......";
}
}
注意:ThinkPHP 3的所有控制器类的方法全部使用
echo
进行输出,而ThinkPHP 5既可以使用echo
,也可以使用return
,其中官方更推荐return
。
ThinkPHP框架使用类的反射来实例化控制类,这使得我们不能轻易更改__construct()
方法的形参列表进行初始化。为弥补这一缺陷,ThinkPHP提供了_initialize()
方法作为替代品。
ThinkPHP框架在控制器中提供了$beforeActionList
数组,用于为某个方法指定前置操作:
public $beforeActionList = [
'方法名', // 所有方法都会预先执行该方法
'方法名'=>['except'=>'action1,action2,...'], // 数组内的action不预先执行该方法
'方法名'=>['only'=>['action1,action2,...']] // 只有数组内的action预先执行该方法
];
我们到目前为止所用的控制器全都定义于application/模块名/controller/控制器类名.php
。事实上,为了便于模块化开发,我们也可以在application/模块名/controller
目录下新建文件夹,在文件夹内编写其它控制器类:
/* 创建application/index/controller/user/Wallet.php */
namespace app\index\controller\user;
class Wallet{
public function index(){
return "用户钱包首页";
}
}
$ curl localhost:8080/index/user/wallet/index
用户钱包首页
ThinkPHP 5对PHP的原始输入进行了包装:
Request 实例方法 |
作用 |
---|---|
param($name='',$default=null,$filter='') |
返回当前请求类型的参数、PATHINFO、$_GET |
get($name='',$default=null,$filter='') |
返回$_GET |
post($name='',$default=null,$filter='') |
返回$_POST |
put($name='',$default=null,$filter='') |
返回PUT请求方法的数据 |
delete($name='',$default=null,$filter='') |
返回DELETE请求方法的数据 |
session($name='',$default=null,$filter='') |
返回$_SESSION |
cookie($name='',$default=null,$filter='') |
返回$_COOKIE |
request($name='',$default=null,$filter='') |
返回$_REQUEST |
server($name='',$default=null,$filter='') |
返回$_SERVER |
env($name='',$default=null,$filter='') |
返回$_ENV |
route($name='',$default=null,$filter='') |
返回路由数据 |
file($name='') |
返回$_FILES[$name] |
header($name='',$default=null) |
返回Header数据 |
$request = Request::instance();
$username = $request->param('username'); // 筛选名为username的参数
$allParam = $request->param(); // 获取经过过滤的所有参数
$allParam = $request->param(false); // 获取未经过滤的所有参数
// $request->param()有以上三种用法,其它方法同理
Request::instance()->get(
'username',
'',
'htmlspecialchars,strip_tags'
);
Request::instance()->only(
['username','password']
); // 一次筛选多个数据
Request::instance()->except(
['username','password']
); // 一次排除多个数据
我们知道,原生PHP使用强制类型转换((int)
/(string)
/(array)
/......)或类型转换函数(intval()
/floatval()
/strval()
)进行数据类型之间的转换。为了简化这一过程,ThinkPHP框架提供了数据类型修饰符:
Request::instance()->param('username/s'); // 字符串
Request::instance()->param('age/d'); // 整型
Request::instance()->param('gender/b'); // 布尔型
Request::instance()->param('price/f'); // 浮点型
Request::instance()->param('list/a'); // 数组
在实际场景中,高并发的网站为降低服务器压力,通常使用页面缓存:
/* 编辑application/route.php */
'tryYourLuck/:id' => ['index/index/index',['cache'=>5]]
/* 编辑application/index/controller/index.php */
......
class Index {
public function index(){
return "您的随机数是".rand(0,10);
}
}
$ curl localhost:8080/index/index/ (重复三次)
您的随机数是3
您的随机数是9
您的随机数是7
$ curl localhost:8008/tryYourLuck (5秒内重复三次)
您的随机数是1
您的随机数是1
您的随机数是1
$ curl localhost:8080/tryYourLuck (5秒后再试一次)
您的随机数是10
MVC的模型层可以再细分,其中DAO(Data Access Object)层用于进行底层数据库的操作,并不涉及业务逻辑。ThinkPHP框架实现了这一层,并且提供数据库的配置文件:
/* 查看application/database.php */
return [
// 数据库类型
'type' => 'mysql',
// 服务器地址
'hostname' => '127.0.0.1',
// 数据库名
'database' => '',
// 用户名
'username' => 'root',
// 密码
'password' => '',
// 端口
'hostport' => '',
// 连接dsn
'dsn' => '',
// 数据库连接参数
'params' => [],
// 数据库编码默认采用utf8
'charset' => 'utf8',
// 数据库表前缀
'prefix' => '',
// 数据库调试模式
'debug' => true,
// 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器)
'deploy' => 0,
// 数据库读写是否分离 主从式有效
'rw_separate' => false,
// 读写分离后 主服务器数量
'master_num' => 1,
// 指定从服务器序号
'slave_no' => '',
// 是否严格检查字段是否存在
'fields_strict' => true,
// 数据集返回类型 array 数组 collection Collection对象
'resultset_type' => 'array',
// 是否自动写入时间戳字段
'auto_timestamp' => false,
// 是否需要进行SQL性能分析
'sql_explain' => false,
];
ThinkPHP允许模型类使用$connection
来连接database.php
定义之外的数据库:
class User extends Model {
protected $connection = 'user'; // 自动读取user连接定义来连接数据库
}
数据库的基本操作离不开CURD(Create/Update/Read/Delete),ThinkPHP 5定义了Db
类用于实现增删查改:
$受影响行 = Db::execute( // 增
'INSERT INTO user (username,password) VALUES (?,?)',
['admin',md5('123456')]
);
$受影响行 = Db::execute( // 改
'UPDATE user SET password=? WHERE username=?',
[md5('123456'),'admin']
);
$受影响行 = Db::execute( // 删
'DELETE FROM user WHERE username=?',
['admin']
);
$查询结果 = Db::query(
'SELECT * FROM user WHERE username=?',
['admin']
);
如果要使用database.php
定义的默认数据库之外的数据库,可以使用Db::config()
方法:
Db::config($connection)->query(
'SELECT * FROM user WHERE username=?',
['admin']
);
实际项目中很少使用Db::execute()
直接执行手写的SQL语句。ThinkPHP将CURD的SQL语句封装成了查询构造器,并且使用了设计模式中的建造者模式,因此支持链式调用:
-
添
// 插入单行 Db::table('user')->insert( ['username'=>'admin', 'password'=>md5('123456')] ); // 插入多行 Db::table('user')->insertAll( [ ['username'=>'admin', 'password'=>md5('123456')], ['username'=>'user1', 'password'=>md5('abcdef')], ] );
-
改:
// 更新数据 Db::table('user')->where('username','admin')->update( ['password'=>md5('123456')] ); // 更新数据(特用于更新的数据中包含主键的情形) Db::table('user')->update( ['id'=>1,'password'=>md5('123456')] ); // 更新数据(特用于只更新某一行的某一个字段的情形) Db::table('user')->where('username','admin')->setField( 'password', md5('123456') ); // 让整型字段自增 Db::table('user')->where('username','admin')->setInc( 'visit',1 ); // 让整型字段自减 Db::table('user')->where('username','admin')->setInc( 'money',1 );
-
查
// 查询单行数据 Db::table('user')->where('username','admin')->find(); // 查询多行数据 Db::table('user')->where('gender',1)->select(); // 查询单行数据的某个字段值 Db::table('user')->where('username','admin')->value('age'); // 查询多行数据的某个字段值集合 Db::table('user')->where('gender',1)->column('username'); // 批量查询(适用于服务器内存有限的情景,通过循环来降低资源占用) Db::table('user')->where('gender',1)->chunk( 100, function ($users){print_r($users);}, 'username' );
MySQL自5.7版本开始支持JSON类型,可以像MongoDB一样存储非结构化数据。ThinkPHP 5也提供了对应的支持:
// admin为JSON类型的字段,在其名称后使用$符号 Db::table('user')->where('info$.email','[email protected]')->find();
-
删
// 依据主键删除 Db::table('user')->delete([1,2,3]); // 删除 Db::table('user')->where('username','admin')->delete();
对于WHERE
子句,ThinkPHP适配SQL语句中的所有逻辑运算符:
SQL逻辑运算符 | ThinkPHP逻辑运算符 | 作用 | 代码实例(省略Db::table('...')-> ) |
---|---|---|---|
= |
= |
等于 | where('username','=','admin') /where('username','admin') |
<> |
<> |
不等于 | where('id','<>',9) |
> |
> |
大于 | where('id','>',9) |
>= |
>= |
大于等于 | where('id','>=',9) |
< |
< |
小于 | where('id','<',9) |
<= |
<= |
小于等于 | where('id','<=',9) |
between /not between |
between /not between |
区间查询 | where('age','between',][18,24]) |
in /not in |
in /not in |
列表查询 | where('age','in',[6,7,8]) |
null /not null |
is null /is not null |
**是否为null** |
where('列名',null) |
exists /not exists |
is exists /is not exists |
是否存在 | |
like |
like |
模糊查询 | where('name','like','%Smith%') |
exp |
表达式查询(ThinkPHP独有) | where('name','exp','between 18 and 24') |
|
and |
逻辑与 | where(...)->where(...)->where(...) |
方法名 | 作用 | 方法名 | 作用 |
---|---|---|---|
table() |
指定表名 | relation() |
关联查询(可多次调用) |
alias() |
为当前表定义别名 | page() |
分页查询(ThinkPHP独有) |
filed() |
查询指定字段(可多次调用) | lock() |
数据库锁 |
order() /orderRaw() |
查询结果排序(可多次调用) | cache() |
缓存查询(ThinkPHP语法) |
limit() |
限制查询结果行数 | with() |
关联查询预处理(可多次调用) |
group() |
分组查询 | bind() |
数据绑定(配合占位符使用) |
having() |
筛选结果集 | strict() |
是否严格检测字段名是否存在 |
join() |
关联查询(可多次调用) | master() |
读写分离环境下从主服务器读取数据 |
union() |
联合查询(可多次调用) | failException() |
未查询到数据时是否抛出异常 |
view() |
视图查询 | partition() |
数据库分表查询(ThinkPHP独有) |
distinct() |
过滤重复数据 |
// SELECT * FROM `user` LIMIT 1;
Db::table('user')->find();
// SELECT * FROM `think_user` LIMIT 1; (使用表前缀,假设为think_)
Db::table('__USER__')->find();
// SELECT * FROM `think`.`user` LIMIT 1; (指定数据库名)
Db::table('think.user')->find();
// SELECT * FROM `think_user` `user` INNER JOIN `think_post` `post` ON `post`.`user_id`=`user`.`user.id`;
Db::table('__USER__')->alias(['think_user'=>'user','think_post'=>'post'])
->join(['think_user'=>'user'],'post.user_id=user.user_id')
->select();
// SELECT `username`,`password` FROM `user` LIMIT 1;
Db::table('user')->field(['username','password'])->find();
// SELECT `username` as `ShownName` FROM `user` LIMIT 1;
Db::table('user')->field(['username'=>'ShownName'])->find();
// SELECT SUM(`amount`) AS `amount` FROM `user` LIMIT 1;
Db::table('user')->field(['SUM(amount)'=>'amount'])->find();
// SELECT `article_id`,`title`,`desc` FROM `article` LIMIT 1; (常用于排除TEXT大字段)
Db::table('user')->field('content',true)->find();
// 安全写入
Db::table('user')->field(['email','phone'])->insert(
['email'=>'[email protected]','phone'=>'123-456789']
);
// SELECT * FROM `user` ORDER BY `age` DESC, `user_id` DESC;
Db::table('user')->order(
['age'=>'desc','user_id'=>'desc']
)->select();
// SELECT * FROM `user` ORDER BY RAND();
Db::table('user')->order('RAND()')->select();
// SELECT * FROM `user` LIMIT 10;
Db::table('user')->limit(10)->select();
// SELECT * FROM `user` LIMIT 100,10; (指定起始行)
Db::table('user')->limit(10)->select();
// DELETE FROM `user` WHERE `gender`=1 LIMIT 1;
Db::table('user')->where('gender',1)->limit(1)->delete();
// SELECT `user_id`,SUM(`score`) AS `score` FROM `exam` GROUP BY `user_id`;
Db::table('exam')->field(
['user_id','SUM(score)'=>'score']
)->group('user_id')->select();
// SELECT `user_id`,SUM(`score`) AS `score`
Db::table('exam')->field(
['user_id','SUM(score)'=>'score']
)->group('user_id')->having('score>=60')->select();
// SELECT * FROM `think_user`INNER JOIN `think post` ON `think_post`.`user_id`=`think_user`.`user_id`;
Db::table('think_user')->join(
['think post','think post.user id=think user.user id']
)->select();
// SELECT * FROM `think user` `user` INNER JOIN `think article` `article` ON `article`.`user_id`=`user`.`user_id` INNER JOIN `think_comment` `comment` ON `comment`.`user_id`=`user`.`user_id`;
Db::table('think user')->alias('user')->join(
[
['think article article','article.user id=user.user id'],
['think_comment comment','comment.user id=user.user id']
]
)->select();
// SELECT `name` FROM `user` UNION SELECT `name` FROM `user1` UNION SELECT `name` FROM `user2`;
Db::table('user')->field(['name'])->union([
'SELECT name FROM user1','SELECT name FROM user2'
])->select();
// SELECT `name` FROM `user` UNION SELECT `name` FROM `user1` UNION SELECT `name` FROM `user2`;
Db::table('user')->field(['name'])
->union(function($query){$query->table('user1')->filed(['name']);})
->union(function($query){$query->table('user2')->filed(['nane']);})
->select();
// SELECT `name` FROM `user` UNION ALL SELECT `name` FROM `user1`;
Db::table('user')->field(['name'])->union(
['SELECT name FROM user1',true]
)->select();
// SELECT DISTINCT `username` FROM `user`;
Db::table('user')->distinct(true)->field(['username'])->select();
page()
是ThinkPHP框架独有的一个方法,用于简化limit()
方法的计算:
// SELECT * FROM `user` LIMIT 0,10
Db::table('user')->page(1,10)->select();
// SELECT * FROM `user` WHERE `user_id`=1 FOR UPDATE;
Db::table('user')->where('user_id',1)->lock(true)->find();
// SELECT * FROM `user` WHERE `user_id`=1 LOCK IN SHARE MODE;
Db::table('user')->where('user_id',1)->lock('lock in share mode')->find();
Db::table('user')->cache(60)->find();
// 指定缓存Key
Db::table('user')->cache('tmp_user',60)->find();
Cache::get('tmp_user');
// 清除缓存
Db::table('user')->update(['user_id'=>1,'name'=>'demo']);
// 根据Key清除缓存
Db::table('user')->cache('tmp_user')->where('user_id',1)->update(['name'=>'demo']);
ThinkPHP 5新增对SQL查询事件的支持,可以在执行数据库操作前后添加监听器。其声明为Query::event(string $event,callable $callable)
,其中监听器$event
的种类包含before_select
、before_find
、after_insert
、after_update
、after_delete
。
Query::event('after_delete',function($options){
return "删除成功!";
});
Db::listen(function($sql,$time,$explain,$isMaster){
// ......
})
Db::transaction(function(){
Db::table('user')->insert($data1);
Db::table('user')->insert($data2);
});
模型层是对DAO层的进一步包装,基于对象之间的关系映射来操作数据库。
一个模型的定义如下所示:
/* 创建application/index/model/User.php */
<?php
namespace app\index\model;
use think\Model;
class User extends Model {
protected $pk = 'id'; // Primary Key名称
protected $table = 'users'; // 表名称
// protected $connection = 'db1'; //
}
-
增
$user = new User(); // 对象方式 $user->username = 'admin'; $user->password = md5('123456'); $user->save(); // 数组方式 $user->data([ 'username'=>'admin' 'password'=>md5('123456') ]); $user->save();
-
改
// 查询条件方式 $user = new User(); $user->save( ['password'=>md5('123456')], ['username'=>'admin'] ); // 对象方式 $user = User::get(['username'=>'admin']); $user->password = md5('123456'); $user->save(); // 根据主键批量更新 $user = new User(); $user->saveAll([ ['id'=>1,'password'=>md5('123456')], ['id'=>1,'password'=>md5('654321')] ]);
-
删
// 对象方式 $user = User::get(['username'=>'admin']); $user->delete(); // 主键方式 User::destory(1); User::destory([2,3,4]); // 根据条件删除 User::where('id',1)->delete();
-
查
// 根据主键查询 $user = User::get(1); // 根据主键批量查询 $users = User::get([1,2.3]); // 指定字段查询 $user = User::get(['username'=>'demo']); // 指定字段批量查询 $users = User::get(['gender'=>1]); // where子句查询 $user = User::where('id',1)->find(); // where子句批量查询 $users = User::where('gender',1); ->page(1,10) ->select(); // 闭包查询 $user = User::get( function($query){$query->where('id',1);} ); // 指定字段查询(通过PHP魔术方法自动识别字段) $user = User::getByUsername('admin');
在[§6.1 CURD](#§6.1 CURD)的User
类中,我们只在数据库中定义了username
和password
字段,但是没有在User
类中定义。为什么我们可以直接调用$user->username
,并且可以得到正确的值且不报错呢?这是因为ThinkPHP框架重写了Model
类的魔术方法__get()
/__set()
abstract class Model implements \JsonSerializable, \ArrayAccess{
......
public function __get($name){
return $this->getAttr($name);
}
public function getAttr($name){
try {
$notFound = false;
$value = $this->getData($name);
} catch (InvalidArgumentException $e) {
$notFound = true;
$value = null;
}
// 检测属性获取器
$method = 'get' . Loader::parseName($name, 1) . 'Attr';
if (method_exists($this, $method)) {
$value = $this->$method($value, $this->data);
} elseif (isset($this->type[$name])) {
// 类型转换
$value = $this->readTransform($value, $this->type[$name]);
} elseif (in_array($name, [$this->createTime, $this->updateTime])) {
if (is_string($this->autoWriteTimestamp) && in_array(strtolower($this->autoWriteTimestamp), [
'datetime',
'date',
'timestamp',
])
) {
$value = $this->formatDateTime(strtotime($value), $this->dateFormat);
} else {
$value = $this->formatDateTime($value, $this->dateFormat);
}
} elseif ($notFound) {
$relation = Loader::parseName($name, 1, false);
if (method_exists($this, $relation)) {
$modelRelation = $this->$relation();
// 不存在该字段 获取关联数据
$value = $this->getRelationData($modelRelation);
// 保存关联对象值
$this->relation[$name] = $value;
} else {
throw new InvalidArgumentException('property not exists:' . $this->class . '->' . $name);
}
}
return $value;
}
......
}
ThinkPHP支持自动向数据库写入时间戳(默认字段名为create_time
/update_time
),可以通过以下两种方式
-
更改配置文件
application/database.php
'auto_timestamp'=>true; // 取值范围true|'datetime'|'timestamp'
-
通过模型类定义
class ... extends Model { protected $autoWriteTimeStamp = true; // protected $createTime = '自定义字段名'; // protected $updateTime = '自定义字段名'; }
我们知道,有时我们在数据库中删除数据,并不是真的使用DELETE
语句,而是标志一个删除位。
class User extends Model {
use SoftDelete;
protected $deleteTime = '自定义字段名';
// 配置后默认为软删除
User::destory([1,2,3]);
// 硬删除
$user = User::get(1);
$user->delete();
}
ThinkPHP在查询时默认不包含软删除的数据,如要需要,则应使用Model::withTrashed()
方法:
User::withTrashed()->select();
User::withTrashed()->find();
只查询软删除数据应使用Model::onlyTrashed()
方法:
User::onlyTrashed()->select();
User::onlyTrashed()->select();
ThinkPHP框架支持调用Model::save()
自动更新某些字段的值:
class User extends Model {
protected $auto = [];
protected $insert = ['created_ip','created_ua'];
protected $update = ['login_ip','login_at'];
protected function setCreatedIpAttr(){ // 插入时自动填充create_ip字段
return request()->ip();
}
protected function setCreatedUaAttr(){ // 插入时自动填充create_ua字段
return request()->header('user-agent');
}
protected function setLoginIpAttr(){ // 更新时自动填充login_ip字段
return request()->ip();
}
protected function setLoginAtAttr(){ // 更新时自动填充login_at字段
return time();
}
}
我们知道PHP是一门弱类型语言,而Server通过Database收到的数据全都是字符串,需要开发者手动完成类型转换。ThinkPHP框架支持的数据库数据类型如下所示:
ThinkPHP数据类型 | 作用 |
---|---|
integer |
读写时转换为整型 |
float |
读写时转换为浮点型 |
boolean |
读写时转换为布尔型 |
array |
写入时转换为JSON,读取时转换为array |
object |
写入时转换为JSON,读取时转换为stdClass |
serialize |
写入时序列化转换为字符串,读取时反序列化转换为前类型 |
json |
写入时调用json_encode() 转为字符串,读取时调用json_decode() 转换为前类型 |
timestamp |
写入时调用strtotime() ,读取时调用date() ,默认格式为Y-m-d H:i:s ,受$dateFormat 控制 |
class User extends Model {
protected $type = [
'status'=>'integer',
'balance'=>'float',
'data'=>'json'
];
}
$user = new User;
$user->status = '1';
$user->balance = '123.456';
$user->data = ['username'=>'Alice'];
$user->save();
var_dump($user->status,$user->balance,$user->data);
// int(1) float(123.456) array(size=1) 'username'=>'Alice'
ThinkPHP框架提供了scopeXXX
的魔术方法:
class User extends Model {
protected function scopeMale($query){
$query->where('gender',1);
}
}
$user = User::scope('male')->select();
在实际项目中,我们有时频繁地只希望查询符合某个条件的记录,例如只查询删除位为false
的所有记录。如果每次使用where()
都必须带上排除删除位的语句就太麻烦了。ThinkPHP提供了base()
方法,我们可以重载该方法实现全局内的查询条件:
class User extends Model {
protected function base($query){
$query->where('deleted',false);
}
}
User::useGolbalScope(true)->where('username','admin')->select(); // 开启全局查询条件
User::useGolbalScope(false)->where('username','admin')->select(); // 关闭全局查询条件
我们已经在[§5.4 查询事件](#§5.4 查询事件)一节中得知如何为数据库操作绑定监听事件,那么如何为Model
操作绑定鉴定事件呢?ThinkPHP在Model
类中提供了静态方法init()
用于注册以下监听事件,通过返回的布尔值控制后续代码是否执行:
监听事件方法名 | 作用 | 监听事件方法名 | 作用 | 监听事件方法名 | 作用 | 监听事件方法名 | 作用 |
---|---|---|---|---|---|---|---|
beforeInsert() |
插入前 | beforeUpdate() |
更新前 | beforeWrite() |
写入前 | beforeDelete() |
删除前 |
afterInsert() |
插入后 | afterUpdate() |
更新后 | afterWrite() |
写入后 | afterDelete() |
删除后 |
class User extends Model {
protected function init(){
User::beforeInsert(
function($user){
return $user->age > 0;
}
);
}
}
class Wallet extends Model {
}
class User extends Model {
protected function wallet(){
return $this->hasOne('Wallet','wallet_id','wallet_id');
}
}
$user = User::get(1);
echo $user->wallet->balance;
$user->wallet->save()
???????????TODO:
ThinkPHP框架规定,如果一个类继承了Controller
类,则可以直接通过echo
/return
输出视图;如果没有继承,则可以通过view('模版文件','模版数据','模板替换数据')
输出内容。
Controller->fetch()
方法负责为控制器类的某个方法绑定模版:
<?php
namespace app\index\controller;
use think\Controller;
class Index extends Controller {
public function index(){
return $this->fetch();
}
}
其中模版路径默认为当前模块/view/当前控制器类名小写/当前方法名小写.html
:
/* 新建application/indes/view/index/index.html */
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
</head>
<body>
<p>Hello World</p>
</body>
</html>
访问路径即可看到模版:
$ curl localhost:8080/index/index
Hello World
如果需要绑定指定的模版,可以给Controller->fetch()
传递相应的参数:
作用 | Controller->fetch() 的$template 参数 |
举例 |
---|---|---|
绑定当前模块默认路径下的其他模版 | fetch('文件主名') |
fetch('home') 绑定application/模块名/view/控制器类名/文件主名开头的html文件 |
绑定当前模块其他路径下的其他模版 | fetch('目录/文件主名') (支持../ ) |
fetch('public/home') 绑定application/模块名/view/public/home.html |
绑定其它模块其他路径下的其他模版 | fetch('其它模块名@目录/主名') |
fetch('forum/public/home') 绑定application/forum/view/public/home.html |
绑定view_path 目录下的模版 |
fetch('/目录/主名') |
view_path 缺省时为application/模块名/view |
注意:如果当前控制器类没有继承
Controller
类,也可以使用助手函数view()
实现上述效果,参数及其含义完全相同。return $this->fetch() // 继承Controller时 return view(...); // 助手函数
当控制器类继承Controller
类时,我们可以使用Controller->assign()
方法向视图进行赋值。
向模版传参的赋值的方法有:
-
传入一个键值对
$this->assign('键名','值');
-
传入多个键值对(一个数组)
$this->assign( [ '键名1'=>'值1', '键名2'=>'值2', ] );
-
直接传入
fetch()
方法的$vars
形参$this->fetch('index', [ '键名1'=>'值1', '键名2'=>'值2', ] );
-
直接使用
display()
方法,绕过模版直接输出$content = '{$name}' return $this->display($content,['name'=>'Alice']);
-
使用助手函数
view()
的$vars
形参return view('index', [ '键名1'=>'值1', '键名2'=>'值2', ] ); return view('index')->assign( [ '键名1'=>'值1', '键名2'=>'值2', ] );
-
使用
View::share()
进行全局赋值\think\facade\View::share('键名','值'); \think\facade\View::share( [ '键名1'=>'值1', '键名2'=>'值2', ] );
下面是一个简单的例子:
class Index extends Controller {
public function index(){
$this->assign('name','Alice');
$this->assign([
'chinese'=>150,
'math'=>150,
'english'=>150
]);
return $this->fetch('index',[
'classRank'=>2,'schoolRank'=>30,'provinceRank'=>3480
]);
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Score List</title>
</head>
<body>
<h1>{$name|default='Null'}'s Score List</h1>
<ul>
<li>Chinese: {$chinese}</li>
<li>Math: {$math}</li>
<li>English: {$english}</li>
</ul>
<ul>
<li>Class Rank: {$classRank}</li>
<li>School Rank: {$schoolRank}</li>
<li>Province Rank: {$provinceRank}</li>
</ul>
</body>
</html>
这里使用的{$...}
是由application/config.php
中的template
数组中的tpl_begin
与tpl_end
决定的。
ThinkPHP会在访问时,将HTML中的变量占位符{$...}
替换成PHP语句,并把新生成的PHP文件放在runtime/temp
目录中:
/* 查看 */
<?php
if (!defined('THINK_PATH'))
exit();
/*
a:1:{
s:101:"C:\WWW\public/../application/index\view\index\index.html";
i:1658219700;
}
*/
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Score List</title>
</head>
<body>
<h1><?php echo $name; ?>'s Score List</h1>
<ul>
<li>Chinese: <?php echo $chinese; ?></li>
<li>Math: <?php echo $math; ?></li>
<li>English: <?php echo $english; ?></li>
</ul>
<ul>
<li>Class Rank: <?php echo $classRank; ?></li>
<li>School Rank: <?php echo $schoolRank; ?></li>
<li>Province Rank: <?php echo $provinceRank; ?></li>
</ul>
</body>
</html>
ThinkPHP模版不仅支持获取原生PHP中的超全局变量、常量等变量,也支持获取ThinkPHP框架本身配置文件的数据:
PHP原生变量 | ThinkPHP框架模版的调用方法 |
---|---|
$_SERVER |
{$Think.server.键名} |
$_ENV |
{$Think.env.键名} |
$_POST |
{$Think.post.键名} |
$_GET |
{$Think.get.键名} |
$_COOKIE |
{$Think.cookie.键名} |
$_SESSION |
{$Think.session.键名} |
$_REQUEST |
{$Think.request.键名} |
常量 | {$Think.const.常量名} |
ThinkPHP框架模版配置 | {$Think.config.template} |
ThinkPHP框架多语言配置 | {$Think.lange.error} |
ThinkPHP框架Reuqest 对象 |
{$Request.get.键名} |
在之前的例子中,如果我们没有给模版中的某个参数传值,那么ThinkPHP就会抛出think\exception\ErrorException
。为避免这一情况,我们可以为其指定一个默认值:
{$signature|default='该用户未填写个性签名'}
在视图中使用|
表示将左边的结果作为右边函数的第一个参数。如果要强制指定参数的其它位置,可以使用###
占位符:
{$user.password|md5}
// 编译为<?php echo md5($user['password']) ?>
{$user.name|substr=0,4}
// 编译为<?php echo substr($user['name'],0,20) ?>
{$user.registerTime|date='y-m-d h:m:s',###}
// 编译为<?php echo date('y-m-d h:m:s',$user['registerTime'])
以此类推,我们可以嵌套多个函数:
{$user.password|md5|substr=0,16|default="密码不能为空!"}
// 算术运算符
{$user->score+10}
{$user->score++}
{$user->score*2}
{pow($user->score,0.5)}
// 三目运算符
{$isVIP?'续费VIP':'成为VIP'}
{$nam??'Null'}
受制于其它模版引擎的特性,任何{$...}
都可能会被明文输出。之前我们已经知道了如何让其不输出,如果我们就想让它明文输出的话,每次都指定默认值{$...|default=...}
太麻烦了,我们可以使用{literal}
标签:
{literal}
Hello, {$name}! // 每次都输出'{$name}'字符串
{/literal}
视图过滤可以在页面显示之前对页面内容进行更改:
class User extends Controller {
public function initialize(){
$this->filter(function($content){
return str_replace('Alice','You\'re hacked!');
});
}
public function index(){
// ......
return $this->fetch(...);
}
}
// 助手函数
return view()->filter(function($content){
// ......
});
实际项目中,许多页面的头部都是导航栏,底部都是版权说明。为了提高代码复用率,ThinkPHP 5提供了布局功能。
-
全局配置
/* 查看application/config.php */ return [ // ...... 'template' => [ 'layout_on' => true, 'layout_name' => 'layout_file' ], // ...... ];
????????TODO:?????????
模版引擎配置在application/config.php
的template
键中:
/* 查看application/confg.php */
return [
......
'template' => [
// 模板引擎类型 支持 php think 支持扩展
'type' => 'Think',
// 模板路径
'view_path' => '',
// 模板后缀
'view_suffix' => 'html',
// 模板文件名分隔符
'view_depr' => DS,
// 模板引擎普通标签开始标记
'tpl_begin' => '{',
// 模板引擎普通标签结束标记
'tpl_end' => '}',
// 标签库标签开始标记
'taglib_begin' => '{',
// 标签库标签结束标记
'taglib_end' => '}',
],
......
]
????????TODO:?????????
ThinkPHP 5使用\think\Validate
类对数据进行验证。在这之前,验证逻辑大多集成在控制器类中,而验证器的出现有利于项目的解耦。
-
验证器的代码模版可以通过在终端中执行
php think make:validate 模型类名
,自动在applicaiton/validate
目录下创建模型类名.php
:<?php namespace app\index\validate; use think\Validate; class User extends Validate { protected $rule = []; protected $message = []; }
-
或者现场
new
一个验证器实例:$validator = new Validate( $rule, // 数组 $message, // 信息 );
-
或者使用
Controller
的validate()
方法:$result = $this->validate( $rule, // 数组 $message, // 信息 ); $result = $this->validate(XXXValidator::class)->save($data);
-
或者使用
Model
的validate()
方法:$model = new User(); $result = $model->validate(XXXValidator::class)->save($data); if($result !== true){ throw new Exception($result); }
-
甚至直接使用验证器类的静态方法进行便捷验证:
if(!Validate::length('username',1,127)){ thrown ew Exception('......'); }
Validate->$rule
数组表示规则,Validate->$message
数组表示报错提示信息:
// ......
class User extends Validate {
protected $rule = [
'name' => 'require|max:128',
'age' => 'number|between:0,127'
'email' =? 'email'
];
protected $message = [
'name.require' => '姓名不能为空!',
'name.max' => '姓名长度不得大于128位!',
'age.number' => '年龄必须为数字!',
'age.between' => '年龄必须在0-127之间!',
'email' => '邮箱格式错误!'
];
}
创建测试类:
class Index extends Controller {
public $data = [
'name'=>'Alice',
'age'=>20,
'email'=>'[email protected]'
];
// 手动实例化验证器类
public function verifyUser(){
$validate = new \app\index\validate\User();
if(!$validate->check($this->data)){
dump($validate->getError());
}
}
// 使用自Controller类继承的validate()方法
public function verityUser(){
$result = $this->validate($this->data,\app\index\validate\User::class);
if($result !== true{
dump($result);
}
}
}
默认情况下,Validate
类会顺序逐次验证数据是否符合要求,一旦发现不符之处就马上终止验证过程,不会验证后续数据。为了让Validate
类把所有数据都验证一遍,找出所有错误,我们可以手动给Controller
的$batchValidate
字段设为true
:
class User extends Controller {
protected $batchValidate = true;
// ......
}
默认情况下,Validate
类发现不符之处就马上终止验证过程,返回错误信息。如果我们想让其直接抛出异常ValidateException
,而不是返回错误信息的字符串或数组,可以手动给Controller
的$failException
字段设为true
:
class User extends Controller {
protected $faliException = true;
// ......
}
除了使用预置规则外,我们也可以自定义新规则:
class User extends Controller {
protected function checkName($value,$rule,$data,$field,$title){
return $value != $rule ? true : '请填写真实姓名!'
}
}
'name'=>'require|checkName:无名氏'
ThinkPHP框架内置了众多常用的规则:
ThinkPHP内置验证器 | 含义 | 举例 |
---|---|---|
require |
必填项 | 'username'=>'require' |
integer |
整数 | 'age'=>'integer' |
float |
浮点数 | 'percent'=>'float' |
boolean |
布尔值 | 'isAdmin'=>'boolean' |
email |
邮箱 | 'email'=>'email' |
array |
数组 | 'list'=>'array' |
date |
日期 | 'registerDate'=>'date' |
alpha |
纯字母 | 'username'=>'alpha' |
alphaNum |
字母+数字 | 'username'=>'alphaNum' |
alphaDash |
字母+数字+下划线+中划线 | 'username'=>'alphaDash' |
chs |
纯中文 | 'username'=>'chs' |
chsAlpha |
中文+字母 | 'username'=>'chsAlpha' |
chsAlphaNum |
中文+字母+数字 | 'username'=>'chsAlphaNum' |
chsDash |
中文+字母+数字+下划线+中划线 | 'username'=>'chsDash' |
activeUrl |
主机名(包含域名和IP) | 'hostname'=>'activeUrl' |
url |
链接 | 'homepage'=>'url' |
ip |
IPv4/IPv6地址 | 'loginIP'=>'ip' |
dateFormat:format |
指定格式日期 | 'loginTime'=>'dateFormat:y-m-d' |
in |
在列表中 | 'gender'=>'in:0,1' |
notIn |
不在列表中 | 'gender'=>'notIn:0,1' |
between |
在范围内 | 'age'=>'between:20,60' |
notbetween |
不在范围内 | 'age'=>'nowBetween:20,60' |
length:min,max |
字符串长度、数组元素数、文件大小的范围 | 'telephone'=>'length:11' 'username'=>'length:3,12' |
ThinkPHP框架使用\think\Cache
类实现缓存,缓存的配置文件在application/config.php
的cache
数组:
/* 查看application/config.php */
return [
// ......
'cache' => [
// 驱动方式
'type' => 'File',
// 缓存保存目录
'path' => CACHE_PATH,
// 缓存前缀
'prefix' => '',
// 缓存有效期 0表示永久缓存
'expire' => 0,
],
// ......
];
目前ThinkPHP框架缓存支持的驱动方式type
包括File
、Memecache
、wincache
、sqlite
、redis
、xcache
。
Cache::set('key','value',3600); // 存入缓存一个小时
$value = Cache::get('key'); // 获取缓存
Cache::rm('key'); // 删除缓存
Cache::inc('number',1); // 自增1
Cache::dec('number',2); // 自减2
$value = Cache::pull('number'); // 获取缓存后删除缓存
Cache::clear(); // 清空缓存
Cache::remember('key',function(){ // 如果存在就返回,富国不存在就写入缓存之后缓存
return 'data';
},3600);
ThinkPHP 5将原生的Session和Cookie分别包装成\think\Session
和\think\Cookie
类,便于切换底层驱动。
Session指的是会话信息,位于服务器中,负责与客户端的交互,当客户端中断连接时Session失效。Cookie位于客户端中,用于维护客户端的信息,在用户主动删除前Cookie一直有效。
Session和Cookie的配置在application/config.php
中:
return [
// ......
'session' => [
'id' => '',
// SESSION_ID的提交变量,解决flash上传跨域
'var_session_id' => '',
// SESSION 前缀
'prefix' => 'think',
// 驱动方式 支持redis memcache memcached
'type' => '',
// 是否自动开启 SESSION
'auto_start' => true,
],
'cookie' => [
// cookie 名称前缀
'prefix' => '',
// cookie 保存时间
'expire' => 0,
// cookie 保存路径
'path' => '/',
// cookie 有效域名
'domain' => '',
// cookie 启用安全传输
'secure' => false,
// httponly设置
'httponly' => '',
// 是否使用 setcookie
'setcookie' => true,
],
// ......
];
Session::set('key','value'); // 写入默认scope
Session::has('key'); // 判断是否写入默认scope
Session::get('name'); // 从默认scope中读取
Session::delete('key'); // 删除默认scope中的值
Session::pull('key'); // 返回默认scope的值并删除
Session::clear(); // 清空默认scope中的所有值
Session::set('key','value','user'); // 写入user scope
Session::has('key','user'); // 判断是否写入user scope
Session::get('name','user'); // 从user scope中读取
Session::delete('key','user'); // 删除user scope中的值
Session::pull('key','value'); // 范湖user scope的值并删除
Session::clear('user'); // 删除user scope中的所有值
Cookie::set('key','value',3600); // 设置Cookie有效期为一小时
Cookie::set('key','value', // 设置Cookie前缀和有效期
[
'prefix'=>'forum_',
'expire'=>'3600'
]
);
Cookie::get('name'); // 获取名为name的Cookie值
Cookie::get('name','demo_'); //获取名为demo_name的Cookie值
Cookie::has('name'); // 判断名为name的Cookie是否存在
Cookie::has('name','demo_'); // 判断名为demo_name的Cookie是否存在
Cookie::delete('name','demo_'); // 删除名为demo_name的Cookie
Cookie::clear('demo_'); // 删除所有以demo_为前缀的Cookie
对于一些对算力要求比较高的业务逻辑,指望PHP在用户提交请求后的几秒内就能返回预期界面是不现实的。这部分业务逻辑必须常驻内存持续运行。
先在application/command.php
配置文件中注册命令行应用:
/* 编辑application/command.php */
return [
'app\index\command\HelloWorld' // 新加的一行
];
然后新建命令行应用类:
/* 新建application/index/command/HelloWorld.php */
<?php
namespace app\index\command;
use think\console\Input;
use think\console\Output;
class HelloWorld extends \think\console\Command{
protected function configure(){
$this->setName('hello_world')
->setDescription('This is a Hello World Command');
}
protected function execute(Input $input, Output $output){
$output->writeln('Hello World');
}
}
在终端中执行该命令行应用:
$ php ./think hello_world
Hello World
PHP脚本可以直接调用命令行应用:
class Index extends Controller {
public function index(){
$output = Console::call('hello_world');
return $this->fetch(......);
}
}