2.4 页面控制器

此时,前端控制器已经实例化一个页面控制器,并且调用了它的fetch()方法。现在,页面控制器查看URI的剩余部分,初始化自己,对URI做相关的处理,然后把处理的结果交付给一个响应对象。当这一切都结束的时候,它把那个响应对象返回给前端控制器。

页面控制器是调度过程中最复杂的部分,操作上有3个不同的阶段:

  • 初始化和加载

  • 执行动作

  • 渲染结果

2.4.1 初始化和加载

构造函数__construct()最后一条命令是调用_setup() 方法;你可以在_setup()函数中放置个性化的初始化代码。这些初始化代码在加载页面控制器和接收URI信息之前执行。

当前端控制器调用页面控制器的fetch()方法时,前端控制器把当前请求的URI的剩余部分传给页面控制器(前端控制器已经去除URI的第一个路径信息元素,因为它已经被用于决定实例化哪个页面控制器)。fetch()方法做的第一件事是根据URI加载页面控制器对象的属性。

$_controller

这是从URI中获得的页面控制器的名字。页面控制器的名字是在其被实例化的时候由前控制器设置的。例如:URI 路径 /blog/read/123/foo 将会在页面控制器Vendor_App_Blog中设置$_controller = 'blog'

$_action

URI的剩余部分(前端控制器中已经去除页面控制器名字)的第一个元素。例如,URI路径/blog/read/123/foo 将设置$_action = 'read'。这些被转化为页面控制器调用actionRead()方法。

$_info

action之后URI中剩余的路径信息元素。例如,URI路径/blog/read/123/foo将会设置$_info = array('123', 'foo')。这些参数将通过call_user_func_array()函数传递给action方法,因此,你不需要直接访问它们。

$_format

它的值是URI的最后一个路径信息元素-格式扩展名,当且令当action方法能够识别这种格式。如果这种格式不能被识别,那么它会被加到$_info数组的最后一部分(你可以通过$_action_format数组设置可识别的某些特殊的格式扩展名,数组的键是action方法名字,数组的值是可被识别的格式扩展名的数组)。

例如,假设URI为/blog/read/123/foo.rss。如果有$_action_format['read'] = array('rss'),那么actionRead() 方法将能够识别rss请求,并且你会得到$_format = 'rss'。但是如果$_action_format中没有那条记录,$_format 将为空,并且actionRead()方法的参数不再是('123', 'foo'),而是('123', 'foo.rss')

$_layout

这个属性指定在两步视图过程的布局模板的名字。当$_format非空时,$_layout将被自动设为空。这就表明,正在请求的是非标准格式,如XML或RSS;这些非标准格式的请求通常是不需要页布局的。

$_query

这是由输入URI的查询项和值构成的数组。例如,查询字符串?foo=bar&baz=dib将会变成$_query = array('foo' => 'bar', 'baz' => 'dib')

2.4.2 执行动作

这里简单介绍一下执行请求的动作时发生的事情:

  • 调用_preRun()方法,在此方法中可以改变上面初始化后的某些属性。

  • 执行_preAction()hook。(下一步,当前动作可能会调用_forward()方法路转至其他动作,所以每次跳转发生时,这个hook都会执行)

  • 执行动作方法本身,使用$_action属性决定执行哪个动作,并使用储存在$_info属性中的URI参数。(这部分是动作的逻辑中心,是由开发者完成的。)例如,给定URI “/blog/read/123/foo” ,系统最终会调用Vendor_App_Blog::actionRead('123', 'foo')

  • 执行_postAction()hook。(上一步,动作可能已经调用_forward()方法跳转至其他动作,所以每次跳转发生时,这个hook都会执行)

  • 最后,调用_postRun()方法,此方法可能会改变由动作处理过的属性的结果。

2.4.3 渲染结果

这里简单介绍下渲染请求动作的结果时发生的事情:

  • _setViewObject()方法为渲染过程初始化一个Solar_View对象。为了实现这一步,该方法给视图添加模板路径栈,给视图辅助类添加类前缀栈。它还向视图中注入一些特殊的变量,如下:$controller_class$controller$action$layout$format$errors

  • 在视图对象初始化之后,渲染过程调用_preRender方法。这样的话,在渲染发生之前,你仍可以修改视图对象或页面控制器的属性。

  • 页面控制器把其所有的公共属性传递给视图对象,因此这些公共属性在模板中可用。

  • 如果已经为属性$_view设置了模板名,页面控制器调用视图对象的fetch()方法时,将使用这个模板名。处理完模板后,就返回它的输出。输出被放置在页面控制器的$_response->content 属性中。(这是两步视图的第一步)

    [Note]Note

    默认情况下,$_view属性的值和action方法名是相同的;例如,如果action名为“actionFooBar”,那么$_view的值就为“fooBar”。这也意味着模板文件名为fooBar.php

    如果指定了一个可识别的格式,那么指定的格式扩展会出现在.php之前。例如,如果$_format = 'rss',渲染过程会查找名为fooBar.rss.php的模板文件。这也就是说,你可以用同一个action方法,同样的数据产生不同的视图响应。

  • 在视图被渲染之后,如果为$_layout属性设置了布局模板名,页面控制器将重用视图对象(包含它所有的变量和辅助方法/类)渲染布局模板。这是通过设置模板路径栈为布局模板并把已存在的$_response->content属性值放置布局模板的适当位置来完成的。这个结果将覆盖$_response->content属性的值,这也是两步视图的第二步。

  • 渲染过程现在基于$_format属性的值和$_format_type数组的映射设置$_response对象的内容类型。

  • 最后,渲染过程调用_postRender()方法,这样就能够让开发者按需修改$_response对象的内容了。

2.4.4 返回响应

调度周期的页面控制器部分完成了。它返回$_response对象给前端控制器,前端控制器再把它发送给入口文件,并由入口文件打印到客户端显示。