深入理解Yii2
导读
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事件和关联操作
乐观锁与悲观锁
附录
附录1:Yii2.0 对比 Yii1.1 的重大改进
附录2:Yii的安装
本文档使用 MrDoc 发布
-
+
首页
请求(Reqeust)
### 获取用户请求 > 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 > 基类是对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 > 命令行应用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_ 来讲吧。
admin
2022年12月19日 14:08
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Markdown文件
分享
链接
类型
密码
更新密码