Phalcon自己有支持建立多种形式的Web应用项目以应对不一样场景,包括迷你应用、单模块标准应用、以及较复杂的多模块应用php
Phalcon环境配置安装后,能够经过命令行生成一个标准的Phalcon多模块应用html
phalcon project eva --type modules
入口文件为public/index.php
,简化后一共5行,包含了整个Phalcon的启动流程,如下将按顺序说明git
require __DIR__ . '/../config/services.php'; $application = new Phalcon\Mvc\Application(); $application->setDI($di); require __DIR__ . '/../config/modules.php'; echo $application->handle()->getContent();
Phalcon的全部组件服务都是经过DI(依赖注入)进行组织的,这也是目前大部分主流框架所使用的方法。经过DI,能够灵活的控制框架中的服务:哪些须要启用,哪些不启用,组件的内部细节等等,所以Phalcon是一个松耦合可替换的框架,彻底能够经过DI替换MVC中任何一个组件。github
require __DIR__ . '/../config/services.php';
这个文件中默认注册了Phalcon\Mvc\Router
(路由)、Phalcon\Mvc\Url
(Url)、Phalcon\Session\Adapter\Files
(Session)三个最基本的组件。同时当MVC启动后,DI中默认注册的服务还有不少,能够经过DI获得全部当前已经注册的服务:算法
$services = $application->getDI()->getServices(); foreach($services as $key => $service) { var_dump($key); var_dump(get_class($application->getDI()->get($key))); }
打印看到Phalcon还注册了如下服务:sql
dispatcher
: Phalcon\Mvc\Dispatcher
分发服务,将路由命中的结果分发到对应的ControllermodelsManager
: Phalcon\Mvc\Model\Manager
Model管理modelsMetadata
: Phalcon\Mvc\Model\MetaData\Memory
ORM表结构response
: Phalcon\Http\Response
响应cookies
: Phalcon\Http\Response\Cookies
Cookiesrequest
: Phalcon\Http\Request
请求filter
: Phalcon\Filter
可对用户提交数据进行过滤escaper
: Phalcon\Escaper
转义工具security
: Phalcon\Security
密码Hash、防止CSRF等crypt
: Phalcon\Crypt
加密算法annotations
: Phalcon\Annotations\Adapter\Memory
注解分析flash
: Phalcon\Flash\Direct
提示信息输出flashSession
: Phalcon\Flash\Session
提示信息经过Session延迟输出tag
: Phalcon\Tag
View的经常使用Helper而每个服务均可以经过DI进行替换。接下来实例化一个标准的MVC应用,而后将咱们定义好的DI注入进去数据库
$application = new Phalcon\Mvc\Application(); c#
$application->setDI($di);api
与DI同样,Phalcon建议经过引入一个独立文件的方式注册全部须要的模块:cookie
require __DIR__ . '/../config/modules.php';
这个文件的内容以下
$application->registerModules(array(
'base' => array(
'className' => 'Cn\Liuxue\Site\Base\Module',
'path' => __DIR__ . '/../apps/base/Module.php'
),
//前台
'front' => array(
'className' => 'Cn\Liuxue\Site\Front\Module',
'path' => __DIR__ . '/../apps/www/Module.php'
),
//后台
'backend' => array(
'className' => 'Cn\Liuxue\Site\Backend\Module',
'path' => __DIR__ . '/../apps/admin/Module.php'
),
//CMS
'cms' => array(
'className' => 'Cn\Liuxue\Site\Cms\Module',
'path' => __DIR__ . '/../apps/cms/Module.php'
),
));
能够看到Phalcon所谓的模块注册,其实只是告诉框架MVC模块的引导文件Module.php
所在位置及类名是什么。
$application->handle()
是整个MVC的核心,这个函数中处理了路由、模块、分发等MVC的所有流程,处理过程当中在关键位置会经过事件驱动触发一系列application:
事件,方便外部注入逻辑,最终返回一个Phalcon\Http\Response
。整个handle
方法的过程并不复杂,下面按顺序介绍:$application->handle()application:Phalcon\Http\Responsehandle
A dependency injection object is required to access internal services
而后从DI启动EventsManager,而且经过EventsManager触发事件application:boot
接下来进入路由阶段,从DI中得到路由服务router
,将uri传入路由并调用路由的handle()
方法。
路由的handle方法负责将一个uri根据路由配置,转换为相应的Module、Controller、Action等,这一阶段接下来会检查路由是否命中了某个模块,并经过Router->getModuleName()
得到模块名。
若是模块存在,则进入模块启动阶段,不然直接进入分发阶段。
注意到了么,在Phalcon中,模块启动是后于路由的,这意味着Phalcon的模块功能比较弱,咱们没法在某个未启动的模块中注册全局服务,甚至没法简单的在当前模块中调用另外一个未启动模块。这多是Phalcon模块功能设计中最大的问题,解决方法暂时不在本文的讨论范围内,之后会另开文章介绍。
模块启动时首先会触发application:beforeStartModule
事件。事件触发后检查模块的正确性,根据modules.php
中定义的className
、path
等,将模块引导文件加载进来,并调用模块引导文件中必须存在的方法
Phalcon\Mvc\ModuleDefinitionInterface->registerAutoloaders ()
Phalcon\Mvc\ModuleDefinitionInterface->registerServices (Phalcon\DiInterface $dependencyInjector)
registerAutoloaders()
用于注册模块内的命名空间实现自动加载。registerServices ()
用于注册模块内服务,在官方示例中registerServices ()
注册并定义了view
服务以及模板的路径,而且注册了数据库链接服务db
并设置数据库的链接信息。
模块启动完成后触发 application:afterStartModule
事件,进入分发阶段
分发过程由Phalcon\Mvc\Dispatcher
(分发器)来完成,所谓分发,在Phalcon里本质上是分发器根据路由命中的结果,调用对应的Controller/Action,最终得到Action返回的结果。
分发开始前首先会准备View,虽然View理论上位于MVC的最后一环,可是若是在分发过程当中出现任何问题,一般都须要将问题显示出来,所以View必须在这个环节就提早启动。Phalcon没有准备默认的View服务,须要从外部注入,在多模块demo中,View的注入官方推荐在模块启动阶段完成的。若是是单模块应用,则能够在最开始的DI阶段注入。
若是始终没有View注入,会抛出错误
Service 'view' was not found in the dependency injection container
致使分发过程直接中断。
分发须要Dispatcher,Dispatcher一样从DI中取得。而后将router中获得的参数(NamespaceName / ModuleName / ControllerName / ActionName / Params),所有复制到Dispatcher中。
分发开始前,会调用View的start()
方法。具体能够参考View相关文档,其实Phalcon\Mvc\View->start()
就是PHP的输出缓冲函数ob_start
的一个简单封装,分发过程当中全部输出都会被暂存到缓冲区。
分发开始前还会触发事件application:beforeHandleRequest
。
正式开始分发会调用Phalcon\Mvc\Dispatcher->dispatch()
。
进入Dispatcher后会发现Dispatcher对整个分发过程进行了进一步细分,而且在分发的过程当中会按顺序触发很是多的分发事件,能够经过这些分发事件进行更加细致的流程控制。部分事件提供了可中断的机制,只要返回false
就能够跳过Dispatcher的分发过程。
因为分发中可使用Phalcon\Mvc\Dispatcher->forward()
来实现Action的复用,所以分发在内部会经过循环实现,经过检测一个全局的finished
标记来决定是否继续分发。当如下几种状况时,分发才会结束:
forward
层数达到最大(256次)分发结束后会触发application:afterHandleRequest
,接下来经过Phalcon\Mvc\Dispatcher->getReturnedValue()
取得分发过程返回的结果并进行处理。
因为Action的逻辑在框架外,Action的返回值是没法预期的,所以这里根据返回值是否实现Phalcon\Http\ResponseInterface
接口进行区分处理。
Phalcon\Http\ResponseInterface
类型此时认为返回值无效,由View本身从新调度Render过程,会触发application:viewRender
事件,同时从Dispatcher中取得ControllerName / ActionName / Params做为Phalcon\Mvc\View->render()
的入口参数。
Render完毕后调用Phalcon\Mvc\View->finish()
结束缓冲区的接收。
接下来从DI得到resonse服务,将Phalcon\Mvc\View->getContent()
得到的内容置入response。
Phalcon\Http\ResponseInterface
类型此时会将Action返回的Response做为最终的响应,不会从新构建新的Response。
经过前面的流程,不管中间经历了多少分支,最终都会汇总为惟一的响应。此时会触发application:beforeSendResponse
,并调用
Phalcon\Http\Response->sendHeaders()
Phalcon\Http\Response->sendCookies()
将http的头部信息先行发送。至此,Application->handle()
对于请求的处理过程所有结束,对外返回一个Phalcon\Http\Response
响应。
HTTP头部发送后通常把响应的内容也发送出去:
echo $application->handle()->getContent();
这就是Phalcon Framework的完整MVC流程。
分析MVC的启动流程,无疑是但愿对流程有更好的把握和控制,方法有两种:
按照上面的流程,咱们其实彻底能够本身实现$application->handle()->getContent()
这一流程,下面就是一个简单的替代方案,代码中暂时没有考虑事件的触发。
//Roter $router = $di['router']; $router->handle(); //Module handle $modules = $application->getModules(); $routeModule = $router->getModuleName(); if (isset($modules[$routeModule])) { $moduleClass = new $modules[$routeModule]['className'](); $moduleClass->registerAutoloaders(); $moduleClass->registerServices($di); } //dispatch $dispatcher = $di['dispatcher']; $dispatcher->setModuleName($router->getModuleName()); $dispatcher->setControllerName($router->getControllerName()); $dispatcher->setActionName($router->getActionName()); $dispatcher->setParams($router->getParams()); //view $view = $di['view']; $view->start(); $controller = $dispatcher->dispatch(); //Not able to call render in controller or else will repeat output $view->render( $dispatcher->getControllerName(), $dispatcher->getActionName(), $dispatcher->getParams() ); $view->finish(); $response = $di['response']; $response->setContent($view->getContent()); $response->sendHeaders(); echo $response->getContent();
为了方便查找,将整个流程整理为一个树形清单以下:
$di = new FactoryDefault();
$di['router'] = function () {}
$di['url'] = function () {}
$di['session'] = function () {}
$application = new Application();
$application->setDI($di);
$application->registerModules()
$application->handle()
application:boot
$di['router']->handle()
$moduleName = $di['router']->getModuleName()
,若是没有则从 $application->getDefaultModule
获取application:beforeStartModule
registerAutoloaders()
以及 registerServices()
application:afterStartModule
View->start()
开启缓冲区application:beforeHandleRequest
Dispatcher->dispatch()
dispatch:beforeDispatchLoop
dispatch:beforeDispatch
dispatch:beforeException
dispatch:beforeExecuteRoute
Controller->beforeExecuteRoute()
Controller->initialize()
dispatch:afterInitialize
dispatch:afterExecuteRoute
dispatch:afterDispatch
dispatch:afterDispatchLoop
$dispatcher->getReturnedValue()
application:afterHandleRequest
分发结束Phalcon\Http\ResponseInterface
类型的返回,则渲染直接结束
application:viewRender
分发结束Phalcon\Mvc\View->render()
,入口参数为Dispatcher的 ControllerName / ActionName / ParamsPhalcon\Mvc\View->finish()
结束缓冲区的接收Phalcon\Mvc\View->getContent()
经过Phalcon\Http\Response->setContent()
放入Responseapplication:beforeSendResponse
Phalcon\Http\Response->sendHeaders()
发送头部Phalcon\Http\Response->sendCookies()
发送Cookie$application->handle()
的返回值返回echo $application->handle()->getContent();
Phalcon做为C扩展型的框架,其优点就在于高性能,虽然咱们能够经过上一种方法本身实现整个启动,但更好的方式仍然是避免替换框架自己的内容,而使用事件驱动。
下面梳理了整个MVC流程中所涉及的可被监听的事件,能够根据不一样需求选择对应事件做为切入点:
application:boot
应用启动application:beforeStartModule
模块启动前application:afterStartModule
模块启动后application:beforeHandleRequest
进入分发器前dispatch:beforeDispatchLoop
分发循环开始前dispatch:beforeDispatch
单次分发开始前dispatch:beforeExecuteRoute
Action执行前dispatch:afterExecuteRoute
Action执行后dispatch:beforeNotFoundAction
找不到Actiondispatch:beforeException
抛出异常前dispatch:afterDispatch
单次分发结束dispatch:afterDispatchLoop
分发循环结束application:afterHandleRequest
分发结束application:viewRender
渲染开始前application:beforeSendResponse
最终响应发送前