Yii 2.0.6 - 从入口到Action执行
defined('YII_DEBUG') or define('YII_DEBUG', true);
defined('YII_ENV') or define('YII_ENV', 'dev'); require(__DIR__ . '/../vendor/autoload.php'); // 注册 Composer 加载器 Yii::setAlias('@common', dirname(__DIR__)); // 注册别名
{
\yii\BaseYii::setAlias($alias, $path)
{
if (strncmp($alias, '@', 1)) {
$alias = '@' . $alias;
}
$pos = strpos($alias, '/');
$root = $pos === false ? $alias : substr($alias, 0, $pos);
if ($path !== null) {
$path = strncmp($path, '@', 1) ? rtrim($path, '\\/') : static::getAlias($path);
if (!isset(static::$aliases[$root])) {
if ($pos === false) {
static::$aliases[$root] = $path;
} else {
static::$aliases[$root] = [$alias => $path];
}
} elseif (is_string(static::$aliases[$root])) {
if ($pos === false) {
static::$aliases[$root] = $path;
} else {
static::$aliases[$root] = [
$alias => $path,
$root => static::$aliases[$root],
];
}
} else {
static::$aliases[$root][$alias] = $path;
krsort(static::$aliases[$root]);
}
} elseif (isset(static::$aliases[$root])) {
if (is_array(static::$aliases[$root])) {
unset(static::$aliases[$root][$alias]);
} elseif ($pos === false) {
unset(static::$aliases[$root]);
}
}
}
} require(__DIR__ . '/../vendor/yiisoft/yii2/Yii.php');
{
class Yii extends \yii\BaseYii
{
} spl_autoload_register(['Yii', 'autoload'], true, true); // 注册类加载器
\yii\BaseYii::autoload($className)
{
if (isset(static::$classMap[$className])) { // 查找 $classMap 映射
$classFile = static::$classMap[$className];
if ($classFile[0] === '@') {
$classFile = static::getAlias($classFile);
}
} elseif (strpos($className, '\\') !== false) {
$classFile = static::getAlias('@' . str_replace('\\', '/', $className) . '.php', false); // 通过别名获取
{
\yii\BaseYii::getAlias($alias, $throwException = true)
{
if (strncmp($alias, '@', 1)) {
// not an alias
return $alias;
} $pos = strpos($alias, '/');
$root = $pos === false ? $alias : substr($alias, 0, $pos);
if (isset(static::$aliases[$root])) {
if (is_string(static::$aliases[$root])) {
return $pos === false ? static::$aliases[$root] : static::$aliases[$root] . substr($alias, $pos);
} foreach (static::$aliases[$root] as $name => $path) {
if (strpos($alias . '/', $name . '/') === 0) {
return $path . substr($alias, strlen($name));
}
}
} if ($throwException) {
throw new InvalidParamException("Invalid path alias: $alias");
} return false;
}
}
if ($classFile === false || !is_file($classFile)) {
return;
}
} else {
return;
} include($classFile); // 加载类 if (YII_DEBUG && !class_exists($className, false) && !interface_exists($className, false) && !trait_exists($className, false)) {
throw new UnknownClassException("Unable to find '$className' in file: $classFile. Namespace missing?");
}
}
Yii::$classMap = require(__DIR__ . '/classes.php'); // 内置类
{
return [
'yii\base\Action' => YII2_PATH . '/base/Action.php',
'yii\base\ActionEvent' => YII2_PATH . '/base/ActionEvent.php',
'yii\base\ActionFilter' => YII2_PATH . '/base/ActionFilter.php',
'yii\base\Application' => YII2_PATH . '/base/Application.php',
'yii\base\ArrayAccessTrait' => YII2_PATH . '/base/ArrayAccessTrait.php',
'yii\base\Arrayable' => YII2_PATH . '/base/Arrayable.php',
];
} Yii::$container = new yii\di\Container();
} $config = array(
'id'=>'app-debugmodule',
'basePath'=>dirname(__DIR__),
'controllerNamespace'=>'debugmodule\controllers',
'defaultRoute'=>'index/index',
'params'=>[
'adminEmail' => 'admin@example.com',
'supportEmail' => 'support@example.com',
'user.passwordResetTokenExpire' => 3600,
],
'components'=>array_merge(
[
'cache' => [
'class' => 'yii\caching\FileCache',
'cachePath' => '@runtime/cache',
],
'log' => [
'class' => 'yii\log\Dispatcher',
'traceLevel' => YII_DEBUG ? 3 : 0,
'targets' => [
[
'class' => 'yii\log\FileTarget',
'levels' => ['error', 'warning'],
'enabled' => true,
'logFile'=>'@runtime/logs/app.log',
'categories' => [], // 全部记录
'except' => ['yii\db\*'], // 不记录的分类
'logVars' => [],
// 'logVars' => ['_GET', '_POST', '_FILES', '_COOKIE', '_SESSION', '_SERVER'],
],
],
],
'mailer' => [
'class' => 'yii\swiftmailer\Mailer',
'transport' => array( // 发送到互联网的传输方法
'class'=>'Swift_Transport_MailTransport',
'constructArgs'=>array(
array('class'=>'Swift_Transport_SimpleMailInvoker'),
array('class'=>'Swift_Events_SimpleEventDispatcher')
)
)
],
'security' => [
'class' => 'yii\base\Security'
],
'i18n' => [
'class' => 'yii\i18n\I18N',
'translations'=>[
'common'=>[ // 语言转换器
'class' => 'yii\i18n\PhpMessageSource',
'basePath' => '@common/messages',
],
'debugmodule'=>[
'class' => 'yii\i18n\PhpMessageSource',
'basePath' => '@debugmodule/messages',
],
'*'=>[ // 默认语言转换器
'class' => 'yii\i18n\PhpMessageSource',
'basePath' => '@common/messages',
]
]
],
// radis
'redis' => [
'class' => 'yii\redis\Connection',
'password'=>'123456',
'database'=>'database0',
],
// 队列
'queue' => [
'class' => \yii\queue\redis\Queue::class,
'redis' => 'redis',
'channel' => 'defaultqueue'
],
],
require (__DIR__ . '/components.php')
),
'modules'=>array_merge(
require (__DIR__ . '/../../common/config/modules.php'),
require (__DIR__ . '/modules.php')
),
'bootstrap' => ['log'],
'vendorPath' => dirname(dirname(__DIR__)) . '/vendor',
'language'=>'zh-CN', // 用户配置的语言
'timeZone'=>'PRC',
); /*
{
构造函数
{
预初始化配置
{
设置 basePath 路径
设置 vendorPath 路径
设置 runtimePath 路径
设置 timeZone 时区
配置 container
核心组件添加到 $config 中
} 注册错误处理器
{
异常处理器
错误处理器
崩溃处理器
} 配置 & 初始化
{
设置 $config 中的属性到 \yii\web\Application 初始化
{
注册别名
{
Yii::setAlias('@webroot', dirname($request->getScriptFile()));
Yii::setAlias('@web', $request->getBaseUrl());
}
调用组件的 bootstrap 方法
} }
} 执行
{
触发 self::EVENT_BEFORE_REQUEST 事件
解析路由
创建 Controller 对象
{
查找 \yii\web\Application->controllerMap 的配置
递归查找 模块 的配置
创建 Controller 对象
} 执行 Action
{
创建 Action 对象
{
查找 Controller 中的 actions 方法 是否存在 action 对象的映射,直接返回Action对象
反射查找action方法,创建\yii\base\InlineAction对象
} 执行 Action 对象
{
从父模块到子模块的,依次调用模块的 beforeAction 方法
触发 self::EVENT_BEFORE_ACTION 事件
识别 action 绑定的参数,调用 action 方法
触发 self::EVENT_AFTER_ACTION 事件
从子模块到父模块的,依次调用模块的 afterAction 方法
}
} 触发 self::EVENT_AFTER_REQUEST 事件
}
} */
(new yii\web\Application($config))
{
\yii\base\Application::__construct()
{
Yii::$app = $this;
static::setInstance($this);
{
if ($instance === null) {
unset(Yii::$app->loadedModules[get_called_class()]);
} else {
Yii::$app->loadedModules[get_class($instance)] = $instance;
}
} $this->state = self::STATE_BEGIN; $this->preInit($config);
{
if (!isset($config['id'])) {
throw new InvalidConfigException('The "id" configuration for the Application is required.');
}
if (isset($config['basePath'])) { // basePath 路径
$this->setBasePath($config['basePath']);
{
parent::setBasePath($path);
Yii::setAlias('@app', $this->getBasePath()); // 设置别名
}
unset($config['basePath']);
} else {
throw new InvalidConfigException('The "basePath" configuration for the Application is required.');
} if (isset($config['vendorPath'])) { // vendorPath 路径
$this->setVendorPath($config['vendorPath']);
{
// @@vendor、@vendor、
$this->_vendorPath = Yii::getAlias($path);
Yii::setAlias('@vendor', $this->_vendorPath); // 设置别名
Yii::setAlias('@bower', $this->_vendorPath . DIRECTORY_SEPARATOR . 'bower');
Yii::setAlias('@npm', $this->_vendorPath . DIRECTORY_SEPARATOR . 'npm');
}
unset($config['vendorPath']);
} else {
// set "@vendor"
$this->getVendorPath();
}
if (isset($config['runtimePath'])) { // runtimePath 路径
$this->setRuntimePath($config['runtimePath']);
{
$this->_runtimePath = Yii::getAlias($path);
Yii::setAlias('@runtime', $this->_runtimePath); // 设置别名
}
unset($config['runtimePath']);
} else {
// set "@runtime"
$this->getRuntimePath();
} if (isset($config['timeZone'])) { // timeZone 时区
$this->setTimeZone($config['timeZone']);
{
date_default_timezone_set($value);
}
unset($config['timeZone']);
} elseif (!ini_get('date.timezone')) {
$this->setTimeZone('UTC');
} if (isset($config['container'])) {
$this->setContainer($config['container']);
{
Yii::configure(Yii::$container, $config); // 配置 container
} unset($config['container']);
} // merge core components with custom components
foreach ($this->coreComponents(){
\yii\web\Application::coreComponents
{
return array_merge(parent::coreComponents(){
return [
'log' => ['class' => 'yii\log\Dispatcher'],
'view' => ['class' => 'yii\web\View'],
'formatter' => ['class' => 'yii\i18n\Formatter'],
'i18n' => ['class' => 'yii\i18n\I18N'],
'mailer' => ['class' => 'yii\swiftmailer\Mailer'],
'urlManager' => ['class' => 'yii\web\UrlManager'],
'assetManager' => ['class' => 'yii\web\AssetManager'],
'security' => ['class' => 'yii\base\Security'],
];
}, [
'request' => ['class' => 'yii\web\Request'],
'response' => ['class' => 'yii\web\Response'],
'session' => ['class' => 'yii\web\Session'],
'user' => ['class' => 'yii\web\User'],
'errorHandler' => ['class' => 'yii\web\ErrorHandler'],
]);
} } as $id => $component) { // 核心组件添加到 $config 中
if (!isset($config['components'][$id])) {
$config['components'][$id] = $component;
} elseif (is_array($config['components'][$id]) && !isset($config['components'][$id]['class'])) {
$config['components'][$id]['class'] = $component['class'];
}
}
} $this->registerErrorHandler($config); // 注册错误处理器
{
if (YII_ENABLE_ERROR_HANDLER) {
if (!isset($config['components']['errorHandler']['class'])) {
echo "Error: no errorHandler component is configured.\n";
exit(1);
}
$this->set('errorHandler', $config['components']['errorHandler']);
unset($config['components']['errorHandler']);
$this->getErrorHandler()->register();
{
\yii\base\ErrorHandler::register()
{
ini_set('display_errors', false);
set_exception_handler([$this, 'handleException']); // 异常处理器
if (defined('HHVM_VERSION')) {
set_error_handler([$this, 'handleHhvmError']);
} else {
set_error_handler([$this, 'handleError']); // 错误处理器
}
if ($this->memoryReserveSize > 0) {
$this->_memoryReserve = str_repeat('x', $this->memoryReserveSize);
}
register_shutdown_function([$this, 'handleFatalError']); // 崩溃处理器
} }
}
} Component::__construct($config);
{
if (!empty($config)) {
Yii::configure($this, $config); // 设置 $config 中的属性到 \yii\web\Application
{
foreach ($config as $name => $value) {
$object->$name = $value;
}
return $object;
}
} $this->init();
{
\yii\base\Application::init
{
$this->state = self::STATE_INIT;
$this->bootstrap();
{
\yii\web\Application::bootstrap()
{
$request = $this->getRequest();
Yii::setAlias('@webroot', dirname($request->getScriptFile()));
Yii::setAlias('@web', $request->getBaseUrl()); parent::bootstrap();
{
\yii\base\Application::bootstrap()
{
if ($this->extensions === null) {
$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);
}
}
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__);
}
}
} foreach ($this->bootstrap as $class) { // 要立即启动的组件,$this->bootstrap = ['log']
$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); // 调用组件的 bootstrap 方法
} else {
Yii::trace('Bootstrap with ' . get_class($component), __METHOD__);
}
}
}
}
}
}
}
}
}
}
}->run();{
\yii\base\Application::run()
{
try { $this->state = self::STATE_BEFORE_REQUEST;
$this->trigger(self::EVENT_BEFORE_REQUEST); // 触发事件
{
$this->ensureBehaviors();
{
if ($this->_behaviors === null) { 当 $this->_behaviors 为 null
$this->_behaviors = []; // 设置 $this->_behaviors 为空数组
foreach ($this->behaviors(){
return [];
} as $name => $behavior) {
$this->attachBehaviorInternal($name, $behavior);
{
if (!($behavior instanceof Behavior)) {
$behavior = Yii::createObject($behavior); // 创建 $behavior 对象
}
if (is_int($name)) { // 没有指定 $behavior 的 name
$behavior->attach($this); // 调用 $behavior 的 attach 方法
$this->_behaviors[] = $behavior;
} else {
if (isset($this->_behaviors[$name])) {
$this->_behaviors[$name]->detach(); // 调用 $behavior 的 detach 方法
}
$behavior->attach($this); // 调用 $behavior 的 attach 方法
{
如:$behavior = \yii\filters\RateLimiter
\yii\base\ActionFilter::attach($owner)
{
$this->owner = $owner; // $owner === \yii\web\Application
$owner->on(Controller::EVENT_BEFORE_ACTION, [$this, 'beforeFilter']); // 注册事件
{
\yii\base\Component::on($name, $handler, $data = null, $append = true)
{
$this->ensureBehaviors();
if ($append || empty($this->_events[$name])) {
$this->_events[$name][] = [$handler, $data]; // 增加事件处理器
} else {
array_unshift($this->_events[$name], [$handler, $data]);
}
}
}
}
}
$this->_behaviors[$name] = $behavior;
} return $behavior;
}
}
}
}
if (!empty($this->_events[$name])) {
if ($event === null) {
$event = new Event; // 创建 $event 对象
}
if ($event->sender === null) {
$event->sender = $this;
}
$event->handled = false;
$event->name = $name;
foreach ($this->_events[$name] as $handler) { // 事件处理器列表
$event->data = $handler[1]; // 事件处理器的参数
call_user_func($handler[0], $event); // 调用事件处理器
// stop further handling if the event is handled
if ($event->handled) {
return;
}
}
}
// invoke class-level attached handlers
Event::trigger($this, $name, $event); // 调用类级别的事件处理器
{
\yii\base\Event::trigger($class, $name, $event = null)
{
if (empty(self::$_events[$name])) {
return;
}
if ($event === null) {
$event = new static;
}
$event->handled = false;
$event->name = $name; if (is_object($class)) { // 如 $class === \yii\web\Application
if ($event->sender === null) {
$event->sender = $class;
}
$class = get_class($class);
} else {
$class = ltrim($class, '\\');
} $classes = array_merge(
[$class],
class_parents($class, true),
class_implements($class, true)
); /*
self::$_events = [
\yii\web\Application::EVENT_BEFORE_REQUEST=>[
\yii\web\Application::class=>[
[$handlerFunc0,$data0]
[$handlerFunc1,$data1]
]
]
]
*/
foreach ($classes as $class) {
if (empty(self::$_events[$name][$class])) {
continue;
} foreach (self::$_events[$name][$class] as $handler) { // 事件处理器
$event->data = $handler[1];
call_user_func($handler[0], $event);
if ($event->handled) {
return;
}
}
}
} } } $this->state = self::STATE_HANDLING_REQUEST;
$response = $this->handleRequest($this->getRequest()); // 处理请求
{
\yii\web\Application::handleRequest($request)
{
if (empty($this->catchAll)) { // 没有配置全局捕获
try {
list ($route, $params) = $request->resolve(); // 解析路由
{
$result = Yii::$app->getUrlManager()->parseRequest($this);
{
\yii\web\UrlManager::parseRequest($request)
{
if ($this->enablePrettyUrl) { // 使用伪静态
/* @var $rule UrlRule */
foreach ($this->rules as $rule) {
$result = $rule->parseRequest($this, $request);
if (YII_DEBUG) {
Yii::trace([
'rule' => method_exists($rule, '__toString') ? $rule->__toString() : get_class($rule),
'match' => $result !== false,
'parent' => null,
], __METHOD__);
}
if ($result !== false) {
return $result;
}
} if ($this->enableStrictParsing) {
return false;
} Yii::trace('No matching URL rules. Using default URL parsing logic.', __METHOD__); $suffix = (string) $this->suffix;
$pathInfo = $request->getPathInfo();
$normalized = false;
if ($this->normalizer !== false) {
$pathInfo = $this->normalizer->normalizePathInfo($pathInfo, $suffix, $normalized);
}
if ($suffix !== '' && $pathInfo !== '') {
$n = strlen($this->suffix);
if (substr_compare($pathInfo, $this->suffix, -$n, $n) === 0) {
$pathInfo = substr($pathInfo, 0, -$n);
if ($pathInfo === '') {
// suffix alone is not allowed
return false;
}
} else {
// suffix doesn't match
return false;
}
} if ($normalized) {
// pathInfo was changed by normalizer - we need also normalize route
return $this->normalizer->normalizeRoute([$pathInfo, []]);
} else {
return [$pathInfo, []];
}
} else {
Yii::trace('Pretty URL not enabled. Using default URL parsing logic.', __METHOD__);
$route = $request->getQueryParam($this->routeParam, ''); // $routeParam = 'r'; // 不使用伪静态
if (is_array($route)) {
$route = '';
} return [(string) $route, []];
}
}
} // 路由识别结束,获取Query参数
if ($result !== false) {
list ($route, $params) = $result;
if ($this->_queryParams === null) {
$_GET = $params + $_GET; // preserve numeric keys
} else {
$this->_queryParams = $params + $this->_queryParams;
}
return [$route, $this->getQueryParams()];
} throw new NotFoundHttpException(Yii::t('yii', 'Page not found.'));
}
} 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();
}
return $this->getResponse()->redirect(Url::to($url, $e->scheme), $e->statusCode);
}
} else {
$route = $this->catchAll[0];
$params = $this->catchAll;
unset($params[0]);
}
try { Yii::trace("Route requested: '$route'", __METHOD__);
$this->requestedRoute = $route; // 识别到的路由,如: $route = "/database-module/keyvalue-sample/redis-demo/foo-method"
$result = $this->runAction($route, $params); // 执行action
{
\yii\base\Module::runAction($route, $params = [])
{
$parts = $this->createController($route); // 创建 Controller 对象
{
if ($route === '') {
$route = $this->defaultRoute; // 'default'
} // double slashes or leading/ending slashes may cause substr problem
$route = trim($route, '/');
if (strpos($route, '//') !== false) {
return false;
} if (strpos($route, '/') !== false) {
list ($id, $route) = explode('/', $route, 2);
} else {
$id = $route;
$route = '';
} // module and controller map take precedence
if (isset($this->controllerMap[$id])) { // 查找 \yii\web\Application->controllerMap 的配置
$controller = Yii::createObject($this->controllerMap[$id], [$id, $this]);
return [$controller, $route];
}
$module = $this->getModule($id); // 查找 模块 的配置
{
\yii\base\Module::getModule($id, $load = true)
{
if (($pos = strpos($id, '/')) !== false) {
// sub-module
$module = $this->getModule(substr($id, 0, $pos)); // 递归查找"子模块" return $module === null ? null : $module->getModule(substr($id, $pos + 1), $load);
} if (isset($this->_modules[$id])) {
if ($this->_modules[$id] instanceof Module) {
return $this->_modules[$id];
} elseif ($load) {
Yii::trace("Loading module: $id", __METHOD__);
/* @var $module Module */
$module = Yii::createObject($this->_modules[$id], [$id, $this]); // 创建模块
{
\yii\base\Module::__construct($id, $parent = null, $config = [])
{
$this->id = $id; // 模块 Id,如:$id = 'database-module'
$this->module = $parent; // 父级别模块,如:$parent = \yii\web\Application
parent::__construct($config);
}
}
$module->setInstance($module); // 对自身的依赖
return $this->_modules[$id] = $module; // 设置模块到父级别的_modules中
}
} return null;
}
}
if ($module !== null) {
return $module->createController($route); // 递归调用 创建 Controller 对象
} if (($pos = strrpos($route, '/')) !== false) { // 最右边的"/"
$id .= '/' . substr($route, 0, $pos);
$route = substr($route, $pos + 1);
} $controller = $this->createControllerByID($id);
{
// 如: $id = 'redis-demo'
$pos = strrpos($id, '/'); // 控制器id中的"/"
if ($pos === false) {
$prefix = '';
$className = $id;
} else { // 控制器id中带有"/"
$prefix = substr($id, 0, $pos + 1); // 签字
$className = substr($id, $pos + 1);
} // 如: $className = 'redis-demo'
if (!preg_match('%^[a-z][a-z0-9\\-_]*$%', $className)) { // 只支持小写字母、数字、横杠、下划线
return null;
}
if ($prefix !== '' && !preg_match('%^[a-z0-9_/]+$%i', $prefix)) { // 只支持小写字母、数字
return null;
} $className = str_replace(' ', '', ucwords(str_replace('-', ' ', $className))) . 'Controller'; // 横杠转成驼峰
// 如: $className = 'RedisDemoController' $className = ltrim($this->controllerNamespace . '\\' . str_replace('/', '\\', $prefix) . $className, '\\'); // 拼接 Controller 的类名
if (strpos($className, '-') !== false || !class_exists($className)) {
return null;
} if (is_subclass_of($className, 'yii\base\Controller')) { // 是 yii\base\Controller 的子类
// 如: $id = 'redis-demo'
$controller = Yii::createObject($className, [$id, $this]); // 创建 Controller 对象
{
\yii\base\Controller::__construct($id, $module, $config = [])
{
$this->id = $id; // 如: $id = 'redis-demo'
$this->module = $module; // 如:$module = \yii\web\Application 或者 \debugmodule\modules\module0\Module
parent::__construct($config);
}
}
return get_class($controller) === $className ? $controller : null;
} elseif (YII_DEBUG) {
throw new InvalidConfigException("Controller class must extend from \\yii\\base\\Controller.");
}
return null;
}
if ($controller === null && $route !== '') {
$controller = $this->createControllerByID($id . '/' . $route); // controller下存在子目录
$route = '';
} return $controller === null ? false : [$controller, $route];
} if (is_array($parts)) {
/* @var $controller Controller */
// 如:$controller === 'RedisDemoController' 对象
// 如:$actionID === 'foo-method'
list($controller, $actionID) = $parts;
$oldController = Yii::$app->controller;
Yii::$app->controller = $controller;
$result = $controller->runAction($actionID, $params);
{
\yii\base\Controller::runAction($id, $params = [])
{
$action = $this->createAction($id);
{
// 如:$id === 'foo-method'
\yii\base\Controller::createAction($id)
{
if ($id === '') {
$id = $this->defaultAction; // $defaultAction = 'index';
} $actionMap = $this->actions(); // Controller 中的 actions 方法
{
return [];
} if (isset($actionMap[$id])) { // 存在 action 对象的映射
return Yii::createObject($actionMap[$id], [$id, $this]); // 创建 action 对象
} elseif (preg_match('/^[a-z0-9\\-_]+$/', $id) && strpos($id, '--') === false && trim($id, '-') === $id) {
$methodName = 'action' . str_replace(' ', '', ucwords(implode(' ', explode('-', $id)))); // 下划线转成驼峰
// 如:$methodName = 'actionFooMethod'
if (method_exists($this, $methodName)) {
$method = new \ReflectionMethod($this, $methodName);
if ($method->isPublic() && $method->getName() === $methodName) { // 公有的
return new InlineAction($id, $this, $methodName);
{
\yii\base\InlineAction::__construct($id, $controller, $actionMethod, $config = [])
{
$this->actionMethod = $actionMethod; // $actionMethod = 'actionFooMethod'
parent::__construct($id, $controller, $config);
{
\yii\base\Action::__construct($id, $controller, $config = [])
{
$this->id = $id; // 如:$id === 'foo-method'
$this->controller = $controller; // 如:$controller === Controller 对象
parent::__construct($config);
}
}
} }
}
}
} return null;
}
}
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; // call beforeAction on modules
foreach ($this->getModules(){ // 递归获取父级别的模块$module
$modules = [$this->module];
$module = $this->module; // 模块
while ($module->module !== null) { // 有父模块
array_unshift($modules, $module->module); // 父模块放到栈顶
$module = $module->module;
}
return $modules;
} as $module) {
if ($module->beforeAction($action)) { // 从父模块到子模块的,依次调用模块的 beforeAction 方法
array_unshift($modules, $module); // 父模块放到栈底
} else {
$runAction = false;
break;
}
} $result = null; if ($runAction && $this->beforeAction($action){
\yii\base\Controller::beforeAction($action)
{
$event = new ActionEvent($action);
$this->trigger(self::EVENT_BEFORE_ACTION, $event); // 触发事件
{
$this->ensureBehaviors();
{
if ($this->_behaviors === null) { 当 $this->_behaviors 为 null
$this->_behaviors = []; // 设置 $this->_behaviors 为空数组
foreach ($this->behaviors(){
return [];
} as $name => $behavior) {
$this->attachBehaviorInternal($name, $behavior);
{
if (!($behavior instanceof Behavior)) {
$behavior = Yii::createObject($behavior); // 创建 $behavior 对象
}
if (is_int($name)) { // 没有指定 $behavior 的 name
$behavior->attach($this); // 调用 $behavior 的 attach 方法
$this->_behaviors[] = $behavior;
} else {
if (isset($this->_behaviors[$name])) {
$this->_behaviors[$name]->detach(); // 调用 $behavior 的 detach 方法
}
$behavior->attach($this); // 调用 $behavior 的 attach 方法
{
如:$behavior = \yii\filters\RateLimiter
\yii\base\ActionFilter::attach($owner)
{
$this->owner = $owner; // $owner === \yii\web\Application
$owner->on(Controller::EVENT_BEFORE_ACTION, [$this, 'beforeFilter']); // 注册事件
{
\yii\base\Component::on($name, $handler, $data = null, $append = true)
{
$this->ensureBehaviors();
if ($append || empty($this->_events[$name])) {
$this->_events[$name][] = [$handler, $data]; // 增加事件处理器
} else {
array_unshift($this->_events[$name], [$handler, $data]);
}
}
}
}
}
$this->_behaviors[$name] = $behavior;
} return $behavior;
}
}
}
}
if (!empty($this->_events[$name])) {
if ($event === null) {
$event = new Event; // 创建 $event 对象
}
if ($event->sender === null) {
$event->sender = $this;
}
$event->handled = false;
$event->name = $name;
foreach ($this->_events[$name] as $handler) { // 事件处理器列表
$event->data = $handler[1]; // 事件处理器的参数
call_user_func($handler[0], $event); // 调用事件处理器
// stop further handling if the event is handled
if ($event->handled) {
return;
}
}
}
// invoke class-level attached handlers
Event::trigger($this, $name, $event); // 调用类级别的事件处理器
{
\yii\base\Event::trigger($class, $name, $event = null)
{
if (empty(self::$_events[$name])) {
return;
}
if ($event === null) {
$event = new static;
}
$event->handled = false;
$event->name = $name; if (is_object($class)) { // 如 $class === \yii\web\Application
if ($event->sender === null) {
$event->sender = $class;
}
$class = get_class($class);
} else {
$class = ltrim($class, '\\');
} $classes = array_merge(
[$class],
class_parents($class, true),
class_implements($class, true)
); /*
self::$_events = [
\yii\base\Controller::EVENT_BEFORE_ACTION=>[
\yii\base\Controller::class=>[
[$handlerFunc0,$data0]
[$handlerFunc1,$data1]
]
]
]
*/
foreach ($classes as $class) {
if (empty(self::$_events[$name][$class])) {
continue;
} foreach (self::$_events[$name][$class] as $handler) { // 事件处理器
$event->data = $handler[1];
call_user_func($handler[0], $event);
if ($event->handled) {
return;
}
}
}
} }
}
return $event->isValid;
}
}) { // 触发事件,并且返回 true // run the action
$result = $action->runWithParams($params);
{
\yii\base\InlineAction::runWithParams($params)
{
$args = $this->controller->bindActionParams($this, $params);
{
\yii\web\Controller::bindActionParams($action, $params)
{
if ($action instanceof InlineAction) { // 反射 action 参数
$method = new \ReflectionMethod($this, $action->actionMethod);
} else {
$method = new \ReflectionMethod($action, 'run');
} $args = [];
$missing = [];
$actionParams = [];
foreach ($method->getParameters() as $param) { // action 方法的参数
$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;
}
} Yii::trace('Running action: ' . get_class($this->controller) . '::' . $this->actionMethod . '()', __METHOD__);
if (Yii::$app->requestedParams === null) {
Yii::$app->requestedParams = $args;
} return call_user_func_array([$this->controller, $this->actionMethod], $args); // 调用 action 方法 }
} $result = $this->afterAction($action, $result);
{
\yii\base\Controller::afterAction($action, $result)
{
$event = new ActionEvent($action);
$event->result = $result;
$this->trigger(self::EVENT_AFTER_ACTION, $event); // 触发事件
return $event->result;
}
} // call afterAction on modules
foreach ($modules as $module) { // 从子模块到父模块的,依次调用模块的 afterAction 方法
/* @var $module Module */
$result = $module->afterAction($action, $result);
}
} if ($oldAction !== null) {
$this->action = $oldAction;
} return $result;
}
}
if ($oldController !== null) {
Yii::$app->controller = $oldController;
} return $result;
} $id = $this->getUniqueId();
throw new InvalidRouteException('Unable to resolve the request "' . ($id === '' ? $route : $id . '/' . $route) . '".');
} }
if ($result instanceof Response) { // 响应结果,action 返回的结果是对象
return $result;
} else {
$response = $this->getResponse();
if ($result !== null) {
$response->data = $result; // 响应结果,action 返回的结果是字符串
} return $response;
}
} catch (InvalidRouteException $e) {
throw new NotFoundHttpException(Yii::t('yii', 'Page not found.'), $e->getCode(), $e);
}
}
} $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);
{
if ($this->state === self::STATE_BEFORE_REQUEST || $this->state === self::STATE_HANDLING_REQUEST) {
$this->state = self::STATE_AFTER_REQUEST;
$this->trigger(self::EVENT_AFTER_REQUEST);
} if ($this->state !== self::STATE_SENDING_RESPONSE && $this->state !== self::STATE_END) {
$this->state = self::STATE_END;
$response = $response ? : $this->getResponse();
$response->send(); // 发送响应数据
} if (YII_ENV_TEST) {
throw new ExitException($status);
} else {
exit($status);
}
}
return $e->statusCode; }
}
}
Yii 2.0.6 - 从入口到Action执行的更多相关文章
- YII 1.0 隐藏单入口index.php 设置路由与伪静态
隐藏 index.php 保证apache配置文件httpd.conf里的LoadModulerewrite_module modules/mod_rewrite.so开启(去掉#)将相对应目录的Al ...
- [Yii2.0] 以Yii 2.0风格加载自定义类或命名空间 [配置使用Yii2 autoloader]
Yii 2.0最显著的特征之一就是引入了命名空间,因此对于自定义类的引入方式也同之前有所不同.这篇文章讨论一下如何利用Yii 2.0的自动加载机制,向系统中引入自定义类和命名空间.本文旨在抛砖引玉,如 ...
- 使用nginx部署Yii 2.0\yii-basic-app-2.0.5
nginx.conf #user nobody;worker_processes 1; #error_log logs/error.log;#error_log logs/error.log noti ...
- MVC4.0 利用IActionFilter实现单一Action返回多种结果
延续MVC4.0 实现单一Action返回多种结果,我们实现了在一个Action中根据前台请求方式的不同和请求内容的不同返回了多个结果,但是这种返回多个结果的业务逻辑并不通用.如果现在年纪Action ...
- Yii Framework2.0开发教程(5)数据库mysql性能
继续<Yii Framework2.0开发教程(3)数据库mysql入门> 首先给予一定的尊重yii2数据库支持引进 Yii 基于 PHP's PDO一个成熟的数据库访问层的建立.它提供了 ...
- Yii 2.0.3 Advanced版控制器不能包含大写字母的Bug
Yii 2.0.3 Advanced版控制器不能包含大写字母的Bug,我是直接下载Archive文件安装的,非Composer方式安装 Yii 框架之前是支持在Url中包含大写字母的 最新的Yii 2 ...
- 08 Zabbix4.0系统配置事件通知 - 动作Action
点击返回:自学Zabbix之路 点击返回:自学Zabbix4.0之路 点击返回:自学zabbix集锦 08 Zabbix4.0系统配置事件通知 - 动作Action 请点击查看Zabbix3.0.8版 ...
- 阻止YII 1.0自动加载内置JQUERY库
有些时候我们会在项目中用到很多js库, 因为Yii 1.0框架会默认自动加载一些自带核心库, 很容易引起冲突问题, 下面的代码就展示了如何在Yii 1.0框架下取消jQuery自动加载. Open C ...
- Yii 2.0 query模式语法
项目使用Yii 2.0版本开发,个人一直喜好使用(new \yii\db\Query())模式操作数据,把增.删.查.改这4种情况的写法整理出来,方便查阅和记忆. 增加 - insert use Yi ...
随机推荐
- 关于 C# 中接口的一些小结
< 关于 C# 中“接口”的一些小结 > 对于 C# 这样的不支持多重继承的语言,很好的体现的层次性,但是有些时候多重继承的确有一些用武之地. 比如,在 Stream 类 . 图形设备 ...
- Android学习——ViewPager的使用(二)
这一节介绍使用FragmentPagerAdapter适配器,来加载Fragment对象. 数据源 加载Fragment对象时,数据源自然来自Fragment,与View类似,依旧使用List来存放数 ...
- 使用Unicode写文本文件:一个简单类的示例
参考了http://forums.codeguru.com/showthread.php?457106-Unicode-text-file示例. class WOFSTREAM : public st ...
- 2016微软技术大会Azure相关回顾
3 天的时间稍纵即逝,伴随着本届大会压轴大奖的揭晓,2016 年度的微软技术大会完美落幕.以“数字化转型”为主题,来自微软全球的近百位顶尖技术专家.工程师和业务负责人拔冗而至,在 130 余场的专业技 ...
- Selenium2学习(四)-- xpath定位
前言 在上一篇简单的介绍了用工具查看目标元素的xpath地址,工具查看比较死板,不够灵活,有时候直接复制粘贴会定位不到.这个时候就需要自己手动的去写xpath了,这一篇详细讲解xpath的一些语法. ...
- 2.安装 Android SDK
安装Android SDK Android SDK(Software Development Kit,软件开发工具包)提供了 Android API 库和开发工具构建,测试和调试应用程序.简单来讲,A ...
- Infinity 与 NAN
System.out.println(5.0/0.0+''-"+0.0/0.0); 正确的输出结果是Infinity-NaN 1.为什么不是java.lang.ArithmeticExcep ...
- apache-实战(一)
Apache 1.html的完整格式 # vim /var/www/html/index.html<html><head><title>我要</title& ...
- Python语言程序设计基础(6)—— 组合数据类型
tuple 元组(创建后不能修改) tuple = "cat","dog","tiger","human" print( ...
- Lucas 大组合数
题目:HDU 3037 题意:有n个树,m个坚果,放到n个树里,可以不放完,有多少种方法. 分析: 得到组合数了. 大组合数什么费马小定理,Lucas定理都来了: 总的说,不能用二维地推了,用的却是组 ...