首页 > 编程 > PHP > 正文

Yii2.0源码分析之控制器文件分析(Controller.php)创建动作、执行动作

2020-03-22 17:23:11
字体:
来源:转载
供稿:网友
  • 先说一下Yii中的控制器是做什么用的,以及在什么地方使用.

    在Yii中,当请求一个Url的时候,首先在application中获取request信息,然后由request通过urlManager解析出route,再在Module中根据route来创建controller并处理request。
    如:http://www.yiifans.com/index.php?r=site/login。会使用SiteController里面的actionLogin动作来处理这个请求。

    Yii中总共有三种控制器类

    baseController.php 这个是下面两个的基类 consoleController.php 这个是控制台控制器 webController.php 这个是web控制器



    先看看基类baseController.php,在基类中大致可分为三个部分

      和action相关的功能 和render相关的功能 其它功能


      1、和action相关的函数

      我们按照这些函数的调用顺序来一一说明

      执行路由:html' target='_blank'>public function run($route, $params = [])

      /** route值即可以为当前controller中的action id,* * 也可为module id/controller id/action id/这种格式* 如果以“/”开头,将于application来处理,否则,用控制器所属模块来处理*/public function run($route, $params = []){        //先判断route中有没有“/”        $pos = strpos($route, '/');        if ($pos === false) {                //如果没有“/”,则为action id,直接调用runAction来执行这个action。如:index            return $this->runAction($route, $params);        } elseif ($pos > 0) {                //如果“/”在中间,由当前的模块来处理这个route。如:test/index            return $this->module->runAction($route, $params);        } else {                //如果以“/”开头,则用当前的应用程序来处理这个route。如:/test/index;            return Yii::$app->runAction(ltrim($route, '/'), $params);        }}

      执行动作:public function runAction($id, $params = [])

      /** $id 为action的id,如定义的actionIndex,那么id就为Index。* */public function runAction($id, $params = []){        //创建action        $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;        /*         * 获取当前控制器的所以的模块,并执行每个模块的beforeAction来检查当前的action是否可以执行,         * 注意:getModules返回的数组顺序为:从父模块到子模块,         * 所以在执行beforeAction的时候,先检查最外层的父模块,然后检查子模块。         *          * 然而在执行afterAction的时候,顺序就反过来了,先执行子模块,最后执行父模块。         *          */        foreach ($this->getModules() as $module) {            if ($module->beforeAction($action)) {                array_unshift($modules, $module);            } else {                $runAction = false;                break;            }        }        $result = null;        //如果所以的父模块都满足执行的条件        if ($runAction) {                /*                 * 再判断当前控制器中是beforeAction,                 * 最后由生成的action对象来执行runWithParams方法                 *                  * 执行完后,再执行afterAction方法                 */            if ($this->beforeAction($action)) {                $result = $action->runWithParams($params);                $result = $this->afterAction($action, $result);            }        }        //执行所有父模块的afterAction        foreach ($modules as $module) {            /** @var Module $module */            $result = $module->afterAction($action, $result);        }        $this->action = $oldAction;        return $result;}

      创建动作 public function createAction($id)

      //由action id来创建action对象public function createAction($id){        //使用默认的action id ,默认值为:index        if ($id === '') {            $id = $this->defaultAction;        }        $actionMap = $this->actions();        if (isset($actionMap[$id])) {                //如果在actions方法中指定了独立的动作,则直接使用此动作。            return Yii::createObject($actionMap[$id], [$id, $this]);        } elseif (preg_match('/^[a-z0-9/-_]+$/', $id) && strpos($id, '--') === false && trim($id, '-') === $id) {                /*                 * action id由:a到z、0到9、、-、_ 这五种字符组成,                 * 并且不能包含“--”                 * 并且不能以“-”为开头或结尾                 *                  * 先以“-”把id分隔为数组,再以“ ”连接到字符串,把每个单词首字母大写,最后把“ ”去掉,并和'action'连接                 * 如;                 * 1、new-post-v-4                 * 2、['new','post','v','4']                 * 3、new post v 4                 * 4、New Post V 4                 * 5、NewPostV4                 * 6、actionNewPostV4                 */            $methodName = 'action' . str_replace(' ', '', ucwords(implode(' ', explode('-', $id))));            if (method_exists($this, $methodName)) {                    /*                     * 如果当前控制器中存在这个actionXXX方法,                     * 再通过反射生成方法,再次检查一遍,最后生成InlineAction                     */                $method = new ReflectionMethod($this, $methodName);                if ($method->getName() === $methodName) {                    return new InlineAction($id, $this, $methodName);                }            }        }        return null;}

      所以,如果一个动作在定义的时候是用骆驼格式名称的,如actionNewArticle,那么写url的时候r=site/new-article。详情见Yii2.0中文开发向导——控制器(Controller)中的路由部分。

      定义独立动作的数组:public function actions()

      /** 独立action定义* 这个用来指定独立的action,返回格式为name-value的数组,name为action的id,value为action类的实现,如:* return [*     'action1' => 'appcomponentsAction1',*     'action2' => [*         'class' => 'appcomponentsAction2',*         'property1' => 'value1',*         'property2' => 'value2',*     ],* ];* 这个主要是用于在子类中重写*/public function actions(){        return [];}

      由createAction可知,当controller在创建action的时候,会根据动作ID先在这个数组里面查找,如果找到则返回这个动作。所以这里定义的动作的优先级要大于在控制器里面定义的actionXXX函数。

      绑定动作的参数:public function bindActionParams($action, $params)

      /** 绑定action的参数。* 比如定义了动作 actionCrate($id,$name=null)* 那个这个函数的作用就是从params(一般为$_GET)中提取$id,$name,* * 具体的实现在webController.php和consoleController.php中*/public function bindActionParams($action, $params){        return [];}

      beforeAction、afterAction,事件触发

      //在具体的动作执行之前会先执行beforeAction,如果返回false,则动作将不会被执行,//后面的afterAction也不会执行(但父模块跌afterAction会执行)public function beforeAction($action){        $event = new ActionEvent($action);        $this->trigger(self::EVENT_BEFORE_ACTION, $event);        return $event->isValid;}//当前动作执行之后,执行afterActionpublic function afterAction($action, $result){        $event = new ActionEvent($action);        $event->result = $result;        $this->trigger(self::EVENT_AFTER_ACTION, $event);        return $event->result;}

      在这个都会触发事件,beforeAction触发EVENT_BEFORE_ACTION事件,afterAction触发EVENT_AFTER_ACTION

      2、和render相关的功能

      关于视图的可以参考Yii2.0中文开发向导——视图(View)
      另外对于视图文件是怎么查找的请参考:Yii2.0源码分析之——视图文件分析(View.php)

      获取、设置view组件:public function getView()、public function setView($view)

      //获取view组件,public function getView(){        if ($this->_view === null) {            $this->_view = Yii::$app->getView();        }        return $this->_view;}//设置view组件public function setView($view){        $this->_view = $view;}

      渲染视图文件和布局文件(如果有布局的话):public function render($view, $params = [])

      //渲染视图文件和布局文件(如果有布局的话)public function render($view, $params = []){        //由view对象渲染视图文件        $output = $this->getView()->render($view, $params, $this);        //查找布局文件        $layoutFile = $this->findLayoutFile($this->getView());        if ($layoutFile !== false) {                //由view对象渲染布局文件,                //并把上面的视图结果作为content变量传递到布局中,所以布局中才会有$content变量来表示            return $this->getView()->renderFile($layoutFile, ['content' => $output], $this);        } else {            return $output;        }}

      渲染视图文件,不会应用布局:public function renderPartial($view, $params = [])

      //这个只渲染视图文件,不会应用布局public function renderPartial($view, $params = []){        return $this->getView()->render($view, $params, $this);}

      渲染文件:public function renderFile($file, $params = [])

      //这个就是用来渲染一个文件,$file为文件实路径或别名路径public function renderFile($file, $params = []){        return $this->getView()->renderFile($file, $params, $this);}

      获取这个控制器对应的view的文件路径:public function getViewPath()

      //获取这个控制器对应的view的文件路径,如@app/views/site/xxxx.phppublic function getViewPath(){        return $this->module->getViewPath() . DIRECTORY_SEPARATOR . $this->id;}

      查找布局文件:protected function findLayoutFile($view)

      //查找布局文件protected function findLayoutFile($view){        $module = $this->module;        //如果当前控制器设置了布局文件,则直接使用所设置的布局文件        if (is_string($this->layout)) {            $layout = $this->layout;        } elseif ($this->layout === null) {                //如果没有设置布局文件,则查找所有的父模块的布局文件。            while ($module !== null && $module->layout === null) {                $module = $module->module;            }            if ($module !== null && is_string($module->layout)) {                $layout = $module->layout;            }        }        //如果没有设置布局文件,返回false        if (!isset($layout)) {            return false;        }        /*         * 布局文件有三种路径写法         * 1、以“@”开头,这种会在别名路径中查找布局文件         * 2、以“/”开头,这个会从应用程序的布局文件目录下面查找布局文件         * 3、其它情况,   这个会从当前模块的布局文件目录下查查找布局文件         */        if (strncmp($layout, '@', 1) === 0) {            $file = Yii::getAlias($layout);        } elseif (strncmp($layout, '/', 1) === 0) {            $file = Yii::$app->getLayoutPath() . DIRECTORY_SEPARATOR . substr($layout, 1);        } else {            $file = $module->getLayoutPath() . DIRECTORY_SEPARATOR . $layout;        }        //如果布局文件有文件扩展名,返回        if (pathinfo($file, PATHINFO_EXTENSION) !== '') {            return $file;        }        //加上默认的文件扩展名。        $path = $file . '.' . $view->defaultExtension;        //如果文件不存在,并且,默认的文件扩展名也不是php,则给加上php作为扩展名。        if ($view->defaultExtension !== 'php' && !is_file($path)) {            $path = $file . '.php';        }        return $path;}

      3、其它功能

      获取当前控制器所有的父模块:public function getModules()

      //获取当前控制器所有的父模块public function getModules(){        $modules = [$this->module];        $module = $this->module;        while ($module->module !== null) {                //由这里可知,返回的数组顺序为从父模块到子模块            array_unshift($modules, $module->module);            $module = $module->module;        }        return $modules;}

      获取控制器id:public function getUniqueId()

      //返回控制器idpublic function getUniqueId(){        //如果当前所属模块为application,则就为该id,否则要前面要加上模块id        return $this->module instanceof Application ? $this->id : $this->module->getUniqueId() . '/' . $this->id;}

      获取路由信息:public function getRoute()

      //获取路由信息public function getRoute(){        return $this->action !== null ? $this->action->getUniqueId() : $this->getUniqueId();}

      另外还有几个变量和2个事件

      //在执行beforeAction方法时触发的事件,//如果对事件的isValid属性设置为false,将取消action的执行const EVENT_BEFORE_ACTION = 'beforeAction';//在执行afterAction方法是触发的事件const EVENT_AFTER_ACTION = 'afterAction';//控制器idpublic $id;//所属模块public $module;//控制器中默认动作public $defaultAction = 'index';//布局文件,如果设置为false,则不使用布局文件public $layout;//当前下面执行的action,可在事件中根据这个action来执行不同的操作public $action;//视图对象private $_view;

      原文链接:http://www.yiifans.com/forum.php?mod=viewthread&tid=45

      PHP编程

      郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。

  • 发表评论 共有条评论
    用户名: 密码:
    验证码: 匿名发表