摘要:在分析源碼的過程中主要借助了工具。運(yùn)行應(yīng)用分析在上面的構(gòu)造函數(shù)執(zhí)行完后,開始運(yùn)行應(yīng)用。發(fā)送響應(yīng)到終端用戶入口腳本接收應(yīng)用主體傳來(lái)的退出狀態(tài)并完成請(qǐng)求的處理。
前言
本文主要分析Yii2應(yīng)用的啟動(dòng)、運(yùn)行的過程,主要包括以下三部分:入口腳本、啟動(dòng)應(yīng)用、運(yùn)行應(yīng)用。
在分析源碼的過程中主要借助了Xdebug工具。
文件位置:webindex.php
//定義全局變量 defined("YII_DEBUG") or define("YII_DEBUG", true); defined("YII_ENV") or define("YII_ENV", "dev"); //composer自動(dòng)加載代碼機(jī)制,可參考 https://segmentfault.com/a/1190000010788354 require(__DIR__ . "/../vendor/autoload.php"); //1.引入工具類Yii //2.注冊(cè)自動(dòng)加載函數(shù) //3.生成依賴注入中使用到的容器 require(__DIR__ . "/../vendor/yiisoft/yii2/Yii.php"); //加載應(yīng)用配置 $config = require(__DIR__ . "/../config/web.php"); //生成應(yīng)用并運(yùn)行 (new yiiwebApplication($config))->run();
分析: (new yiiwebApplication($config))->run()
根據(jù)$config配置,生成應(yīng)用實(shí)例(啟動(dòng)應(yīng)用:new yiiwebApplication($config))
運(yùn)行應(yīng)用實(shí)例(運(yùn)行應(yīng)用:yiiwebApplication::run())
分析:new yiiwebApplication($config)
主要就是執(zhí)行構(gòu)造函數(shù)(代碼位置:vendoryiisoftyii2aseApplication.php)
public function __construct($config = []) { //將Yii::$app指向當(dāng)前的Application實(shí)例,因此后續(xù)就可以通過Yii::$app來(lái)調(diào)用應(yīng)用 Yii::$app = $this; //調(diào)用yiiaseModule::setInstance($instance) //將Application實(shí)例本身記錄為“已加載模塊” //詳細(xì)參考本文后續(xù)“1-1分析” static::setInstance($this); //設(shè)置當(dāng)前應(yīng)用狀態(tài) $this->state = self::STATE_BEGIN; //進(jìn)行一些預(yù)處理(根據(jù)$config配置應(yīng)用) //詳細(xì)代碼位置:yiiaseApplication::preInit(&$config) //主要根據(jù)config文件做了以下這些事情 //1.判斷$config中是否有配置項(xiàng)‘id’(必須有,否則拋異常) //2.設(shè)置別名:@app,@vendor,@bower,@npm,@runtime //3.設(shè)置時(shí)間區(qū)域(timeZone) //4.自定義配置容器(Yii::$container)的屬性(由這里我們知道可以自定義配置容器) //5.合并核心組件配置到自定義組件配置:數(shù)組$config["components"] //(核心組件有哪些參考:yiiwebApplication::coreComponents()) //(注意:這個(gè)方法中$config使用了引用,所以合并$config["components"]可以改變$config原來(lái)的值) $this->preInit($config); //注冊(cè)ErrorHandler,這樣一旦拋出了異常或錯(cuò)誤,就會(huì)由其負(fù)責(zé)處理 //代碼位置:yiiaseApplication::registerErrorHandler(&$config) //詳細(xì)參考本文后續(xù)“1-2分析” $this->registerErrorHandler($config); //根據(jù)$config配置Application //然后執(zhí)行yiiwebApplication::init()(實(shí)際上是執(zhí)行yiiaseApplication::init()) //詳細(xì)參考本文后續(xù)“1-3分析” Component::__construct($config); }
1-1分析:yiiaseModule::setInstance($instance)
public static function setInstance($instance) { if ($instance === null) { unset(Yii::$app->loadedModules[get_called_class()]); } else { //將Application實(shí)例本身記錄為“已加載模塊” Yii::$app->loadedModules[get_class($instance)] = $instance; } }
1-2分析:yiiaseApplication::registerErrorHandler(&$config)
//yiiwebApplication的 $errorHandler 對(duì)應(yīng) yiiwebErrorHandler(由yiiwebApplication::coreComponents()得知) //而yiiwebErrorHandler繼承自yiiaseErrorHandler protected function registerErrorHandler(&$config) { if (YII_ENABLE_ERROR_HANDLER) { if (!isset($config["components"]["errorHandler"]["class"])) { echo "Error: no errorHandler component is configured. "; exit(1); } //可以看到就是根據(jù)$config["components"]["errorHandler"]來(lái)配置 $this->set("errorHandler", $config["components"]["errorHandler"]); unset($config["components"]["errorHandler"]); //通過PHP函數(shù)注冊(cè)error handler(具體參考下面的yiiaseErrorHandler::register()) $this->getErrorHandler()->register(); } } //默認(rèn)config文件關(guān)于error handler的配置,這里的配置會(huì)合并到Y(jié)ii2本身對(duì)核心組件的定義中去(),核心組件的定義在yiiwebApplication::coreComponents() "components" => [ "errorHandler" => [ "errorAction" => "site/error", ], ], //yiiaseErrorHandler::register() public function register() { //該選項(xiàng)設(shè)置是否將錯(cuò)誤信息作為輸出的一部分顯示到屏幕, //或者對(duì)用戶隱藏而不顯示。 ini_set("display_errors", false); //當(dāng)拋出異常且沒有被catch的時(shí)候,由yiiaseErrorHandler::handleException()處理 set_exception_handler([$this, "handleException"]); if (defined("HHVM_VERSION")) { set_error_handler([$this, "handleHhvmError"]); } else { //當(dāng)出現(xiàn)error的時(shí)候由yiiaseErrorHandler::handleError()處理 set_error_handler([$this, "handleError"]); } if ($this->memoryReserveSize > 0) { $this->_memoryReserve = str_repeat("x", $this->memoryReserveSize); } ////當(dāng)出現(xiàn)fatal error的時(shí)候由yiiaseErrorHandler::handleFatalError()處理 register_shutdown_function([$this, "handleFatalError"]); } 一個(gè)值得注意的地方: 在handleException()、handleError()、handleFatalError()中會(huì)直接或間接調(diào)用yiiwebErrorHandle::renderException(),而在這個(gè)函數(shù)里面,有以下這一行代碼 $result = Yii::$app->runAction($this->errorAction); 回顧上面config文件中對(duì)errorAction這個(gè)屬性的定義,我們知道可以通過自定義一個(gè)action用于在異常或錯(cuò)誤時(shí)顯示自定義的出錯(cuò)頁(yè)面 例如yii2中默認(rèn)使用’site/error’這個(gè)action來(lái)處理
1-3分析:Component::__construct($config)
(代碼實(shí)際位置:vendoryiisoftyii2aseObject.php)
public function __construct($config = []) { if (!empty($config)) { //見下面的詳細(xì)分析 Yii::configure($this, $config); } //跳轉(zhuǎn)到y(tǒng)iiaseApplication::init(),見下面的分析 $this->init(); } //Yii::configure($object, $properties)分析 //實(shí)際上就是為Application的屬性賦值 //注意:這里會(huì)觸發(fā)一些魔術(shù)方法,間接調(diào)用了: //yiiwebApplication::setHomeUrl() //yiidiServiceLocator::setComponents()(這個(gè)值得注意,詳細(xì)參考本文后續(xù)“1-3-1分析”) //yiiaseModule::setModules() public static function configure($object, $properties) { foreach ($properties as $name => $value) { $object->$name = $value; } return $object; } yiiaseApplication::init()分析 public function init() { //設(shè)置當(dāng)前應(yīng)用狀態(tài) $this->state = self::STATE_INIT; //見下面的bootstrap() $this->bootstrap(); } protected function bootstrap() { //request是核心組件,這里使用了依賴注入機(jī)制(ServiceLocator)來(lái)獲取request //注意:這里是第一次調(diào)用request組件,會(huì)初始化該組件 $request = $this->getRequest(); //設(shè)置別名 Yii::setAlias("@webroot", dirname($request->getScriptFile())); Yii::setAlias("@web", $request->getBaseUrl()); //跳轉(zhuǎn)到y(tǒng)iiaseApplication::bootstrap(),詳細(xì)參考本文后續(xù)“1-3-2分析” //在這里主要是處理第三方擴(kuò)展(extensions)和一些預(yù)啟動(dòng)(bootstrap) parent::bootstrap(); }
1-3-1分析:yiidiServiceLocator::setComponents()
public function setComponents($components) { foreach ($components as $id => $component) { //設(shè)置各個(gè)組件的定義,這樣后續(xù)就能通過依賴注入機(jī)制來(lái)獲取獲取組件了 //應(yīng)該還記得這里的配置參數(shù)來(lái)源于config文件中的定義和yiiwebApplication::coreComponents()中的定義 $this->set($id, $component); } }
1-3-2分析:yiiaseApplication::bootstrap()
protected function bootstrap() { if ($this->extensions === null) { //@vendor/yiisoft/extensions.php是一些關(guān)于第三方擴(kuò)展的配置,當(dāng)用composer require安裝第三擴(kuò)展的時(shí)候就會(huì)將新的擴(kuò)展的相關(guān)信息記錄到該文件,這樣我們就可以在代碼中調(diào)用了 $file = Yii::getAlias("@vendor/yiisoft/extensions.php"); $this->extensions = is_file($file) ? include($file) : []; } foreach ($this->extensions as $extension) { if (!empty($extension["alias"])) { foreach ($extension["alias"] as $name => $path) { Yii::setAlias($name, $path); } } //進(jìn)行一些必要的預(yù)啟動(dòng) if (isset($extension["bootstrap"])) { $component = Yii::createObject($extension["bootstrap"]); if ($component instanceof BootstrapInterface) { Yii::trace("Bootstrap with " . get_class($component) . "::bootstrap()", __METHOD__); $component->bootstrap($this); } else { Yii::trace("Bootstrap with " . get_class($component), __METHOD__); } } } //預(yù)啟動(dòng),通常會(huì)包括‘log’組件,‘debug’模塊和‘gii’模塊(參考配置文件) foreach ($this->bootstrap as $class) { $component = null; if (is_string($class)) { if ($this->has($class)) { $component = $this->get($class); } elseif ($this->hasModule($class)) { $component = $this->getModule($class); } elseif (strpos($class, "") === false) { throw new InvalidConfigException("Unknown bootstrapping component ID: $class"); } } if (!isset($component)) { $component = Yii::createObject($class); } if ($component instanceof BootstrapInterface) { Yii::trace("Bootstrap with " . get_class($component) . "::bootstrap()", __METHOD__); $component->bootstrap($this); } else { Yii::trace("Bootstrap with " . get_class($component), __METHOD__); } } }
在完成了上面的流程之后,應(yīng)用就算啟動(dòng)成功了,可以開始運(yùn)行,處理請(qǐng)求了。
分析:yiiaseApplication::run()
在上面的構(gòu)造函數(shù)執(zhí)行完后,開始運(yùn)行應(yīng)用。即下面這行代碼的run()部分
(new yiiwebApplication($config))->run();//(實(shí)際上執(zhí)行的是yiiaseApplication::run())
public function run() { try { $this->state = self::STATE_BEFORE_REQUEST; //觸發(fā)事件’beforeRequest’,依次執(zhí)行該事件的handler, $this->trigger(self::EVENT_BEFORE_REQUEST); $this->state = self::STATE_HANDLING_REQUEST; //處理請(qǐng)求,這里的返回值是yiiwebResponse實(shí)例 //handleRequest(),詳細(xì)參考本文后續(xù)“2-1分析” $response = $this->handleRequest($this->getRequest()); $this->state = self::STATE_AFTER_REQUEST; //觸發(fā)事件’afterRequest’,依次執(zhí)行該事件的handler $this->trigger(self::EVENT_AFTER_REQUEST); $this->state = self::STATE_SENDING_RESPONSE; //發(fā)送響應(yīng),詳細(xì)參考本文后續(xù)“2-2分析” $response->send(); $this->state = self::STATE_END; return $response->exitStatus; } catch (ExitException $e) { //結(jié)束運(yùn)行 $this->end($e->statusCode, isset($response) ? $response : null); return $e->statusCode; } }
2-1分析: yiiwebApplication::handleRequest()
public function handleRequest($request) { if (empty($this->catchAll)) { try { //解析請(qǐng)求得到路由和相應(yīng)的參數(shù),這里會(huì)調(diào)用urlManager組件來(lái)處理 list ($route, $params) = $request->resolve(); } catch (UrlNormalizerRedirectException $e) { $url = $e->url; if (is_array($url)) { if (isset($url[0])) { // ensure the route is absolute $url[0] = "/" . ltrim($url[0], "/"); } $url += $request->getQueryParams(); } //當(dāng)解析請(qǐng)求出現(xiàn)異常時(shí)進(jìn)行重定向 return $this->getResponse()->redirect(Url::to($url, $e->scheme), $e->statusCode); } } else { //’catchAll’參數(shù)可以在配置文件中自定義,可用于在項(xiàng)目需要臨時(shí)下線維護(hù)時(shí)給出一個(gè)統(tǒng)一的訪問路由 $route = $this->catchAll[0]; $params = $this->catchAll; unset($params[0]); } try { Yii::trace("Route requested: "$route"", __METHOD__); //記錄下當(dāng)前請(qǐng)求的route $this->requestedRoute = $route; //執(zhí)行路由相對(duì)應(yīng)的action(yiiaseModule::runAction()詳細(xì)參考本文后續(xù)“2-1-1分析”) $result = $this->runAction($route, $params); if ($result instanceof Response) { return $result; } else { //如果action的返回結(jié)果不是Response的實(shí)例,則將結(jié)果封裝到Response實(shí)例的data屬性中 $response = $this->getResponse(); if ($result !== null) { $response->data = $result; } return $response; } } catch (InvalidRouteException $e) { throw new NotFoundHttpException(Yii::t("yii", "Page not found."), $e->getCode(), $e); } }
2-1-1分析: yiiaseModule::runAction()
public function runAction($route, $params = []) { //根據(jù)路由創(chuàng)建Controller實(shí)例(詳細(xì)參考本文后續(xù)“2-1-1-1分析”) $parts = $this->createController($route); if (is_array($parts)) { /* @var $controller Controller */ list($controller, $actionID) = $parts; $oldController = Yii::$app->controller; //設(shè)置當(dāng)前的Controller實(shí)例 Yii::$app->controller = $controller; //執(zhí)行action(yiiaseController::runAction()詳細(xì)參考本文后續(xù)“2-1-1-2分析”) $result = $controller->runAction($actionID, $params); if ($oldController !== null) { //可以看做是棧 Yii::$app->controller = $oldController; } return $result; } $id = $this->getUniqueId(); throw new InvalidRouteException("Unable to resolve the request "" . ($id === "" ? $route : $id . "/" . $route) . ""."); }
2-1-1-1分析: yiiaseModule::createController()
public function createController($route) { //如果route為空則設(shè)置route為默認(rèn)路由,這個(gè)可以在配置文件中自定義 if ($route === "") { $route = $this->defaultRoute; } // double slashes or leading/ending slashes may cause substr problem $route = trim($route, "/"); if (strpos($route, "http://") !== false) { return false; } if (strpos($route, "/") !== false) { list ($id, $route) = explode("/", $route, 2); } else { $id = $route; $route = ""; } //優(yōu)先使用controllerMap,controllerMap可以如下 /* [ "account" => "appcontrollersUserController", "article" => [ "class" => "appcontrollersPostController", "pageTitle" => "something new", ], ] */ // module and controller map take precedence if (isset($this->controllerMap[$id])) { $controller = Yii::createObject($this->controllerMap[$id], [$id, $this]); return [$controller, $route]; } //先判斷是否存在相應(yīng)的模塊 $module = $this->getModule($id); if ($module !== null) { //當(dāng)存在模塊時(shí),進(jìn)行遞歸 return $module->createController($route); } if (($pos = strrpos($route, "/")) !== false) { $id .= "/" . substr($route, 0, $pos); $route = substr($route, $pos + 1); } //最終找到Controller的id $controller = $this->createControllerByID($id); if ($controller === null && $route !== "") { //詳細(xì)見下面的代碼分析 $controller = $this->createControllerByID($id . "/" . $route); $route = ""; } //返回Controller實(shí)例和剩下的路由信息 return $controller === null ? false : [$controller, $route]; } public function createControllerByID($id) { $pos = strrpos($id, "/"); if ($pos === false) { $prefix = ""; $className = $id; } else { $prefix = substr($id, 0, $pos + 1); $className = substr($id, $pos + 1); } if (!preg_match("%^[a-z][a-z0-9-_]*$%", $className)) { return null; } if ($prefix !== "" && !preg_match("%^[a-z0-9_/]+$%i", $prefix)) { return null; } //進(jìn)行一些轉(zhuǎn)換 $className = str_replace(" ", "", ucwords(str_replace("-", " ", $className))) . "Controller"; $className = ltrim($this->controllerNamespace . "" . str_replace("/", "", $prefix) . $className, ""); if (strpos($className, "-") !== false || !class_exists($className)) { return null; } if (is_subclass_of($className, "yiiaseController")) { //通過依賴注入容器獲得Controller實(shí)例 $controller = Yii::createObject($className, [$id, $this]); return get_class($controller) === $className ? $controller : null; } elseif (YII_DEBUG) { throw new InvalidConfigException("Controller class must extend from yiiaseController."); } return null; }
2-1-1-2分析:yiiaseController::runAction()
public function runAction($id, $params = []) { //創(chuàng)建action實(shí)例,詳細(xì)見下面的代碼 $action = $this->createAction($id); if ($action === null) { throw new InvalidRouteException("Unable to resolve the request: " . $this->getUniqueId() . "/" . $id); } Yii::trace("Route to run: " . $action->getUniqueId(), __METHOD__); if (Yii::$app->requestedAction === null) { Yii::$app->requestedAction = $action; } $oldAction = $this->action; $this->action = $action; $modules = []; $runAction = true; //返回的modules包括該controller當(dāng)前所在的module,以及該module的所有祖先module(遞歸直至沒有祖先module) //然后從最初的祖先module開始,依次執(zhí)行“模塊級(jí)”的beforeActio() //如果有beforeAction()沒有返回true, 那么會(huì)中斷后續(xù)的執(zhí)行 // call beforeAction on modules foreach ($this->getModules() as $module) { if ($module->beforeAction($action)) { array_unshift($modules, $module); } else { $runAction = false; break; } } $result = null; //執(zhí)行當(dāng)前控制器的beforeAction,通過后再最終執(zhí)行action //(如果前面“模塊級(jí)beforeAction”沒有全部返回true,則這里不會(huì)執(zhí)行) if ($runAction && $this->beforeAction($action)) { // run the action //(代碼位置:yiiaseInlineAction::runWithParams()詳細(xì)參考本文后續(xù)“2-1-1-2-1分析”和yiiaseAction::runWithParams()詳細(xì)參考本文后續(xù)“2-1-1-2-2分析”) $result = $action->runWithParams($params); //執(zhí)行當(dāng)前Controller的afterAction $result = $this->afterAction($action, $result); //從當(dāng)前模塊開始,執(zhí)行afterAction,直至所有祖先的afterAction // call afterAction on modules foreach ($modules as $module) { /* @var $module Module */ $result = $module->afterAction($action, $result); } } if ($oldAction !== null) { $this->action = $oldAction; } //如果有beforeAction沒有通過,那么會(huì)返回null return $result; } public function createAction($id) { //默認(rèn)action if ($id === "") { $id = $this->defaultAction; } //獨(dú)立action(Standalone Actions ) $actionMap = $this->actions(); if (isset($actionMap[$id])) { //返回一個(gè)action實(shí)例,通常是yiiaseAction的子類 return Yii::createObject($actionMap[$id], [$id, $this]); } elseif (preg_match("/^[a-z0-9-_]+$/", $id) && strpos($id, "--") === false && trim($id, "-") === $id) { $methodName = "action" . str_replace(" ", "", ucwords(implode(" ", explode("-", $id)))); if (method_exists($this, $methodName)) { $method = new ReflectionMethod($this, $methodName); if ($method->isPublic() && $method->getName() === $methodName) { //InlineAction封裝了將要執(zhí)行的action的相關(guān)信息,該類繼承自yiiaseAction return new InlineAction($id, $this, $methodName); } } } return null; }
2-1-1-2-1分析: yiiaseInlineAction::runWithParams()
public function runWithParams($params) { //yiiwebController::bindActionParams()詳細(xì)參考本文后續(xù)“2-1-1-2-1-1分析” $args = $this->controller->bindActionParams($this, $params); Yii::trace("Running action: " . get_class($this->controller) . "::" . $this->actionMethod . "()", __METHOD__); if (Yii::$app->requestedParams === null) { Yii::$app->requestedParams = $args; } //真正地調(diào)用開發(fā)者寫的Action代碼 return call_user_func_array([$this->controller, $this->actionMethod], $args); }
2-1-1-2-2分析:yiiaseAction::runWithParams()
public function runWithParams($params) { if (!method_exists($this, "run")) { throw new InvalidConfigException(get_class($this) . " must define a "run()" method."); } //yiiwebController::bindActionParams()詳細(xì)參考本文后續(xù)“2-1-1-2-1-1分析” $args = $this->controller->bindActionParams($this, $params); Yii::trace("Running action: " . get_class($this) . "::run()", __METHOD__); if (Yii::$app->requestedParams === null) { Yii::$app->requestedParams = $args; } if ($this->beforeRun()) { //執(zhí)行獨(dú)立Action的run方法 $result = call_user_func_array([$this, "run"], $args); $this->afterRun(); return $result; } else { return null; } }
2-1-1-2-1-1分析:yiiwebController::bindActionParams()
public function bindActionParams($action, $params) { if ($action instanceof InlineAction) { //如果是InlineAction則對(duì)Controller中相應(yīng)的action方法進(jìn)行反射 $method = new ReflectionMethod($this, $action->actionMethod); } else { //如果是獨(dú)立action則對(duì)該Action類的run方法進(jìn)行反射 $method = new ReflectionMethod($action, "run"); } $args = []; $missing = []; $actionParams = []; //通過php提供的反射機(jī)制綁定Action的參數(shù),同時(shí)還會(huì)判斷url中的參數(shù)是否滿足要求 foreach ($method->getParameters() as $param) { $name = $param->getName(); if (array_key_exists($name, $params)) { if ($param->isArray()) { $args[] = $actionParams[$name] = (array) $params[$name]; } elseif (!is_array($params[$name])) { $args[] = $actionParams[$name] = $params[$name]; } else { throw new BadRequestHttpException(Yii::t("yii", "Invalid data received for parameter "{param}".", [ "param" => $name, ])); } unset($params[$name]); } elseif ($param->isDefaultValueAvailable()) { $args[] = $actionParams[$name] = $param->getDefaultValue(); } else { $missing[] = $name; } } if (!empty($missing)) { throw new BadRequestHttpException(Yii::t("yii", "Missing required parameters: {params}", [ "params" => implode(", ", $missing), ])); } $this->actionParams = $actionParams; return $args; }
2-2分析: yiiwebResponse::send()
public function send() { if ($this->isSent) { return; } $this->trigger(self::EVENT_BEFORE_SEND); //預(yù)處理,詳見下面的代碼 $this->prepare(); $this->trigger(self::EVENT_AFTER_PREPARE); //發(fā)送http響應(yīng)的頭部,詳見下面的代碼 $this->sendHeaders(); //發(fā)送http響應(yīng)的主體,詳見下面的代碼 $this->sendContent(); $this->trigger(self::EVENT_AFTER_SEND); $this->isSent = true; } protected function prepare() { if ($this->stream !== null) { return; } //使用formatter對(duì)相應(yīng)進(jìn)行處理,這個(gè)可以在配置文件中自定義 if (isset($this->formatters[$this->format])) { $formatter = $this->formatters[$this->format]; if (!is_object($formatter)) { $this->formatters[$this->format] = $formatter = Yii::createObject($formatter); } if ($formatter instanceof ResponseFormatterInterface) { $formatter->format($this); } else { throw new InvalidConfigException("The "{$this->format}" response formatter is invalid. It must implement the ResponseFormatterInterface."); } } elseif ($this->format === self::FORMAT_RAW) { if ($this->data !== null) { $this->content = $this->data; } } else { throw new InvalidConfigException("Unsupported response format: {$this->format}"); } //確保響應(yīng)的content為string if (is_array($this->content)) { throw new InvalidParamException("Response content must not be an array."); } elseif (is_object($this->content)) { if (method_exists($this->content, "__toString")) { $this->content = $this->content->__toString(); } else { throw new InvalidParamException("Response content must be a string or an object implementing __toString()."); } } } protected function sendHeaders() { //判斷是否已經(jīng)把頭部發(fā)送出去了 if (headers_sent()) { return; } if ($this->_headers) { $headers = $this->getHeaders(); //設(shè)置并發(fā)送http響應(yīng)頭 foreach ($headers as $name => $values) { $name = str_replace(" ", "-", ucwords(str_replace("-", " ", $name))); // set replace for first occurrence of header but false afterwards to allow multiple $replace = true; foreach ($values as $value) { //主要就是調(diào)用了PHP的header()函數(shù) header("$name: $value", $replace); $replace = false; } } } $statusCode = $this->getStatusCode(); header("HTTP/{$this->version} {$statusCode} {$this->statusText}"); //主要就是調(diào)用了PHP的setcookie()函數(shù) $this->sendCookies(); } protected function sendContent() { if ($this->stream === null) { //直接echo輸出內(nèi)容 echo $this->content; return; } set_time_limit(0); // Reset time limit for big files $chunkSize = 8 * 1024 * 1024; // 8MB per chunk if (is_array($this->stream)) { list ($handle, $begin, $end) = $this->stream; fseek($handle, $begin); while (!feof($handle) && ($pos = ftell($handle)) <= $end) { if ($pos + $chunkSize > $end) { $chunkSize = $end - $pos + 1; } echo fread($handle, $chunkSize); flush(); // Free up memory. Otherwise large files will trigger PHP"s memory limit. } fclose($handle); } else { while (!feof($this->stream)) { echo fread($this->stream, $chunkSize); flush(); } fclose($this->stream); } }
聲明:以下內(nèi)容轉(zhuǎn)載自‘http://www.yiichina.com/doc/g...’
1.用戶向入口腳本 web/index.php 發(fā)起請(qǐng)求。
2.入口腳本加載應(yīng)用配置并創(chuàng)建一個(gè)應(yīng)用實(shí)例去處理請(qǐng)求。
3.應(yīng)用通過請(qǐng)求組件解析請(qǐng)求的路由。
4.應(yīng)用創(chuàng)建一個(gè)控制器實(shí)例去處理請(qǐng)求。
5.控制器創(chuàng)建一個(gè)操作實(shí)例并針對(duì)操作執(zhí)行過濾器。
6.如果任何一個(gè)過濾器返回失敗,則操作退出。
7.如果所有過濾器都通過,操作將被執(zhí)行。
8.操作會(huì)加載一個(gè)數(shù)據(jù)模型,或許是來(lái)自數(shù)據(jù)庫(kù)。
9.操作會(huì)渲染一個(gè)視圖,把數(shù)據(jù)模型提供給它。
10.渲染結(jié)果返回給響應(yīng)組件。
11.響應(yīng)組件發(fā)送渲染結(jié)果給用戶瀏覽器。
聲明:以下內(nèi)容轉(zhuǎn)載自"http://www.yiichina.com/doc/guide/2.0/structure-applications"
在深入研究了源碼之后,在此總結(jié)一下。當(dāng)運(yùn)行入口腳本處理請(qǐng)求時(shí), 應(yīng)用主體會(huì)經(jīng)歷以下生命周期:
入口腳本加載應(yīng)用主體配置數(shù)組。
入口腳本創(chuàng)建一個(gè)應(yīng)用主體實(shí)例:
a.調(diào)用 preInit() 配置幾個(gè)高級(jí)別應(yīng)用主體屬性, 比如 yiiaseApplication::basePath。 b.注冊(cè) yiiaseApplication::errorHandler 錯(cuò)誤處理方法. c.配置應(yīng)用主體屬性. d.調(diào)用 init() 初始化,該函數(shù)會(huì)調(diào)用 bootstrap() 運(yùn)行引導(dǎo)啟動(dòng)組件.
入口腳本調(diào)用 yiiaseApplication::run() 運(yùn)行應(yīng)用主體:
a.觸發(fā) EVENT_BEFORE_REQUEST 事件。 b.處理請(qǐng)求:解析請(qǐng)求 路由 和相關(guān)參數(shù); 創(chuàng)建路由指定的模塊、控制器和動(dòng)作對(duì)應(yīng)的類,并運(yùn)行動(dòng)作。 c.觸發(fā) EVENT_AFTER_REQUEST 事件。 d.發(fā)送響應(yīng)到終端用戶.
入口腳本接收應(yīng)用主體傳來(lái)的退出狀態(tài)并完成請(qǐng)求的處理。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/26026.html
摘要:在用戶注冊(cè)的時(shí)候是為空的,當(dāng)用戶忘記密碼在登錄頁(yè)面點(diǎn)擊后生成的,用來(lái)給用法發(fā)送郵件后重置密碼時(shí)進(jìn)行驗(yàn)證。如有錯(cuò)誤,不吝賜教。 注冊(cè) 在advanced模板中,進(jìn)入frontend/index.php?r=site%2Fsignup頁(yè)面,可以看到框架的注冊(cè)頁(yè)面showImg(https://segmentfault.com/img/bVDEaZ?w=300&h=235); 填寫完User...
get_called_class get_class call_user_func strncmp method_exists property_exists array_unshift get_class
摘要:反射簡(jiǎn)介參考官方簡(jiǎn)介的話,具有完整的反射,添加了對(duì)類接口函數(shù)方法和擴(kuò)展進(jìn)行反向工程的能力。此外,反射提供了方法來(lái)取出函數(shù)類和方法中的文檔注釋。 反射簡(jiǎn)介 參考官方簡(jiǎn)介的話,PHP 5 具有完整的反射 API,添加了對(duì)類、接口、函數(shù)、方法和擴(kuò)展進(jìn)行反向工程的能力。 此外,反射 API 提供了方法來(lái)取出函數(shù)、類和方法中的文檔注釋。 YII2框架中示例 對(duì)于yii2框架,應(yīng)該都知道di容器,...
摘要:今天我就來(lái)講講插件的使用,它是如何實(shí)現(xiàn)列表表頭自定義顯示字段的,我把我的經(jīng)驗(yàn)分享出來(lái),滿足一下不懂英語(yǔ)的人,給你們搭個(gè)快車。需求分析實(shí)現(xiàn)列表表頭自定義顯示字段,自定義表頭排序。 序言 Yii2框架的擴(kuò)展性能真的很不錯(cuò),很多效果都可以通過插件去實(shí)現(xiàn),你想不到的老外都幫你想好了,于是,人群中就流傳了這么一句話:效果不會(huì)寫不要緊,會(huì)用插件也不錯(cuò)。GitHub是一個(gè)龐大而且開放的資源庫(kù),平時(shí)有...
摘要:而這些問題目前的最好解決方案就是集成一個(gè)編輯器,鑒于大家這里不是指程序員都是喜歡所見即所得,所以,這里我主要是演示怎么集成所見即所得的富文本編輯器。 原文來(lái)自: https://jellybool.com/post/programming-with-yii2-rich-text-input-with-redactor 首先,很慚愧的是,前幾天都出去外面玩了,沒有及時(shí)更新教程,...
閱讀 1438·2023-04-25 16:31
閱讀 2040·2021-11-24 10:33
閱讀 2745·2021-09-23 11:33
閱讀 2528·2021-09-23 11:31
閱讀 2900·2021-09-08 09:45
閱讀 2336·2021-09-06 15:02
閱讀 2647·2019-08-30 14:21
閱讀 2313·2019-08-30 12:56