A deeper understanding of the request life cycle will lift the veil on the main application mechanisms and offers you the possibility to handle requests more flexibly. This recipe demonstrates how each request usually flows within the framework.
To complete this recipe, you need to have a web server installed and, what is more, you must make the base configuration of your application. Usually, by base configuration, we mean that you have your application deployed and have the services configured in all the ways available. You should understand the main class autoloading principles in Phalcon and know how to configure Phalcon Loader. Besides, you need to configure logging in PHP into a readable file.
Follow these steps to complete this recipe:
Open the application service configuration and create an event manager, which we will use during further steps:
$em = new Phalcon\Events\Manager();
Then, add the following routing setting:
$di->setShared('router', function () use($em) { $router = new \Phalcon\Mvc\Router(); $router->add( '/:controller/:action/:params', [ 'controller' => 1, 'action' => 2, 'params' => 3 ] ); $em->attach('router', new RouterListener); $router->setEventsManager($em); return $router; });
Now we need the router event listener. Create a directory with the name
library
in your project root directory. Add that directory into the Phalcon autoloader and create a class namedRouterListener
in it, inserting the following content:<?php use Phalcon\Mvc\Router; use Phalcon\Mvc\Router\Route; use Phalcon\Events\Event; use Phalcon\Di; class RouterListener { public function beforeCheckRoute(Event $event, Router $router) { $request = Di::getDefault()->getShared('request'); error_log('[REQUEST] ' . $request->getMethod() . ": " . $request->getURI()); } public function matchedRoute(Event $event, Router $router, Route $route) { error_log('[ROUTE] matched pattern: ' . $route->getPattern()); }
Create a
ProductsController
in theapp/controllers
file and place the following code there:<?php class ProductsController extends Phalcon\Mvc\Controller { public function viewAction($id) { $this->tag->setTitle('View Product'); $this->view->setVars([ 'product_id' => $id, 'product_name' => 'Pizza', 'product_price' => 12, ]); } }
Then, create a view in
app/views/products/view.volt
and place the following code there:{{ content() }} <h1>{{ product_name }}</h1> <p> <strong>ID:</strong> {{ product_id }}<br> <strong>Price:</strong> <span style="text-decoration: line-through">{{ product_price / 0.25 }}</span> <strong style="color:darkred">{{ product_price }}</strong> </p>
Add an initial dispatcher setting into the service configuration, as follows:
$di->setShared('dispatcher', function() use ($em) { $dispatcher = new Phalcon\Mvc\Dispatcher; $em->attach('dispatch', new ActionListener); $dispatcher->setEventsManager($em); return $dispatcher; });
Then, we have to create the Event Listener. For this step, create an
ActionListener
class in thelibrary
directory and put the following content into this class:<?php use Phalcon\Events\Event; use Phalcon\Mvc\Dispatcher; class ActionListener { public function afterExecuteRoute(Event $event, Dispatcher $dispatcher) { $report = [ $dispatcher->getControllerName() => $dispatcher->getControllerClass(), $dispatcher->getActionName() => $dispatcher->getActiveMethod(), 'params' => $dispatcher->getParams() ]; error_log('[ACTION] ' . json_encode($report)); } }
Open the application services definition and add a view service into the DI container:
$di->setShared('view', function () use ($config, $em) { $view = new \Phalcon\Mvc\View(); $view->setViewsDir($config->get('application')->viewsDir); $view->registerEngines([ '.volt' => function ($view, $di) use ($config) { $volt = new \Phalcon\Mvc\View\Engine\Volt($view, $di); return $volt; } ]); $em->attach('view', new ViewListener()); $view->setEventsManager($em); return $view; });
Finally, create the listener named
ViewListener
in thelibrary
directory and put the following code into it:<?php use Phalcon\Events\Event; use Phalcon\Mvc\View; class ViewListener { public function afterRender(Event $event, View $view) { error_log('[VIEW] ' . $view->getActiveRenderPath()); return true; } }
If you've done it all correctly, after you go to http://your_site/products/view/1 in your browser, you will see the product card. Note that we use the hostname your_site
in our example; however, you should use your real project hostname instead.
Additionally, if you look at the content of your php-log
file, you will see something like this:
[30-Sep-2015 02:06:48 Europe/Berlin] [REQUEST] GET: /products/view/1 [30-Sep-2015 02:06:48 Europe/Berlin] [ROUTE] matched pattern: /:controller/:action/:params [30-Sep-2015 02:06:48 Europe/Berlin] [ACTION] {"products":"ProductsController","view":"viewAction","params":["1"]} [30-Sep-2015 02:06:48 Europe/Berlin] [VIEW] /var/www/your_site/app/views/index.volt
A request life cycle starts with an entry point (for example, index.php
). All requests are directed to it by the web server (such as Apache, Nginx, and others). The entry point usually doesn't contain much code; it defines only the application object and delegates the control to the last one. The application instance, in turn, uses the Dependency Injection Container (DIC) and thereby calls different application components one after the other, delegating them the control and if necessary the request context or its current handling result.
The default request life cycle consists of the following stages:
An HTTP Request is handled by the dispatcher and routed to the controller by means of the router pattern.
The controller performs the action defined in it and passes data to the View.
The View transforms and/or formats the data as appropriate and provides it in a format that is required for the HTTP Response.
There are many ways to change the default request handling logic, including:
Multi module applications
Halting view rendering
Non-use of controllers (for example, in a RESTful application)
Working directly with HTTP requests or routers with the immediate return from anonymous functions bound with the defined URIs and even throwing an exception in any of these steps
However, the default request life cycle stages just listed outline the concept of the three main places where we can begin our research.
To demonstrate, we create an event manager, and in it register the listeners of the events we are interested in:
RouterListener
ActionListener
ViewListener
In each listener
method, we log the current request state, that is, which line of the URL request is handled by the Request
component, which router pattern matches for the address handling, which controller and action are selected by the dispatcher for the request handling, and which view is involved eventually.
Note that we define the routing pattern clearly. We couldn't define the pattern for the handling of the request /products/view/1
in this recipe, because we've created a routing component with default settings:
$router = new \Phalcon\Mvc\Router();
Try to comment the adding of your pattern into the routing component and refresh the page. Then, you'll see a matched pattern log entry, like this:
[ROUTE] matched pattern: #^/([\w0-9\_\-]+)/([\w0-9\.\_]+)(/.*)*$#u
If you take a detailed look at the request Uniform Resource Identifier (URI) and refer it to this regular expression, it will fall in place that those are a perfect match. But we recommend you to define the pattern clearly and not rely on the default one, since it helps with performance and avoids undesired behaviors.