Phalcon框架启动流程(部分源码)分析
创建项目
A、通过命令行生成一个标准的Phalcon多模块应用
phalcon project demo modules(phalcon project store (micro-微型, simple - 单应用, modules-多应用 ) - 默认是单应用文档结构)
B、通过手工的方式创建
入口文件为public/index.php,简化后一共7行,包含了整个Phalcon的启动流程,以下将按顺序说明
use Phalcon\Mvc\Application;
$config = include APP_PATH . "/apps/frontend/config/config.php";
require __DIR__ . '/../config/services.php';
$application = new Application($di);
require __DIR__ . '/../config/modules.php';
require __DIR__ . '/../config/routes.php';
echo $application->handle()->getContent();
DI注册阶段
Phalcon的所有组件服务都是通过DI(依赖注入)进行组织的,这也是目前大部分主流框架所使用的方法。通过DI,可以灵活的控制框架中的服务:哪些需要启用,哪些不启用,组件的内部细节等等,因此Phalcon是一个松耦合可替换的框架,完全可以通过DI替换MVC中任何一个组件。
require __DIR__ . '/../config/services.php';
这个文件中默认注册了Phalcon\Mvc\Router(路由)、Phalcon\Mvc\Url(Url)、Phalcon\Mvc\Model\MetaData\Memory(ORM表结构)、Phalcon\Session\Adapter\Files(Session)三个最基本的组件。同时当MVC启动后,DI中默认注册的服务还有很多,可以通过DI得到所有当前已经注册的服务:
$services = $application->getDI()->getServices();
foreach ($services as $key => $value) {
var_dump($key);
var_dump(get_class($application->getDI()->get($key)));
}
打印看到Phalcon还注册了以下服务:
Router:Phalcon\Mvc\Router 路由
Dispatcher:Phalcon\Mvc\Dispatcher 分发服务,把路由命中的结果分发到对应的controller
Url:Phalcon\Mvc\Url
modelsManager:Phalcon\Mvc\Model\Manager Model管理器
modelsMetadata:Phalcon\Mvc\Model\MetaData\Memory ORM表结构
Response:Phalcon\Http\Response 响应
Cookies:Phalcon\Http\Response\Cookies Cookies管理
Request: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
Session:Phalcon\Session\Adapter\Files session
每个服务都可以通过DI进行替换。接着来实例化一个标准的MVC应用,然后我们把定义好DI注册进去:
$application = new Phalcon\Mvc\Application();
$application->setDI($di);
模块注册阶段
与DI一样,phalcon 可以通过引入一个独立的文件实现模块的注册:
require __DIR__ . '/../config/modules.php';
文件内容如下:
$application->registerModules(array(
'frontend' => array(
'className' => 'Phalconsmvc\Frontend\Module',
'path' => __DIR__ . '/../apps/frontend/Module.php'
)
));
可以看到phalcon的模块注册,其实是告诉框架MVC的模块引导文件Module.php文件所在位置及类名是什么。
MVC阶段
$application->handle()是整个框架MVC的核心,这个函数负责处理模块、控制器、分发等MVC的流程。具体的执行流程如下:
基础检查阶段:
首先检查依赖对象服务DI是否注册,如果没有注册DI则抛出异常
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()获得模块名称。
如果模块存在,则进入模块启动阶段,如果模块不存在则直接进入路由分发阶段。可见路由的启动是先于模块的启动的。
模块启动:
在模块启动时会先调用application:beforeStartModule方法,根据modules.php的配置检查模块的正确性、名称、路径是否存在等,并把模块引导文件引进来。把模块内的action、service注册为自动加载。
moduleObject->registerAutoloaders(dependencyInjector);
moduleObject->registerServices(dependencyInjector);
registerAutoloaders()用于注册模块内的命名空间实现自动加载;registerServices ()用于注册模块内服务。
路由dispatcher分发阶段:
分发阶段由Phalcon\Mvc\Dispatcher(分发器)来完成,分发器跟进路由命中的结果调用相应的controller/action,最终获得action返回的结果。
在分发前会准备view,理论上MVC流程中view是最后一环节,但是分发过程中出现任何问题都需要显示出来,因此view层必须在这个环节提前启动。
分发需要dispatcher服务,可以从DI服务获得,dispatcher根据router传过来的参数设置模块名称、命名空间、控制器、动作、参数,分发启动开始前会调用view->start()启动缓存,分发过程中所有输出都会被暂存到缓冲区。
分发时会先调用application:beforeHandleRequest方法检查dispatcher是否已经注册,然后调用dispatcher->dispatch()进行分发作业。
View渲染阶段
分发结束后通过Phalcon\Mvc\Dispatcher->getReturnedValue()取得分发过程返回的结果并进行处理。
由于action的逻辑在框架外,action的返回结构是无法预知的,因此这里跟进返回结构决定是否调用ResponseInterface接口进行区分处理。当action返回非returnedResponse对象的时候,view自己重新调度Render过程,会触发application:viewRender事件,通过dispatcher获得controllerName、actionName、params参数作为Phalcon\Mvc\View->render()的入口参数。
Render过程结束后,调用Phalcon\Mvc\View->finish()刷新缓冲区数据。
接下来从DI获取response服务,将Phalcon\Mvc\View->getContent()获得的内容置入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流程。