认识ThinkPHP
前言:
ThinkPHP存在3.2.3、5、6版本,其中ThinkPHP5具有反序列化漏洞、ThinkPHP6有任意文件写入漏洞等等,本文章计划从TP框架学起,搞清MVC框架的路由关系,将结合本地搭建得TP5.1的源码对框架进行学习。从官网进行下载:http://www.thinkphp.cn
什么是ThinkPHP
ThinkPHP是一个快速、简单的基于MVC和面向对象的轻量级PHP开发框架,遵循 Apache2开源协议发布,从诞生以来一直秉承简洁实用的设计原则,在保持出色的性能和至简的代码的同时,尤其注重开发体验和易用性,并且拥有众多的原创功能和特性,为WEB应用和API开发提供了强有力的支持。
正文
本地讲源码放到PHPStudy的网站根目录,重命名为tp5,访问http://127.0.0.1/tp5/public/访问到首页,同时可以看到版本信息存在
ThinkPHP目录结构如下:
因为public是网站对外访问的目录,所以我们访问http://127.0.0.1/tp5/public/的时候可以访问到欢迎页面。默认访问的是public目录下的index.php文件。
application =>ROOT_PATH
thinkphp =>THINK _PATH
extend =>EXTEND_PATH
vendor=>VEHNDOR_PATH
通过在代码中后面的大写字母,可以判断出是在哪个文件夹下。
ThinkPHP框架核心目录下内容解析
Lang 语言包目录
Library-think 类的库包目录、系统的tritias目录
Tpl 系统模板目录
Base.php 框架的基础文件
Console.php 控制台入口文件
Convention.php 惯例配置文件
Start.php 框架引导文件
Application框架核心目录下内容解析
Index 是一个模块的目录(官方提供的可更改的)
Index-controller 有一个控制器
Index-view 视图
command.php 命令行工具配置文件
common.php 应用公共文件
config.php 应用配置文件(内有default默认的控制器、默认语言等等)
database.php 数据库配置文件
route.php 路由文件
tags.php 应用行为扩展文件
/public/index.php
注:如果要添加新的模块,在Application目录下新建一个文件夹即可。
审计时可开启应用调试模式,进入Application->config.php文件,将app_debug改为true,保存后当访问不存在页面时,即会抛出异常。但注意业务上线的时候一定要关掉(默认也是false)
访问http://127.0.0.1/tp5/public/index.php时相当于访问http://127.0.01/tp5/public/index.php/index/index/index
第一个index表示模块,第二个index表示控制器,第三个表示方法。
实际访问的是application->index->controller->index.php下的index类的index方法
如果在该文件下再添加一个hello方法
<?php namespace app\index\controller;#命名空间 class Index { public function index() { return '<style type="text/css">*{ padding: 0; margin: 0; } .think_default_text{ padding: 4px 48px;} a{color:#2E5CD5;cursor: pointer;text-decoration: none} a:hover{text-decoration:underline; } body{ background: #fff; font-family: "Century Gothic","Microsoft yahei"; color: #333;font-size:18px} h1{ font-size: 100px; font-weight: normal; margin-bottom: 12px; } p{ line-height: 1.6em; font-size: 42px }</style><div style="padding: 24px 48px;"> <h1>:)</h1><p> ThinkPHP V5<br/><span style="font-size:30px">十年磨一剑 - 为API开发设计的高性能框架</span></p><span style="font-size:22px;">[ V5.0 版本由 <a href="http://www.qiniu.com" target="qiniu">七牛云</a> 独家赞助发布 ]</span></div><script type="text/javascript" src="https://tajs.qq.com/stats?sId=9347272" charset="UTF-8"></script><script type="text/javascript" src="https://e.topthink.com/Public/static/client.js"></script><think id="ad_bd568ce7058a1091"></think>'; } public function hello(){ return "Hello Wrold"; } }
访问http://127.0.0.1/tp5/public/index.php/index/index/hello才可以访问到这个方法的内容,如果少写一个index.php则不会访问到
命名空间也对应了文件的所在目录
视图
接下来,在application->index文件夹下新建一个view文件夹,再进入view文件夹新建一个index文件夹,再进入index文件夹新建一个hello.html文件,内容如下:
<!DOCTYPE html> <html> <head> <title>hello{$name}</title> </head> <body> hello, {$name}! </body> </html>
在修改application->index->controller->index.php文件
<?php namespace app\index\controller; use think\Controller;#新增,为了导入命名空间的类库,这样就可以在当前文件中直接使用该别名 class Index extends Controller#新增 { public function index() { return '<style type="text/css">*{ padding: 0; margin: 0; } .think_default_text{ padding: 4px 48px;} a{color:#2E5CD5;cursor: pointer;text-decoration: none} a:hover{text-decoration:underline; } body{ background: #fff; font-family: "Century Gothic","Microsoft yahei"; color: #333;font-size:18px} h1{ font-size: 100px; font-weight: normal; margin-bottom: 12px; } p{ line-height: 1.6em; font-size: 42px }</style><div style="padding: 24px 48px;"> <h1>:)</h1><p> ThinkPHP V5<br/><span style="font-size:30px">十年磨一剑 - 为API开发设计的高性能框架</span></p><span style="font-size:22px;">[ V5.0 版本由 <a href="http://www.qiniu.com" target="qiniu">七牛云</a> 独家赞助发布 ]</span></div><script type="text/javascript" src="https://tajs.qq.com/stats?sId=9347272" charset="UTF-8"></script><script type="text/javascript" src="https://e.topthink.com/Public/static/client.js"></script><think id="ad_bd568ce7058a1091"></think>'; } public function hello($name = "thinkphp"){#默认参数thinkphp $this -> assign('name' , $name);#第一个参数是在模板中使用的变量名,名字可以任意取,第二个参数是传递给模板的变量 return $this -> fetch();#这是thinkphp5的模板渲染 } }
上述新方法中的$this->fetch详细参看:https://blog.csdn.net/puspos/article/details/84331693
再访问http://127.0.0.1/tp5/public/index.php/index/index/hello,页面回显如下
传递参数,在后面加上参数名和值,用“/”分割即可,如:http://127.0.0.1/tp5/public/index.php/index/index/hello/name/world
数据库操作
application文件夹下的database.php中配置与数据库连接信息
URL和路由
URL访问
ThinkPHP采用单一入口模式访问应用,对应用的所有请求都定向到应用的入口文件,系统会从URL参数中解析当前请求的模块、控制器和操作。
http://127.0.0.1/tp5/public/index.php/index/index/index
在Application->index->controller下如果新加一个PHP文件(控制器),需用驼峰命名法HelloWorld.php
<?php Namespace app\index\controller; Class HelloWorld { Public function index($name = "World") { return "hello, ". $name . "!"; } }
访问http://127.0.0.1/tp5/public/index.php/index/hello_world/index可以访问到这个方法,访问时可以小写,注意用下划线分割。
如果在config.php里将url_convert改为false就必须注意大小写了。
定义路由
路由的作用是简化URL访问地址,并根据定义的路由类型做出正确的解析。
router.php
<?php return [ '__pattern__' => [ 'name' => '\w+', ], 'hello/:name' => 'index/index/hello', ];
该代码意思时所有以hello开头带参数的url都会路由到index/index/hello操作方法。
注:定义完路由规则后原本的URL的访问就会失效
访问http://127.0.0.1/tp5/public/index.php/hello/think。其中think可以任意写
若在route.php里路由配置时多加一个$,则表示完整匹配,’hello/:name$’ => ‘index/index/hello’再访问http://127.0.0.1/tp5/public/index.php/hello/think/aaa/bbb这样的页面时(即在正常访问道think截止后继续添加参数)就会报错,不加$时这样访问不会报错。
将路由还原会默认的,再修改下index.php
public function hello($name = "thinkphp",$city = ""){ return "Hello " . $name . " You come from " . $city . "."; }
当有两个参数时,有两种方式可以访问
1)依然访问时用”参数/参数值”进行传参,如“http://127.0.0.1/tp5/public/index.php/index/index/hello/name/purplet/city/neimenggu”,同时传参先后顺序不影响输出。
2)使用?传参,如“http://127.0.0.1/tp5/public/index.php/index/index/hello?city=neimenggu&name=purplet”
请求和响应
请求对象
ThinkPHP5的Request对象由think/Request类完成。
Request对象的一个主要职责是统一和更安全地获取当前地请求信息,你需要避免直接操作$_GET $_POST、$_REQUEST、$_SESSION、$_COOKIE甚至$_FILES等全局变量,而是统一使用Request对象提供的方法来获取变量
1.传统调用方法
2.继承think/Controller
3.自动注入请求对象
4.使用助手函数
传统调用方式
index.php
<?php namespace app\index\controller; use think\Request;#增加 class Index { public function index() { return '<style type="text/css">*{ padding: 0; margin: 0; } .think_default_text{ padding: 4px 48px;} a{color:#2E5CD5;cursor: pointer;text-decoration: none} a:hover{text-decoration:underline; } body{ background: #fff; font-family: "Century Gothic","Microsoft yahei"; color: #333;font-size:18px} h1{ font-size: 100px; font-weight: normal; margin-bottom: 12px; } p{ line-height: 1.6em; font-size: 42px }</style><div style="padding: 24px 48px;"> <h1>:)</h1><p> ThinkPHP V5<br/><span style="font-size:30px">十年磨一剑 - 为API开发设计的高性能框架</span></p><span style="font-size:22px;">[ V5.0 版本由 <a href="http://www.qiniu.com" target="qiniu">七牛云</a> 独家赞助发布 ]</span></div><script type="text/javascript" src="https://tajs.qq.com/stats?sId=9347272" charset="UTF-8"></script><script type="text/javascript" src="https://e.topthink.com/Public/static/client.js"></script><think id="ad_bd568ce7058a1091"></think>'; } public function hello($name = "thinkphp",$city = ""){ $request = Request::instance(); echo "url: " . $request->url() . "<br/>";#使用request对象中获取url参数的方法 return "Hello ," . $name; } }
访问http://127.0.0.1/tp5/public/index.php/index/index/hello得到访问路径。
继承think/Controller
index.php
<?php namespace app\index\controller; use think\Controller; class Index extends Controller { public function index() { return '<style type="text/css">*{ padding: 0; margin: 0; } .think_default_text{ padding: 4px 48px;} a{color:#2E5CD5;cursor: pointer;text-decoration: none} a:hover{text-decoration:underline; } body{ background: #fff; font-family: "Century Gothic","Microsoft yahei"; color: #333;font-size:18px} h1{ font-size: 100px; font-weight: normal; margin-bottom: 12px; } p{ line-height: 1.6em; font-size: 42px }</style><div style="padding: 24px 48px;"> <h1>:)</h1><p> ThinkPHP V5<br/><span style="font-size:30px">十年磨一剑 - 为API开发设计的高性能框架</span></p><span style="font-size:22px;">[ V5.0 版本由 <a href="http://www.qiniu.com" target="qiniu">七牛云</a> 独家赞助发布 ]</span></div><script type="text/javascript" src="https://tajs.qq.com/stats?sId=9347272" charset="UTF-8"></script><script type="text/javascript" src="https://e.topthink.com/Public/static/client.js"></script><think id="ad_bd568ce7058a1091"></think>'; } public function hello($name = "thinkphp",$city = ""){ echo "url: " . $this -> request->url() . "<br/>"; return "Hello ," . $name; } }
访问http://127.0.0.1/tp5/public/index.php/index/index/hello与之前的是一样的
自动注入请求对象
index.php
<?php namespace app\index\controller; use think\Request; class Index { public function index() { return '<style type="text/css">*{ padding: 0; margin: 0; } .think_default_text{ padding: 4px 48px;} a{color:#2E5CD5;cursor: pointer;text-decoration: none} a:hover{text-decoration:underline; } body{ background: #fff; font-family: "Century Gothic","Microsoft yahei"; color: #333;font-size:18px} h1{ font-size: 100px; font-weight: normal; margin-bottom: 12px; } p{ line-height: 1.6em; font-size: 42px }</style><div style="padding: 24px 48px;"> <h1>:)</h1><p> ThinkPHP V5<br/><span style="font-size:30px">十年磨一剑 - 为API开发设计的高性能框架</span></p><span style="font-size:22px;">[ V5.0 版本由 <a href="http://www.qiniu.com" target="qiniu">七牛云</a> 独家赞助发布 ]</span></div><script type="text/javascript" src="https://tajs.qq.com/stats?sId=9347272" charset="UTF-8"></script><script type="text/javascript" src="https://e.topthink.com/Public/static/client.js"></script><think id="ad_bd568ce7058a1091"></think>'; } public function hello(Request $request , $name = "World"){ echo "url: " . $request->url() . "<br/>"; return "Hello ," . $name; } }
访问http://127.0.0.1/tp5/public/index.php/index/index/hello与之前的是一样的
使用助手函数
index.php
<?php namespace app\index\controller; class Index { public function index() { return '<style type="text/css">*{ padding: 0; margin: 0; } .think_default_text{ padding: 4px 48px;} a{color:#2E5CD5;cursor: pointer;text-decoration: none} a:hover{text-decoration:underline; } body{ background: #fff; font-family: "Century Gothic","Microsoft yahei"; color: #333;font-size:18px} h1{ font-size: 100px; font-weight: normal; margin-bottom: 12px; } p{ line-height: 1.6em; font-size: 42px }</style><div style="padding: 24px 48px;"> <h1>:)</h1><p> ThinkPHP V5<br/><span style="font-size:30px">十年磨一剑 - 为API开发设计的高性能框架</span></p><span style="font-size:22px;">[ V5.0 版本由 <a href="http://www.qiniu.com" target="qiniu">七牛云</a> 独家赞助发布 ]</span></div><script type="text/javascript" src="https://tajs.qq.com/stats?sId=9347272" charset="UTF-8"></script><script type="text/javascript" src="https://e.topthink.com/Public/static/client.js"></script><think id="ad_bd568ce7058a1091"></think>'; } public function hello($name = "World"){ echo "url: " . request()->url() . "<br/>"; return "Hello ," . $name; } }
访问http://127.0.0.1/tp5/public/index.php/index/index/hello与之前的是一样的
请求对象的常用方法:
Param方法统一获取当前请求变量。
param方法获取的参数会自动判断当前的请求,以POST请求为例的话,参数的优先级别为:
路由变量>当前请求变量($_POST变量)>$_GET变量
Request对象获取当前请求变量
index.php
<?php namespace app\index\controller; use think\Request; class Index { public function index() { return '<style type="text/css">*{ padding: 0; margin: 0; } .think_default_text{ padding: 4px 48px;} a{color:#2E5CD5;cursor: pointer;text-decoration: none} a:hover{text-decoration:underline; } body{ background: #fff; font-family: "Century Gothic","Microsoft yahei"; color: #333;font-size:18px} h1{ font-size: 100px; font-weight: normal; margin-bottom: 12px; } p{ line-height: 1.6em; font-size: 42px }</style><div style="padding: 24px 48px;"> <h1>:)</h1><p> ThinkPHP V5<br/><span style="font-size:30px">十年磨一剑 - 为API开发设计的高性能框架</span></p><span style="font-size:22px;">[ V5.0 版本由 <a href="http://www.qiniu.com" target="qiniu">七牛云</a> 独家赞助发布 ]</span></div><script type="text/javascript" src="https://tajs.qq.com/stats?sId=9347272" charset="UTF-8"></script><script type="text/javascript" src="https://e.topthink.com/Public/static/client.js"></script><think id="ad_bd568ce7058a1091"></think>'; } public function hello(Request $request){ echo "请求参数:"; var_dump($request->param()); return "Hello ," . $request->param('name'); } }
http://127.0.0.1/tp5/public/index.php/index/index/hello?test=123&name=purplet
param参数使用方法
public function hello(Request $request){ echo "请求参数:"; echo "name:" . $request->param('name' , 'World' ,'strtolower'); echo "<br/>test:" . $request->param('test' , 'Thinkphp' ,'strtoupper'); }
会将传递的参数转换为对应的大小写
获取get、post参数
var_dump($request->get()); var_dump($request->post());
使用助手函数获取get、post参数
var_dump(input('get.')); var_dump(input('get.name')); var_dump(input('post.'));
这些都可以以上述方式进行请求。
请求参数的常用方法
请求方法
资源类型
访问地址
是否Ajax请求
请求参数
public function hello(Request $request){ echo "请求方法:".$request->method() . "<br/>"; echo "资源类型:".$request->type() . "<br/>"; echo "访同的IP: ".$request->ip() . "<br/>"; echo "是否是Ajax请求" . var_export($request->isAjax(),true) . "<br/>"; echo '请求的参数:'; var_dump($request->param()); echo '<br/>请求的参数:'; var_dump($request->only(['name']));#获取name参数对应的值 echo '<br/>请求的参数:'; var_dump($request->except(['name']));#获取排除name参数以外参数对应的值 }
访问http://127.0.0.1/tp5/public/index.php/index/index/hello?test=123&name=purplet
更多操作,如图所示
响应对象
ThinkPHP5的Response 响应对象由think\Response类或者子类完成
自动输出
修改config.php中的default_return_type选项为json,修改index.php内容为return一个数组
$data = ["name"=>"purplet","status"=>"1"]; return $data;
访问后就会得到一个json数据
手动输出
不修改config.php中的默认返回类型,对index.php中的返回内容加json包裹,输出与上图相同。
$data = ["name"=>"purplet","status"=>"1"]; return json($data);
返回头信息可进行修改
$data = ["name"=>"purplet","status"=>"1"]; return json($data)->code(201)->header(['flag'=>'test']);
页面跳转
<?php namespace app\index\controller; use think\Request; class Index { use \traits\controller\Jump; public function index() { return '<style type="text/css">*{ padding: 0; margin: 0; } .think_default_text{ padding: 4px 48px;} a{color:#2E5CD5;cursor: pointer;text-decoration: none} a:hover{text-decoration:underline; } body{ background: #fff; font-family: "Century Gothic","Microsoft yahei"; color: #333;font-size:18px} h1{ font-size: 100px; font-weight: normal; margin-bottom: 12px; } p{ line-height: 1.6em; font-size: 42px }</style><div style="padding: 24px 48px;"> <h1>:)</h1><p> ThinkPHP V5<br/><span style="font-size:30px">十年磨一剑 - 为API开发设计的高性能框架</span></p><span style="font-size:22px;">[ V5.0 版本由 <a href="http://www.qiniu.com" target="qiniu">七牛云</a> 独家赞助发布 ]</span></div><script type="text/javascript" src="https://tajs.qq.com/stats?sId=9347272" charset="UTF-8"></script><script type="text/javascript" src="https://e.topthink.com/Public/static/client.js"></script><think id="ad_bd568ce7058a1091"></think>'; } public function hello($name){ if ('thinkphp' == $name){ $this -> success('欢迎来学习','admin'); }else{ $this -> error('错误的name','guest'); } } public function admin(){ return "Hello admin!"; } public function guest(){ return "Hello guest!"; } }
通过name传递的参数是否与thinkphp相等,判断跳转到哪个方法,自定义admin方法和guest方法
当传递name不等于thinkphp时页面就跳转出现以下错误,然后跳到guest方法中,等于thinkphp跳转方式同理
页面重定向
public function hello($name){ if ('thinkphp' == $name){ $this -> redirect('http://www.baidu.com'); }else{ return "Hello Guest"; } }
利用redirect方法,当name=thinkphp就会跳转到百度首页
查询语言
本小节主要介绍thinkphp与数据库进行交互的命令操作
首先修改好database.php中的连接配置。
然后在index.php引入数据库模块:use think\Db;
<?php namespace app\index\controller; use think\Db; class Index { public function index() { return '<style type="text/css">*{ padding: 0; margin: 0; } .think_default_text{ padding: 4px 48px;} a{color:#2E5CD5;cursor: pointer;text-decoration: none} a:hover{text-decoration:underline; } body{ background: #fff; font-family: "Century Gothic","Microsoft yahei"; color: #333;font-size:18px} h1{ font-size: 100px; font-weight: normal; margin-bottom: 12px; } p{ line-height: 1.6em; font-size: 42px }</style><div style="padding: 24px 48px;"> <h1>:)</h1><p> ThinkPHP V5<br/><span style="font-size:30px">十年磨一剑 - 为API开发设计的高性能框架</span></p><span style="font-size:22px;">[ V5.0 版本由 <a href="http://www.qiniu.com" target="qiniu">七牛云</a> 独家赞助发布 ]</span></div><script type="text/javascript" src="https://tajs.qq.com/stats?sId=9347272" charset="UTF-8"></script><script type="text/javascript" src="https://e.topthink.com/Public/static/client.js"></script><think id="ad_bd568ce7058a1091"></think>'; } public function hello(){ $result = Db::query('select * from think_data where id = 5'); var_dump($result); } }
但是一般在代码编写的过程中,不推荐直接query执行数据库命令
普通查询表达式
public function hello(){ //select * from think_data where id=1; $result = Db::name("data")->where("id",1)->find(); var_dump($result); }
Db::name(“data”)相当于select * from think_data表,因为在database.php中配置了默认前缀,所以要有个think_开头;where(“id”,1)相当于where id=1;最后->find()相当于查询所有内容。这样的话ThinkPHP会有一个预处理,而find方法还有个默认操作,如果不指定where则代表查询满足条件的第一个数据。
其中若想where id>=2,则这么写:where(“id”,”>=”,2)
//select * from think_data where id>=2 limit 10; $result = Db::name("data")->where("id",">=",2)->limit(10)->select(); var_dump($result);
//select * from think_data where id in (1,3,5) limit 10; $result = Db::name("data")->where("id","in",[1,3,5])->limit(10)->select(); var_dump($result);
//select * from think_data where id between 3 and 5 limit 10; $result = Db::name("data")->where("id","between",[3,5])->limit(10)->select(); var_dump($result);
//select * from think_data where id between 3 and 5 and name like '%think%'; $result = Db::name("data")->where("id","between",[3,5])->where('name','like','%think%')->limit(10)->select(); var_dump($result);
批量查询表达式
public function hello(){ //select * from think_data where id between 3 and 5 and name like '%think%'; $result = Db::name("data")->where([ "id" => ["between" , "1,5"], "name" => ["like","%think%"], ])->select(); var_dump($result); }
public function hello(){ //select * from think_data where id in (1,2,3) or id between 3 and 5 and name like '%think%'; $result = Db::name("data")->where([ "id" => [["between" , "3,5"],["in",[1,2,3]],"or"], "name" => ["like","%think%"], ])->select(); var_dump($result); }
快截查询
public function hello(){ //select * from think_data where name and pass like '%think%'; $result = Db::name("data")->where("name&pass","like","%a%")->select(); var_dump($result); }
使用Query对象
public function hello(){ //select * from think_data where name like '%think%'; $query = new \think\db\Query; $query->name("data")->where("name","like","%think%")->select(); $result = Db::select($query); var_dump($result); }
闭包查询
public function hello(){ //select * from think_data where name like '%think%'; $result = Db::name("data")->select(function($query){ $query->name("data")->where("name","like","%think%")->select(); }); var_dump($result); }
获取数据
public function hello(){ //select * from think_data where name like '%think%'; $name = Db::name('data')->where('where',5)->value("name"); var_dump($name); }
获取列数据
public function hello(){ $name = Db::name('data')->column("name"); var_dump($name); }
聚合查询
public function hello(){ $max = Db::name('data')->max("id"); var_Dump($max); }
字符串查询
public function hello(){ //select * from think_data where(id>'5' and name is not null); $result = Db::name('data')->where("id > :id and name is not null ",["id" => 5])->select(); var_dump($result); }