Zend的MVC机制使用分析(一)
代码
$front = zend_controller_front::getinstance();
zend_layout::startmvc(array('layoutpath' => usvn_layouts_dir));
$front->setrequest(new usvn_controller_request_http());
$front->throwexceptions(true);
$front->setbaseurl($config->url->base);
$router = new zend_controller_router_rewrite();
$routes_config = new usvn_config_ini(usvn_routes_config_file, usvn_config_section);
$router->addconfig($routes_config, 'routes');
$front->setrouter($router);
$front->setcontrollerdirectory(usvn_controllers_dir);
zend_controller_front::getinstance()->dispatch();
分析
首先看下zend_controller_front::getinstance是调用单例模式,实例化了它的内部属性_plugins,实例化了一个zend_controller_plugin_broker类。
这个类是管理front的插件的类。先看一个front中的方法public function registerplugin(zend_controller_plugin_abstract $plugin, $stackindex = null)
意思是如果你有一个自己的插件要插入使用的话,调用这个函数能把你自己的插件委托给zend_controller_plugin_broker使用。
如果你有愿望继续跟下去你会看到注册插件做的一件最根本的事情就是把request和response放入到你的插件中去(setrequest和setresponse)。
class zend_controller_plugin_broker extends zend_controller_plugin_abstract
这个实现了抽象类zend_controller_plugin_abstract。
zend_controller_plugin_abstract是所有插件的抽象类,所有用户自己定义的插件或者zend已有的插件都要从这个类继承。这里就看到了,前端控制器front就是使用broker作为用户插件注册。
这个抽象类可以被实现的函数有:
routestartup: 在路由发送请求前被调用
routeshutdown:在路由完成请求后被调用
dispatchloopstartup:在进入分发循环(dispatch loop)前被调用
predispatch:在动作由分发器分发前被调用
postdispatch:在动作由路由器分发后被调用
dispatchloopshutdown:在进入分发循环(dispatch loop)后被调用
我们还看到了getrequest, getresponse两个方法,我们可以通过他们分别从控制器中获取request对象和response对象
好了,扯远了,回到最开始的代码,zend_controller_front::getinstance实际上来看做的事情就是注册了一个broker插件放到$front中。
下面一行代码
zend_layout::startmvc(array('layoutpath' => usvn_layouts_dir));
看到zend/layout.php中,startmvc做了两件事:首先是调用自己的构造函数来实例化自己(切记带着initmvc参数为true),然后是设置参数。
zend_layout的构造函数比较复杂,就跟到里面看看。首先也是设置传递进来的参数$options,我们这个例子中是传递进来array ( [layoutpath] => /var/www/html/usvn/app/layouts )这个array作为options,构造函数就是调用$this->setoptions($options);
这个setoptions做的事是根据array的每个key,调用$this->set$key($val);也就是说,以上面的例子来说,setoptions调用了setlayoutpath("/var/www/html/usvn/app/layouts")
顺藤摸瓜,setlayoutpath的功能是设置自己类的this->_layout为"/var/www/html/usvn/app/layouts", 然后设置_enable为true;这两个属性记住,以后会有使用的。
回退到zend_layout的构造函数,初始化options之后是调用了_initvarcontainer();
这个函数做了这么个事情:
$this->_container = zend_view_helper_placeholder_registry::getregistry()->getcontainer(__class__);
又出现了zend_view_helper_placeholder_registry(我翻译为:zend视图助手注册表)
getregistry() 将zend_view_helper_placeholder_registry作为key,zend_view_helper_placeholder_registry类的实例作为value注册到之前见过的zend_registry中。这个类的构造函数就什么事都没有。
getregistry()返回了zend_view_helper_placeholder_registry实例,下面调用getcontainer(__class__)。 这里的__class__是什么,当前调用的类,自然就是zend_layout了。这里是getcontainer("zend_layout")
进入到getcontainer里面,它调用了createcontainer("zend_layout")。createcontainer("zend_layout")是在registry中以zend_layout为key,zend_view_helper_placeholder_container类为value的array。
zend_view_helper_placeholder_container实现抽象类zend_view_helper_placeholder_container_abstract,这个抽象类实际上也是一个arrayobject,这个在之前的文章有提到过了,是一个和泛型类一样的东东。
好了,这里不跟下去了,回头到zend_layout的构造函数
_initvarcontainer结束了,下面是调用两个重要的函数:
$this->_setmvcenabled(true);
$this->_initmvc();
mvc大家一定很熟悉,我们来看看这里是怎么个mvc的
setmvcenabled没什么特别,设置标志位this->_mvcenabled
_initmvc做了两件事,_initplugin和_inithelper。
先看initplugin:
获取pluginclass,这里的pluginclass就是zend_layout_controller_plugin_layout,可以看到,这里是作为一个插件的形式放进来的。
接着又获取了zend_controller_front的实例,调用:
$front->registerplugin(
new $pluginclass($this),
99
);
记得前面对zend_controller_front的分析不?里面有registerplugin的函数,是将插件委托给front的broker来用。有人就会问后面的99是什么意思?是插件的索引顺序,越后面的插件越后执行插件的动作。
下面再看_inithelper:
获取helperclass,这里的helperclass就是zend_layout_controller_action_helper_layout
if (!zend_controller_action_helperbroker::hashelper('layout')) {
。。。
zend_controller_action_helperbroker::getstack()->offsetset(-90, new $helperclass($this));
}
如果action_helperbroker没有layout的helper的话
就执行下面的offsetset命令。将-90和zend_layout_controller_action_helper_layout实例作为参数传入。
和plugin同样的关系,将zend_layout_controller_action_helper_layout实例作为value存入到this->_helpersbypriority和this->_helpersbynameref去了
前面的-90是权重,也是要保证这个helper是最后调用(看最后一行是krsort排序)
好了,layout的构造函数就这样分析结束了。
推荐阅读