1 版本
// yii\BaseYii\getVersion
public static function getVersion()
{
return '2.0.10';
}
2 继承与实现
Controller继承与Component, 并实现了ViewContextInterface接口。
在Controller重要的有两块: action, view
3 actions
public function actions()
{
return [];
}
在自定义的XXController类中可以看见各种actionXXX函数,比如actionIndex, actionCreate
而actions用于添加额外的action函数,如果有需要,派生类可以重载它:
public function actions()
{
return [
'error' => [
'class' => 'yii\web\ErrorAction',
],
];
}
这样,只要再添加View/XX/error.php 就可以使用这个action了。
4 createAction
public function createAction($id)
{
// $id相当于 index, create, update, 就是actionIndex, actionCreate。
// 框架在使用method_exists查找的时候, 会加上action + $id, 并把$id的首字母大写
// 如果$id为空, 则使用默认值'index'
if ($id === '')
{
$id = $this->defaultAction;
}
// 优先查找额外添加的action
$actionMap = $this->actions();
if (isset($actionMap[$id]))
{
return Yii::createObject($actionMap[$id], [$id, $this]);
}
// $id 的命名规则:
// 由a~z, 0~9, \, -, _ 组成, 且不能包含--
// $id头尾都不能有-
elseif (preg_match('/^[a-z0-9\\-_]+$/', $id) && strpos($id, '--') === false && trim($id, '-') === $id)
{
// 组成函数名规则
// 1 以action开头
// 2 如果$id有用-连接, 则先用explode转为数组, 然后用implode组合到一起, 并将每个单词的首字母大写
// 3 用str_replace将implode中的' '替换为''
// 示例
// get-your-name
// explode: ['get', 'your', 'name']
// implode: get your name
// ucwords: Get Your Name
// str_replace: GetYourName
// 最终: actionGetYourName
$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就是用于把动作当成是controller函数
return new InlineAction($id, $this, $methodName);
}
}
}
return null;
}
5 getModules
该函数返回其主人,主人的主人…的组合
public function getModules()
{
$modules = [$this->module];
$module = $this->module;
// 如果主人还有主人, 则继续查找下去
while ($module->module !== null)
{
// 越古老的主人放在modules的越前面
array_unshift($modules, $module->module);
$module = $module->module;
}
return $modules;
}
6 runAction
public function runAction($id, $params = [])
{
$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;
// getModules保存的主人顺序是 越古老越前面
foreach ($this->getModules() as $module)
{
// beforeAction的返回值将会决定action是否继续执行下去
// 如果有一个主人在beforeAction中决定不执行, 则action就不再执行
if ($module->beforeAction($action))
{
// 现在将越年轻的主人放在越前面
array_unshift($modules, $module);
}
else
{
$runAction = false;
break;
}
}
$result = null;
// 所有主人都满足条件
// 判断自己的beforeAction是否满足条件
if ($runAction && $this->beforeAction($action))
{
// 运行这个action, 如果是自定义的action,一定要重载run函数
// 这里面也有一个beforeRun, afterRun
$result = $action->runWithParams($params);
// 在beforeAction中,是优先执行更古老的主人,最后轮到自己
// 在afterAction中,是优先执行自己的,然后逐步轮向最古老的主人
$result = $this->afterAction($action, $result);
foreach ($modules as $module)
{
$result = $module->afterAction($action, $result);
}
}
$this->action = $oldAction;
return $result;
}
7 run
public function run($route, $params = [])
{
$pos = strpos($route, '/');
// $route不包含'/',则是action id, 比如index, create, 最后会执行actionIndex, actionCreate
if ($pos === false)
{
return $this->runAction($route, $params);
}
// 如果'/'不在开头, 则有主人来执行,比如 site/index, site/create
elseif ($pos > 0)
{
return $this->module->runAction($route, $params);
}
// 如果'/'在开头, 比如 /site/index, /site/create, 则由app来处理了
else
{
return Yii::$app->runAction(ltrim($route, '/'), $params);
}
}
8 findLayoutFile
public function findLayoutFile($view)
{
$module = $this->module;
// 如果当前的Controller设置了布局文件, 则直接使用
if (is_string($this->layout))
{
$layout = $this->layout;
}
// 如果当前的Controller没有设置布局文件,则往上一直找
elseif ($this->layout === null)
{
while ($module !== null && $module->layout === null)
{
$module = $module->module;
}
if ($module !== null && is_string($module->layout))
{
$layout = $module->layout;
}
}
if (!isset($layout))
{
return false;
}
// 以@开头的, 会在别名系统中查找真正的布局文件
if (strncmp($layout, '@', 1) === 0)
{
$file = Yii::getAlias($layout);
}
// 以/开头的, 则在应用程序的布局文件目录下查找
// 一般位于views/layouts
// 新建项目的时候, 这里一般有一个main.php文件
elseif (strncmp($layout, '/', 1) === 0)
{
$file = Yii::$app->getLayoutPath() . DIRECTORY_SEPARATOR . substr($layout, 1);
}
// 其余情况都在本controller的布局文件目录下查找
// 比如SiteController
// 会在views/site/ 下查找
else
{
$file = $module->getLayoutPath() . DIRECTORY_SEPARATOR . $layout;
}
// 该布局文件有返回扩展名, 则返回这个文件
if (pathinfo($file, PATHINFO_EXTENSION) !== '')
{
return $file;
}
// 添加php扩展名
$path = $file . '.' . $view->defaultExtension;
if ($view->defaultExtension !== 'php' && !is_file($path))
{
$path = $file . '.php';
}
return $path;
}
9 render, renderContent
public function render($view, $params = [])
{
// 渲染视图文件
$content = $this->getView()->render($view, $params, $this);
// 渲染布局文件
return $this->renderContent($content);
}
public function renderContent($content)
{
$layoutFile = $this->findLayoutFile($this->getView());
if ($layoutFile !== false)
{
// render中, 视图渲染的结果通过content传递到布局文件中
return $this->getView()->renderFile($layoutFile, ['content' => $content], $this);
}
else
{
return $content;
}
}
10 参考
1 http://www.cnblogs.com/yiifans/p/3741634.html
以下链接中也有很多好东西:
http://www.yiifans.com/forum.php?mod=viewthread&tid=60