## 獲取用戶請求[](http://www.digpage.com/request.html#id1 "Permalink to this headline")
PHP并未提供集中的、統一的界面以獲取用戶請求,而是分散在?$_SERVER?$_POST?等變量和其他代碼中。 萬能的Yii怎么會允許群雄割據這種局面出現呢?他肯定是要一統江湖的。 那么對于任何Yii應用而言,初始化后第一件正事,就是獲取用戶請求。 這個代碼在?yii\base\Application::run()?中:
~~~
public function run()
{
try {
$this->state = self::STATE_BEFORE_REQUEST;
$this->trigger(self::EVENT_BEFORE_REQUEST);
$this->state = self::STATE_HANDLING_REQUEST;
// 獲取用戶請求,并進行處理,處理的過程也是產生響應內容的過程
$response = $this->handleRequest($this->getRequest());
$this->state = self::STATE_AFTER_REQUEST;
$this->trigger(self::EVENT_AFTER_REQUEST);
$this->state = self::STATE_SENDING_RESPONSE;
// 將響應內容發送回用戶
$response->send();
$this->state = self::STATE_END;
return $response->exitStatus;
} catch (ExitException $e) {
$this->end($e->statusCode, isset($response) ? $response : null);
return $e->statusCode;
}
}
~~~
上面的代碼主要看注釋的兩個地方,聰明的讀者朋友們一定都猜出來了,?$this->getRequest()?就是用于獲取用戶請求的嘛。
其實這是一個getter,用于獲取Application的request組件 (component) 。Yii用這個組件來代表用戶請求, 他承載著所有的用戶輸入信息。
我們知道,Yii應用有命令行(Console)應用和Web應用之分。因此,這個Request類其實涉及到了以下的類:
* yii\base\Request?Request類基類
* yii\console\Request?表示Console應用的的Request
* yii\web\Request?表示Web應用的Request
下面我們逐一進行講解。
## 基類Request[](http://www.digpage.com/request.html#id2 "Permalink to this headline")
基類是對Console應用和Web應用Request的抽象,他僅僅定義了兩個屬性和一個虛函數:
~~~
abstract class Request extends Component
{
// 屬性scriptFile,用于表示入口腳本
private $_scriptFile;
// 屬性isConsoleRequest,用于表示是否是命令行應用
private $_isConsoleRequest;
// 虛函數,要求子類來實現
// 這個函數的功能主要是為了把Request解析成路由和相應的參數
abstract public function resolve();
// isConsoleRequest屬性的getter函數
// 使用 PHP_SAPI 常量判斷當前應用是否是命令行應用
public function getIsConsoleRequest()
{
// 一切 PHP_SAPI 不為 'cli' 的,都不是命令行
return $this->_isConsoleRequest !== null ?
$this->_isConsoleRequest : PHP_SAPI === 'cli';
}
// isConsoleRequest屬性的setter函數
public function setIsConsoleRequest($value)
{
$this->_isConsoleRequest = $value;
}
// scriptFile屬性的getter函數
// 通過 $_SERVER['SCRIPT_FILENAME'] 來獲取入口腳本名
public function getScriptFile()
{
if ($this->_scriptFile === null) {
if (isset($_SERVER['SCRIPT_FILENAME'])) {
$this->setScriptFile($_SERVER['SCRIPT_FILENAME']);
} else {
throw new InvalidConfigException(
'Unable to determine the entry script file path.');
}
}
return $this->_scriptFile;
}
// scriptFile屬性的setter函數
public function setScriptFile($value)
{
$scriptFile = realpath(Yii::getAlias($value));
if ($scriptFile !== false && is_file($scriptFile)) {
$this->_scriptFile = $scriptFile;
} else {
throw new InvalidConfigException(
'Unable to determine the entry script file path.');
}
}
}
~~~
yii\base\Request?通過getter和setter提供了兩個可讀寫的屬性,?isConsoleRequest?和?scriptFile?。 同時,要求子類實現一個?resolve()?方法。
基類的代碼相對簡單,主要涉及到PHP的一些知識,如?PHP_SAPI?$_SERVER['SCRIPT_FILENAME']?等, 讀者朋友們可以通過搜索引擎或PHP手冊了解下相關的知識,相信上面的代碼難不倒你們的。
## 命令行應用Request[](http://www.digpage.com/request.html#id3 "Permalink to this headline")
命令行應用Request由?yii\console\Request?負責實現,相比較于?yii\base\Request?稍有豐富:
~~~
class Request extends \yii\base\Request
{
// 屬性 params,用于表示命令行參數
private $_params;
// params屬性的getter函數
// 通過 $_SERVER['argv'] 來獲取命令行參數
public function getParams()
{
if (!isset($this->_params)) {
if (isset($_SERVER['argv'])) {
$this->_params = $_SERVER['argv'];
// 刪除數組的第一個元素,這個元素是PHP腳本名。
// 因此,屬性params中全部是參數,不帶腳本名
array_shift($this->_params);
} else {
$this->_params = [];
}
}
return $this->_params;
}
// params屬性的setter函數
public function setParams($params)
{
$this->_params = $params;
}
// 父類虛函數的實現
public function resolve()
{
// 獲取全部的命令行參數
$rawParams = $this->getParams();
// 第一個命令行參數作為路由
if (isset($rawParams[0])) {
$route = $rawParams[0];
array_shift($rawParams);
} else {
$route = '';
}
$params = [];
// 遍歷剩余的全部命令行參數
foreach ($rawParams as $param) {
// 正則匹配每一個參數
if (preg_match('/^--(\w+)(=(.*))?$/', $param, $matches)) {
// 參數名
$name = $matches[1];
// yii\console\Application::OPTION_APPCONFIG = 'appconfig'
if ($name !== Application::OPTION_APPCONFIG) {
$params[$name] = isset($matches[3]) ? $matches[3] : true;
}
// 無名參數,直接作為參數值
} else {
$params[] = $param;
}
}
return [$route, $params];
}
}
~~~
相比較于?yii\base\Request?,?yii\console\Request?提供了一個?params?屬性, 該屬性以數組形式保存了入口腳本?Yii?的命令行參數。這是通過?$_SERVER['argv']?獲取的。 注意?params?屬性不保存入口腳本名。入口腳本名由基類的?scriptName?屬性保存。
同時,?yii\console\Request?還實現了父類的?resolve()?虛函數, 這個函數主要做了這么幾件事:
* 將?params?屬性的第一個元素作為路由。如果入口腳本未提供任何參數,也即?params?是個空數組, 那么將路由置為一個空字符串。
* 遍歷?params?中剩余的參數,使用正則匹配Yii應用的參數名和參數值,看看是不是--參數名=參數值?形式。 其中,以?--?打頭的任意字母、數字、下劃線的組合,就是參數名。 緊跟參數名的?=?后面的內容,則為參數值。 對于僅有參數名,沒有參數值的,視參數值為true?。
* 如果正則匹配不成功,則將這個命令行參數作為Yii應用的一個無名參數的值。
* 如果第二步中的參數名為?appconfig?則忽略該參數,Console Application會專門針對該參數進行處理。
* 上面步驟中的參數和參數值,被保存進一個數組中。數組的鍵表示參數名,數組的值表示參數值。
* 最終?resolve()?返回一個數組,第一個元素是一個表示路由的字符串,第二元素則是參數數組。 該方法由Application在處理Request時調用。
關于?appconfig?參數的問題,只要在調用?yii?時,指定了?appconfig?參數, 就表明不使用默認的參數配置文件,而使用該參數所指定的配置文件。相關的代碼在?yii\console\Application?中:
~~~
// 定義一個常量
const OPTION_APPCONFIG = 'appconfig';
// yii\console\Application類的構造函數
public function __construct($config = [])
{
// 重點看這句,會調用loadConfig() 成員函數
$config = $this->loadConfig($config);
parent::__construct($config);
}
// 如果指定的配置文件存在,那么返回其配置數組
// 否則,返回構造函數調用時的數組
protected function loadConfig($config)
{
if (!empty($_SERVER['argv'])) {
// 設定了一個字符串 "--appconfig="
$option = '--' . self::OPTION_APPCONFIG . '=';
// 遍歷所有命令行參數,看看能不能找到上面說的這個字符串
foreach ($_SERVER['argv'] as $param) {
if (strpos($param, $option) !== false) {
// 截取參數值部分
$path = substr($param, strlen($option));
if (!empty($path) && is_file($file = Yii::getAlias($path))) {
// 將指定文件的內容引入進來
return require($file);
} else {
die("The configuration file does not exist: $path\n");
}
}
}
}
return $config;
}
~~~
講完了Request基類和命令行應用的Request只是熱身而已,接下來要講的Web應用Request才是重頭。 畢竟最最主要的,還是Web開發嘛。考慮到Web Request的內容較多,還是單獨成?[_Web應用Request_](http://www.digpage.com/web_request.html#web-request)?來講吧。
- 更新記錄
- 導讀
- Yii是什么
- Yii2.0的亮點
- 背景知識
- 如何閱讀本書
- Yii基礎
- 屬性(Property)
- 事件(Event)
- 行為(Behavior)
- Yii約定
- Yii應用的目錄結構和入口腳本
- 別名(Alias)
- Yii的類自動加載機制
- 環境和配置文件
- 配置項(Configuration)
- Yii模式
- MVC
- 依賴注入和依賴注入容器
- 服務定位器(Service Locator)
- 請求與響應(TBD)
- 路由(Route)
- Url管理
- 請求(Reqeust)
- Web應用Request
- Yii與數據庫(TBD)
- 數據類型
- 事務(Transaction)
- AcitveReocrd事件和關聯操作
- 樂觀鎖與悲觀鎖
- 《深入理解Yii2.0》視頻教程
- 第一講:基礎配置
- 第二講:用戶登錄
- 第三講:文章及評論的模型
- 附錄
- 附錄1:Yii2.0 對比 Yii1.1 的重大改進
- 附錄2:Yii的安裝
- 熱心讀者