Phalcon itself supports the creation of various forms of web application projects to deal with different scenarios, including mini-applications , single-module standard applications , and more complex multi-module applications
Create project
After the Phalcon environment configuration is installed, a standard Phalcon multi-module application can be generated through the command line
phalcon project eva --type modules
The entry file is public/index.php
, after simplification, a total of 5 lines, including the entire Phalcon startup process, the following will be explained in order
require __DIR__ . '/../config/services.php';
$application = new Phalcon\Mvc\Application(); $application->setDI($di); require __DIR__ . '/../config/modules.php'; echo $application->handle()->getContent();
DI registration stage
All of Phalcon's component services are organized through DI (dependency injection) , which is the method used by most mainstream frameworks today. Through DI, you can flexibly control the services in the framework: which ones need to be enabled, which ones are not enabled, the internal details of components, etc., so Phalcon is a loosely coupled and replaceable framework, which can completely replace any component in MVC through DI.
require __DIR__ . '/../config/services.php';
Phalcon\Mvc\Router
The three most basic components (Route), Phalcon\Mvc\Url
(Url), and Phalcon\Session\Adapter\Files
(Session) are registered by default in this file . At the same time, when MVC is started, there are still many services registered by default in DI, and all currently registered services can be obtained through DI:
$services = $application->getDI()->getServices();
foreach($services as $key => $service) { var_dump($key); var_dump(get_class($application->getDI()->get($key))); }
Printing sees that Phalcon has also registered the following services:
dispatcher
:Phalcon\Mvc\Dispatcher
Distribute service, distribute the result of route hit to the corresponding ControllermodelsManager
:Phalcon\Mvc\Model\Manager
Model managementmodelsMetadata
:Phalcon\Mvc\Model\MetaData\Memory
ORM table structureresponse
:Phalcon\Http\Response
responsecookies
:Phalcon\Http\Response\Cookies
Cookiesrequest
:Phalcon\Http\Request
requestfilter
:Phalcon\Filter
Filter the data submitted by the userescaper
:Phalcon\Escaper
escape toolsecurity
:Phalcon\Security
Password Hash, CSRF prevention, etc.crypt
:Phalcon\Crypt
encryption algorithmannotations
:Phalcon\Annotations\Adapter\Memory
Annotation Analysisflash
:Phalcon\Flash\Direct
prompt information outputflashSession
:Phalcon\Flash\Session
The prompt information is delayed output through the Sessiontag
:Phalcon\Tag
Common Helper for View
And each service can be replaced by DI. Next, instantiate a standard MVC application, and then inject our defined DI into it
$application = new Phalcon\Mvc\Application();
$application->setDI($di);
Module registration phase
Like DI, Phalcon recommends registering all required modules by introducing a separate file:
require __DIR__ . '/../config/modules.php';
The content of this file is as follows
$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'
),
));
You can see that Phalcon's so-called module registration actually just tells the framework where the bootstrap file of the MVC module Module.php
is located and what the class name is.
MVC stage
$application->handle()
是整个MVC的核心,这个函数中处理了路由、模块、分发等MVC的全部流程,处理过程中在关键位置会通过事件驱动触发一系列application:
事件,方便外部注入逻辑,最终返回一个Phalcon\Http\Response
。整个handle
方法的过程并不复杂,下面按顺序介绍:
Basic inspection
Check DI first, if there is no DI injected, an error will be thrown
A dependency injection object is required to access internal services
Then start EventsManager from DI and trigger events through EventsManagerapplication:boot
routing phase
Next, enter the routing phase, get the routing service from DI router
, pass the uri into the route and call the route's handle()
method .
The handle method of the route is responsible for converting a uri into the corresponding Module, Controller, Action, etc. according to the route configuration. In this stage, it will check whether the route hits a certain module, and obtain the module name by Router->getModuleName()
obtaining the module name.
If the module exists, enter the module startup phase, otherwise directly enter the distribution phase.
Did you notice that in Phalcon, the module startup is after the routing , which means that Phalcon's module function is relatively weak, we cannot register a global service in a module that is not started, or even simply call another module in the current module. An inactive module. This may be the biggest problem in the functional design of Phalcon modules. The solution is not within the scope of this article for the time being, and will be introduced in another article in the future.
module start
application:beforeStartModule
The event is first fired when the module starts . After the event is triggered, check the correctness of the module, load the module bootstrap file according modules.php
to the definitions in className
, path
etc., and call the methods that must exist in the module bootstrap file
Phalcon\Mvc\ModuleDefinitionInterface->registerAutoloaders ()
Phalcon\Mvc\ModuleDefinitionInterface->registerServices (Phalcon\DiInterface $dependencyInjector)
registerAutoloaders()
Used to register namespaces within modules for autoloading. registerServices ()
It is used to register the service in the module. In the official example, the service and template path are registerServices ()
registered and defined , and the database connection service is registered and the connection information of the database is set.view
db
After the module is started, the event is triggered application:afterStartModule
and the distribution stage is entered.
Dispatch
The distribution process is Phalcon\Mvc\Dispatcher
completed by the (distributor), the so-called distribution, in Phalcon essentially, the distributor calls the corresponding Controller/Action according to the result of the route hit, and finally obtains the result returned by the Action.
Before the distribution starts, the View will be prepared first. Although the View is theoretically located in the last link of MVC, if there is any problem during the distribution process, it is usually necessary to display the problem, so the View must be started in advance in this link. Phalcon does not prepare the default View service, which needs to be injected from the outside. In the multi-module demo, the official recommendation for the injection of View is done in the module startup phase. If it is a single module application, it can be injected in the very first DI stage.
If there is always no View injection, an error will be thrown
Service 'view' was not found in the dependency injection container
Causes a direct interruption of the distribution process.
Distribution requires Dispatcher, and Dispatcher is also obtained from DI. Then copy all the parameters (NamespaceName / ModuleName / ControllerName / ActionName / Params) obtained in the router to the Dispatcher.
start()
Before the distribution starts, the View's method is called . For details, please refer to the related documents of View. In fact, it Phalcon\Mvc\View->start()
is a simple encapsulation of PHP's output buffer function ob_start
. All output during the distribution process will be temporarily stored in the buffer.
Events are also fired before the distribution starts application:beforeHandleRequest
.
The official start of the distribution will be called Phalcon\Mvc\Dispatcher->dispatch()
.
Distribution processing within the Dispatcher
After entering the Dispatcher, you will find that the Dispatcher further subdivides the entire distribution process, and will trigger a lot of distribution events in sequence during the distribution process, and you can use these distribution events for more detailed process control. Some events provide an interruptible mechanism to false
skip the dispatching process of the Dispatcher as long as it returns.
Since the distribution can be used Phalcon\Mvc\Dispatcher->forward()
to achieve the reuse of Actions, the distribution will be implemented internally through a loop, and a global finished
flag will be detected to determine whether to continue the distribution. Distribution ends when:
- Controller throws exception
forward
The number of layers reaches the maximum (256 times)- All Actions are called
Rendering stage View Render
It will be triggered after the distribution is completed application:afterHandleRequest
, and then Phalcon\Mvc\Dispatcher->getReturnedValue()
the results returned by the distribution process are obtained and processed.
Since the logic of the Action is outside the framework, the return value of the Action is unpredictable, so here we Phalcon\Http\ResponseInterface
differentiate the processing according to whether the return value implements the interface.
When Action returns a non- Phalcon\Http\ResponseInterface
type
At this time, the return value is considered invalid, and the View itself reschedules the Render process, which will trigger application:viewRender
the event and obtain the ControllerName / ActionName / Params from the Dispatcher as Phalcon\Mvc\View->render()
the entry parameters.
After Render is completed, call to Phalcon\Mvc\View->finish()
end the receiving of the buffer.
Next, get the response service from DI, and put the Phalcon\Mvc\View->getContent()
obtained content into the response.
When Action returns a Phalcon\Http\ResponseInterface
type
At this time, the Response returned by the Action will be used as the final response, and a new Response will not be rebuilt.
return response
Through the previous process, no matter how many branches are experienced in the middle, it will eventually be aggregated into a unique response. This will trigger application:beforeSendResponse
and call
Phalcon\Http\Response->sendHeaders()
Phalcon\Http\Response->sendCookies()
Send the http header information first. At this point, Application->handle()
the processing of the request is all over, and a Phalcon\Http\Response
response is returned to the outside world.
send response
After the HTTP header is sent, the content of the response is generally sent:
echo $application->handle()->getContent();
This is the complete MVC flow of Phalcon Framework.
Process control
To analyze the startup process of MVC, we undoubtedly hope to have a better grasp and control of the process. There are two methods:
custom start
According to the above process, we can actually implement $application->handle()->getContent()
this process ourselves. The following is a simple alternative. The triggering of events is not considered in the code for the time being.
//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();
Process checklist
In order to facilitate the search, the entire process is organized into a tree list as follows:
- Initialize DI (config/services.php)
$di = new FactoryDefault();
- set route
$di['router'] = function () {}
- set URL
$di['url'] = function () {}
- Set Session
$di['session'] = function () {}
- set route
- Initialize Application (public/index.php)
- Instantiate App
$application = new Application();
- inject DI
$application->setDI($di);
- Registering modules (config/modules.php)
$application->registerModules()
- Instantiate App
- Start Application (ext/mvc/application.c)
$application->handle()
- Check DI
- E trigger event
application:boot
- route start
$di['router']->handle()
- Get the module name
$moduleName = $di['router']->getModuleName()
, if not$application->getDefaultModule
get it from - Module starts (if route hits)
- E trigger event
application:beforeStartModule
- Call the module initialization method (Module.php)
registerAutoloaders()
andregisterServices()
- E trigger event
application:afterStartModule
- E trigger event
- distribution
- Initialize View
- Initialize the Dispatcher and copy the parameters from the Router to the Dispatcher
- Call View to
View->start()
open the buffer - E trigger event
application:beforeHandleRequest
- Start distribution (etc/dispatcher.c)
Dispatcher->dispatch()
- E trigger event
dispatch:beforeDispatchLoop
- The loop starts a single distribution
- E trigger event
dispatch:beforeDispatch
- Obtain the complete class and method name according to the Module, Namespace, Controller, and Action carried by the Dispatcher, and trigger event E if it is not found
dispatch:beforeException
- E trigger event
dispatch:beforeExecuteRoute
- transfer
Controller->beforeExecuteRoute()
- transfer
Controller->initialize()
- E trigger event
dispatch:afterInitialize
- Call the Action method
- E trigger event
dispatch:afterExecuteRoute
- E trigger event
dispatch:afterDispatch
- E trigger event
- If there is forward() in Action, start the next distribution
- E trigger event
- E All distribution ends, triggering an event
dispatch:afterDispatchLoop
- Application gets the output after distribution
$dispatcher->getReturnedValue()
- E trigger event
application:afterHandleRequest
distribution ends
- Rendering, if Application gets the
Phalcon\Http\ResponseInterface
type of return from the distribution, the rendering ends directly- E trigger event
application:viewRender
distribution ends - Call
Phalcon\Mvc\View->render()
, the entry parameter is ControllerName / ActionName / Params of Dispatcher - Call to
Phalcon\Mvc\View->finish()
end the receive of the buffer
- E trigger event
- ready to respond
- will be
Phalcon\Mvc\View->getContent()
passedPhalcon\Http\Response->setContent()
into the Response - E trigger event
application:beforeSendResponse
- call
Phalcon\Http\Response->sendHeaders()
send header - call
Phalcon\Http\Response->sendCookies()
to send cookies - returns the prepared response as
$application->handle()
the return value of
- will be
- send response
echo $application->handle()->getContent();
MVC events
Phalcon, as a C extension framework, has the advantage of high performance. Although we can implement the entire startup by ourselves through the previous method, a better way is to avoid replacing the content of the framework itself and use event-driven.
The following is a summary of the events that can be monitored in the entire MVC process. You can choose the corresponding event as the entry point according to different needs:
- DI injection
application:boot
app launch
- routing phase
- module start
application:beforeStartModule
Before the module startsapplication:afterStartModule
After the module starts
- distribution phase
application:beforeHandleRequest
before entering the dispenser- start distribution
dispatch:beforeDispatchLoop
Before the distribution cycle beginsdispatch:beforeDispatch
Before a single distribution beginsdispatch:beforeExecuteRoute
Before the Action is executeddispatch:afterExecuteRoute
After the Action is executeddispatch:beforeNotFoundAction
Action not founddispatch:beforeException
before throwing exceptiondispatch:afterDispatch
End of single distributiondispatch:afterDispatchLoop
Distribution cycle ends
application:afterHandleRequest
distribution ends
- render stage
application:viewRender
Before rendering starts
- send response
application:beforeSendResponse
before the final response is sent