本講的內容為請求對象(Request)的概念和使用方法
#### 請求變量
ThinkPHP5使用了請求對象(由`think\Request`類實現)來記錄當前請求的相關信息和執行相關操作,而獲取請求變量是請求對象的一個重要用途。
例如,在原生PHP開發中經常會被使用的 `$_GET`、`$_POST`、`$_SESSION`和`$_COOKIE`等變量都被統一封裝到請求對象的方法里面,取而代之的是使用請求對象的`get()`、`post()`、`session()`和 `cookie()`方法來獲取當前請求的系統變量。
為什么要用請求對象的方法來替代原生的系統變量呢?
首先直接操作系統變量存在很多的不便和不足,例如大寫變量、不支持過濾和批量讀取、排除等等細節; 其次,也不利于單元測試。
在控制器操作方法中獲取請求變量的方法如下:
~~~
<?php
namespace app\index\controller;
use think\Request;
class Index
{
public function hello()
{
// 獲取當前的GET變量
dump(Request::instance()->get());
// 或者使用助手函數
dump(request()->get());
}
}
~~~
`request函數`是系統提供的一個簡化讀取的助手函數,用于獲取當前請求對象的單例。在ThinkPHP5中,你不需要根據當前請求類型來判斷使用get()還是post()方法(事實上這是一個很愚蠢的做法),框架提供了一個專門的param()方法來統一獲取當前請求的變量,會自動識別當前的請求類型(支持`PUT/DELETE`等所有的請求類型)獲取不同的請求變量,例如:
~~~
<?php
namespace app\index\controller;
use think\Request;
class Index
{
public function hello()
{
// 獲取當前的請求變量
dump(Request::instance()->param());
// 或者使用助手函數
dump(request()->param());
}
}
~~~
獲取單個變量的方式
~~~
<?php
namespace app\index\controller;
use think\Request;
class Index
{
public function hello()
{
// 獲取當前的請求變量
dump(Request::instance()->param('name'));
// 或者使用助手函數
dump(request()->param('name'));
}
}
~~~
所以,官方的建議是最好棄用`get()`、`post()` 這些方法,直接使用`param()`方法。
`param`方法的優勢包括:支持`統一過濾`以及更方便的`調用多級變量`,下面舉個例子說明。
框架默認并沒有設置任何的過濾機制,請根據應用的需要設置全局的過濾規則。
首先設置全局過濾規則如下:
~~~
// 默認全局過濾方法
'default_filter' => 'htmlspecialchars',
~~~
控制器的hello方法改為:
~~~
<?php
namespace app\index\controller;
use think\Request;
class Index
{
public function hello()
{
// 獲取當前的請求變量
dump(Request::instance()->param('data.name', '', 'strtoupper'));
// 或者使用助手函數
dump(request()->param('data.name', '', 'strtoupper'));
}
}
~~~
然后再來測試下數據(注意,data傳入一個數組的正確姿勢):

如果要判斷某個請求變量是否存在,可以使用:
~~~
<?php
namespace app\index\controller;
use think\Request;
class Index
{
public function hello()
{
// 判斷變量是否存在(當前請求變量中)
dump(request()->has('name'));
// 判斷是否存在get變量
dump(request()->has('name', 'get'));
// 或者使用助手函數
dump(input('?name'));
dump(input('?get.name'));
}
}
~~~
為了方便使用,系統還提供了獲取多個變量的方法(不是獲取所有變量):
~~~
<?php
namespace app\index\controller;
class Index
{
public function hello()
{
// 獲取請求變量中的id和name變量
dump(request()->only('id,name'));
// 獲取請求變量中的id以外的變量
dump(request()->except('id'));
}
}
~~~
only表示只獲取部分變量,except表示獲取除了排除變量之外的所有變量,測試效果如下:

請求對象提供的用于變量獲取的方法:
|方法| 描述|
|--|--|
|has |判斷請求變量是否存在|
|param |獲取當前請求的變量|
|get 獲取 |$_GET變量|
|post |獲取 $_POST 變量|
|put |獲取 PUT 變量|
|delete |獲取 DELETE 變量|
|session |獲取 $_SESSION變量|
|cookie |獲取 $_COOKIE 變量|
|request |獲取 $_REQUEST 變量|
|server |獲取 $_SERVER 變量|
|env |獲取 $_ENV變量|
|route |獲取 路由(包括PATHINFO) 變量|
|file |獲取 $_FILES變量|
|only |指定允許變量名獲取請求變量|
|except |指定排除變量名獲取請求變量|
#### 請求信息
每次請求還包含了URL信息、路由信息、模塊/控制器/操作名稱、客戶端請求IP、請求類型以及請求頭信息等等,我們都稱之為請求信息。
使用`param方法`獲取請求變量無需判斷當前的請求類型,如果你確實需要獲取當前的請求類型,則可以使用method方法。
~~~
<?php
namespace app\index\controller;
class Index
{
public function hello()
{
// 獲取當前請求類型
return '當前請求類型:' . request()->method();
}
}
~~~
使用postman進行get請求訪問: `http://tp5.com/index/index/hello`
頁面輸出結果為:

使用put請求訪問后頁面輸出:

如果你沒有安裝postman,那么可以用ThinkPHP5的請求偽裝功能來進行請求測試。
創建一個測試表單如下:
~~~
<form action="/index/index/hello" method="post" >
<input type="hidden" name="_method" value="put">
<input type="submit" value="發起模擬請求">
</form>
~~~
為了更好的演示效果,我們調整下控制器的hello方法:
~~~
<?php
namespace app\index\controller;
class Index
{
public function hello()
{
// 獲取當前請求類型
return '模擬請求類型:' . request()->method() . '<br/>實際請求類型:' . request()->method(true);
}
}
~~~
`method(true)`表示獲取實際的請求類型。
點擊發起模擬請求按鈕后頁面輸出:
~~~
模擬請求類型:PUT
實際請求類型:POST
~~~
有些時候,我們需要判斷當前的請求是否為`ajax`,就可以使用`isAjax`方法來判斷:
~~~
<?php
namespace app\index\controller;
class Index
{
public function hello()
{
// 判斷是否ajax提交
return '是否AJAX請求:' . var_export(request()->isAjax(), true);
}
}
~~~

請求對象中關于請求信息的方法包括:
|方法 |描述|
|--|--|
|method |獲取當前請求類型|
|isGet |判斷是否get請求|
|isPost |判斷是否post請求|
|isPut |判斷是否put請求|
|isDelete |判斷是否delete請求|
|isHead |判斷是否為head請求|
|isPatch |判斷是否為patch請求|
|isOptions |判斷是否為options請求|
|time |獲取當前請求的請求時間|
|isSsl |判斷是否為https請求|
|isAjax |判斷是否為ajax請求|
|isPjax |判斷是否為pjax請求|
|ip |判斷當前訪問的客戶端IP|
|isMobile |判斷是否為手機訪問|
|module |獲取請求的模塊名|
|controller |獲取請求的控制器名|
|action |獲取請求的操作名|
|host |獲取請求的host地址|
|port |獲取請求的端口地址|
|protocol |獲取請求的協議|
|remotePort |獲取請求的REMOTE_PORT地址|
|header |獲取當前請求頭信息|
|contentType |獲取當前請求的內容類型(V5.0.5+)|
#### 請求緩存
請求緩存的原理是第一次請求的時候會根據當前請求的緩存標識把響應輸出的內容緩存起來并且設置HTTP緩存(如果判斷已經存在請求緩存的話會直接讀取請求緩存并且設置HTTP緩存),當第二次訪問相同的請求標識的時候,會自動讀取HTTP緩存(也就是瀏覽器緩存)內容而不是真實的調用請求方法,也就是說請求緩存是HTTP緩存+響應(數據)緩存的合體。
如果你需求全局使用請求緩存的話,在應用配置中設置下面的兩個配置參數:
~~~
'request_cache' => true,
'request_cache_expire' => 600,
~~~
上面的設置會開啟全局請求緩存,默認的緩存標識為當前請求的URL地址(做md5編碼處理),并且緩存有效期為600秒,也就是說10分鐘之內的相同get請求(請求緩存只支持GET請求)會進行緩存,可以有效提升性能。
我們使用hello方法進行測試:
~~~
<?php
namespace app\index\controller;
class Index
{
public function hello()
{
return '當前請求時間:' . date('y-m-d H:i:s', request()->time());
}
}
~~~
第一次訪問:`http://tp5.com/index/index/hello`
查看請求信息顯示:

再次訪問的時候顯示(注意頁面輸出的請求時間并沒有變化):

當給當前URL地址增加參數后,再次訪問后會發現請求緩存已經無效了(因為全局請求緩存默認是根據URL地址緩存)。

如果需要設置個別請求的緩存參數,可以在路由規則中設置,例如:
~~~
// 定義路由規則 并設置3600秒的緩存
Route::get('blog/:id','index/Blog/read',['cache'=>3600]);
路由的請求緩存默認標識是當前的pathinfo地址,也支持指定緩存標識,例如:
// 定義路由規則 并設置3600秒的緩存
Route::get('blog/:id','index/Blog/read',['cache'=>['blog/:id/:page',3600]]);
~~~
緩存標識中的`:id` 和 `:page` 都會被替換成當前請求變量的值。
路由規則中的緩存參數是優先的,并且不依賴全局請求緩存設置,如果你只需要部分請求使用請求緩存功能,那么可以直接在路由規則中設置。
#### 參數綁定
雖然提供了助手函數,在操作方法中讀取請求變量的方式還是比較麻煩,使用參數綁定可以進一步簡化請求變量的讀取,我們通過hello方法的代碼來對比下前后兩種請求變量的獲取方式。
不使用參數綁定的方式:
~~~
<?php
namespace app\index\controller;
class Index
{
public function hello()
{
// 獲取當前的請求變量
return request()->param('name');
}
}
~~~
使用參數綁定的方式:
~~~
<?php
namespace app\index\controller;
class Index
{
public function hello($name = '')
{
// 獲取當前請求的name變量
return $name;
}
}
~~~
顯而易見使用參數綁定方式的代碼更簡潔,我們給hello方法添加了name參數(注意:這里給了默認值可以避免當前請求沒有傳入name變量的時候出錯),會自動綁定當前請求變量中的`name變量`(并且支持全局的變量過濾),這種方式就稱之為參數綁定。
> 默認情況下,操作方法的參數定義順序對參數綁定的參數沒有影響,參數綁定只是根據參數名自動綁定。
如果我們定義了多個參數:
~~~
<?php
namespace app\index\controller;
class Index
{
public function hello($name = '', $id = 0)
{
return $name . ':' . $id;
}
}
~~~
下面的URL訪問都是有效的
~~~
http://tp5.com/index/index/hello/name/thinkphp/id/10
http://tp5.com/index/index/hello/id/10/name/thinkphp
~~~
如果設置了參數順序綁定的話(所有URL相關的配置必須在應用配置文件中設置):
~~~
// URL參數按順序解析
'url_param_type' => 1,
~~~
當我們訪問下面的URL地址:`http://tp5.com/index/index/hello/thinkphp/10`
頁面輸出結果為:
> thinkphp:10
當訪問下面的URL地址:`http://tp5.com/index/index/hello/10/thinkphp`
頁面輸出結果為:
> 10:thinkphp
記得始終給參數定義一個默認值可以避免當前請求沒有傳值的時候拋出異常。
* * * * *
參考:
http://www.hmoore.net/thinkphp/controller-in-detail/250871