Micro Application


    Phalcon offers a very ‘thin’ application, so that you can create applications with minimal PHP code and overhead. Micro applications are suitable for small applications that will have very low overhead. Such applications are usually API ones, prototypes etc.

    The Phalcon\Mvc\Micro class is the one responsible for creating a Micro application.

    1. <?php
    2. use Phalcon\Mvc\Micro;
    3. $app = new Micro();

    Routing

    Defining routes in a Phalcon\Mvc\Micro application is very easy. Routes are defined as follows:

    1. Application -> (method/verb) -> (route url/regex, callable PHP function)

    Routing is handled by the object. [Info]

    Usually, the starting route in an application is the route /, and in most cases it is accessed via the GET HTTP method:

    1. <?php
    2. // This is the start route
    3. $app->get(
    4. '/',
    5. function () {
    6. echo '<h1>Welcome!</h1>';
    7. }
    8. );

    Application object

    Routes can be set using the Phalcon\Mvc\Micro application object as follows:

    1. use Phalcon\Mvc\Micro;
    2. $app = new Micro();
    3. // Matches a GET request
    4. $app->get(
    5. '/orders/display/{name}',
    6. function ($name) {
    7. echo "<h1>This is order: {$name}!</h1>";
    8. }
    9. );

    Router object

    You can also create a Phalcon\Mvc\Router object, setting the routes there and then injecting it in the dependency injection container.

    1. use Phalcon\Mvc\Micro;
    2. use Phalcon\Mvc\Router;
    3. $router = new Router();
    4. $router->addGet(
    5. '/orders/display/{name}',
    6. 'OrdersClass::display'
    7. );
    8. $app = new Micro();
    9. $app->setService('router', $router, true);

    Setting up your routes using the applications verb methods (get, post, etc.) is much easier than setting up a router object with relevant routes and then injecting it in the application.

    Each method has its advantages and disadvantages. It all depends on the design and needs of your application.

    Rewrite Rules

    In order for routes to work, certain configuration changes need to be made in your web server’s configuration for your particular site.

    Those changes are outlined in the .

    Handlers

    Handlers are callable pieces of code that get attached to a route. When the route is matched, the handler is executed with all the defined parameters. A handler is any callable piece of code that exists in PHP.

    Definitions

    Phalcon offers several ways to attach a handler to a route. Your application needs and design as well as coding style will be the factors influencing your choice of implementation.

    Anonymous Function

    Finally we can use an anonymous function (as seen above) to handle the request

    1. $app->get(
    2. '/orders/display/{name}',
    3. function ($name) {
    4. echo "<h1>This is order: {$name}!</h1>";
    5. }
    6. );

    Accessing the $app object inside the anonymous function can be achieved by injecting it as follows:

    1. $app->get(
    2. '/orders/display/{name}',
    3. function ($name) use ($app) {
    4. $content = "<h1>This is order: {$name}!</h1>";
    5. $app->response->setContent($content);
    6. $app->response->send();
    7. }
    8. );
    Function

    We can define a function as our handler and attach it to a specific route.

    1. // With a function
    2. function order_display($name) {
    3. echo "<h1>This is order: {$name}!</h1>";
    4. }
    5. $app->get(
    6. '/orders/display/{name}',
    7. 'orders_display'
    8. );
    Static Method

    We can also use a static method as our handler as follows:

    1. class OrdersClass
    2. {
    3. public static function display($name) {
    4. echo "<h1>This is order: {$name}!</h1>";
    5. }
    6. }
    7. $app->get(
    8. '/orders/display/{name}',
    9. 'OrdersClass::display'
    10. );
    Method in an Object

    We can also use a method in an object:

    1. class OrdersClass
    2. {
    3. public function display($name) {
    4. echo "<h1>This is order: {$name}!</h1>";
    5. }
    6. }
    7. $orders = new OrdersClass();
    8. $app->get(
    9. '/orders/display/{name}',
    10. [
    11. $orders,
    12. 'display',
    13. ]
    14. );
    Controllers

    With the you can create micro or medium applications. Medium applications use the micro architecture but expand on it to utilize more than the Micro but less than the Full application.

    In medium applications you can organize handlers in controllers.

    1. <?php
    2. use Phalcon\Mvc\Micro\Collection as MicroCollection;
    3. $orders = new MicroCollection();
    4. // Set the main handler. ie. a controller instance
    5. $orders->setHandler(new OrdersController());
    6. // Set a common prefix for all routes
    7. $orders->setPrefix('/orders');
    8. // Use the method 'index' in OrdersController
    9. $orders->get('/', 'index');
    10. // Use the method 'show' in OrdersController
    11. $orders->get('/display/{slug}', 'show');
    12. $app->mount($orders);

    The OrdersController might look like this:

    1. <?php
    2. use Phalcon\Mvc\Controller;
    3. class OrdersController extends Controller
    4. {
    5. public function index()
    6. {
    7. // ...
    8. }
    9. public function show($name)
    10. {
    11. // ...
    12. }
    13. }

    Since our controllers extend the Phalcon\Mvc\Controller, all the dependency injection services are available with their respective registration names. For example:

    1. <?php
    2. use Phalcon\Mvc\Controller;
    3. class OrdersController extends Controller
    4. {
    5. public function index()
    6. {
    7. // ...
    8. }
    9. public function show($name)
    10. {
    11. $content = "<h1>This is order: {$name}!</h1>";
    12. $this->response->setContent($content);
    13. return $this->response;
    14. }
    15. }

    Lazy Loading

    In order to increase performance, you might consider implementing lazy loading for your controllers (handlers). The controller will be loaded only if the relevant route is matched.

    Lazy loading can be easily achieved when setting your handler in your Phalcon\Mvc\Micro\Collection:

    1. $orders->setHandler(\OrdersController::class, true);
    2. $orders->setHandler(\Blog\Controllers\OrdersController::class, true);
    Use case

    We are developing an API for an online store. The endpoints are /users, /orders and /products. Each of those endpoints are registered using handlers, and each handler is a controller with relevant actions.

    The controllers that we use as handlers are as follows:

    1. <?php
    2. use Phalcon\Mvc\Controller;
    3. class UsersController extends Controller
    4. {
    5. public function get($id)
    6. {
    7. // ...
    8. }
    9. public function add($payload)
    10. {
    11. // ...
    12. }
    13. }
    14. class OrdersController extends Controller
    15. {
    16. public function get($id)
    17. {
    18. // ...
    19. }
    20. public function add($payload)
    21. {
    22. // ...
    23. }
    24. }
    25. class ProductsController extends Controller
    26. {
    27. public function get($id)
    28. {
    29. // ...
    30. }
    31. public function add($payload)
    32. {
    33. // ...
    34. }
    35. }

    We register the handlers:

    1. <?php
    2. use Phalcon\Mvc\Micro\Collection as MicroCollection;
    3. // Users handler
    4. $users = new MicroCollection();
    5. $users->setHandler(
    6. new UsersController()
    7. );
    8. $users->setPrefix('/users');
    9. $users->get('/get/{id}', 'get');
    10. $users->get('/add/{payload}', 'add');
    11. $app->mount($users);
    12. // Orders handler
    13. $orders = new MicroCollection();
    14. $orders->setHandler(
    15. new OrdersController()
    16. );
    17. $orders->setPrefix('/users');
    18. $orders->get('/get/{id}', 'get');
    19. $orders->get('/add/{payload}', 'add');
    20. $app->mount($orders);
    21. // Products handler
    22. $products = new MicroCollection();
    23. $products->setHandler(
    24. new ProductsController()
    25. );
    26. $products->setPrefix('/products');
    27. $products->get('/get/{id}', 'get');
    28. $products->get('/add/{payload}', 'add');
    29. $app->mount($products);

    This implementation loads each handler in turn and mounts it in our application object. The issue with this approach is that each request will result to only one endpoint and therefore one class method executed. The remaining methods/handlers will just remain in memory without being used.

    Using lazy loading we reduce the number of objects loaded in memory and as a result our application uses less memory.

    The above implementation changes if we want to use lazy loading as follows:

    1. <?php
    2. use Phalcon\Mvc\Micro\Collection as MicroCollection;
    3. // Users handler
    4. $users = new MicroCollection();
    5. $users->setHandler(new UsersController(), true);
    6. $users->setPrefix('/users');
    7. $users->get('/get/{id}', 'get');
    8. $users->get('/add/{payload}', 'add');
    9. $app->mount($users);
    10. // Orders handler
    11. $orders = new MicroCollection();
    12. $orders->setHandler(new OrdersController(), true);
    13. $orders->setPrefix('/users');
    14. $orders->get('/get/{id}', 'get');
    15. $orders->get('/add/{payload}', 'add');
    16. $app->mount($orders);
    17. // Products handler
    18. $products = new MicroCollection();
    19. $products->setHandler(new ProductsController(), true);
    20. $products->setPrefix('/products');
    21. $products->get('/get/{id}', 'get');
    22. $products->get('/add/{payload}', 'add');
    23. $app->mount($products);

    Using this simple change in implementation, all handlers remain uninstantiated until requested by a caller. Therefore whenever a caller requests /orders/get/2, our application will instantiate the OrdersController and call the get method in it. Our application now uses less resources than before.

    Not found (404)

    1. <?php
    2. $app->notFound(
    3. function () use ($app) {
    4. $app->response->setStatusCode(404, 'Not Found');
    5. $app->response->sendHeaders();
    6. $message = 'Nothing to see here. Move along....';
    7. $app->response->setContent($message);
    8. $app->response->send();
    9. }
    10. );

    You can also handle routes that have not been matched (404) with Middleware discussed below.

    Methods - Verbs

    The Phalcon\Mvc\Micro application provides a set of methods to bind the HTTP method with the route it is intended to.

    delete

    Matches if the HTTP method is DELETE and the route is /api/products/delete/{id}

    1. $app->delete(
    2. '/api/products/delete/{id}',
    3. 'delete_product'
    4. );

    get

    Matches if the HTTP method is GET and the route is /api/products

    1. $app->get(
    2. '/api/products',
    3. 'get_products'
    4. );

    head

    Matches if the HTTP method is HEAD and the route is /api/products

    map

    Map allows you to attach the same endpoint to more than one HTTP method. The example below matches if the HTTP method is GET or POST and the route is /repos/store/refs

    1. $app
    2. ->map(
    3. '/repos/store/refs',
    4. 'action_product'
    5. )
    6. ->via(
    7. [
    8. 'GET',
    9. 'POST',
    10. ]
    11. );

    options

    Matches if the HTTP method is OPTIONS and the route is /api/products/options

    1. $app->options(
    2. '/api/products/options',
    3. 'info_product'
    4. );

    patch

    Matches if the HTTP method is PATCH and the route is /api/products/update/{id}

    1. $app->patch(
    2. '/api/products/update/{id}',
    3. 'update_product'
    4. );

    post

    Matches if the HTTP method is POST and the route is /api/products/add

    1. $app->post(
    2. '/api/products',
    3. 'add_product'
    4. );

    put

    Matches if the HTTP method is PUT and the route is /api/products/update/{id}

    1. $app->put(
    2. '/api/products/update/{id}',
    3. 'update_product'
    4. );

    Collections

    Collections are a handy way to group collections attached to a handler and a common prefix (if needed). For a hypothetical /orders endpoint we could have the following endpoints:

    1. /orders/get/{id}
    2. /orders/add/{payload}
    3. /orders/update/{id}
    4. /orders/delete/{id}

    All of those routes are handled by our OrdersController. We set up our routes with a collection as follows:

    1. <?php
    2. use Phalcon\Mvc\Micro\Collection as MicroCollection;
    3. $orders = new MicroCollection();
    4. $orders->setHandler(new OrdersController());
    5. $orders->setPrefix('/orders');
    6. $orders->get('/get/{id}', 'displayAction');
    7. $orders->get('/add/{payload}', 'addAction');
    8. $orders->get('/update/{id}', 'updateAction');
    9. $orders->get('/delete/{id}', 'deleteAction');
    10. $app->mount($orders);

    The name that we bind each route has a suffix of Action. This is not necessary, your method can be called anything you like.

    Parameters

    We have briefly seen above how parameters are defined in the routes. Parameters are set in a route string by enclosing the name of the parameter in brackets.

    1. $app->get(
    2. '/orders/display/{name}',
    3. function ($name) {
    4. echo "<h1>This is order: {$name}!</h1>";
    5. }

    We can also enforce certain rules for each parameter by using regular expressions. The regular expression is set after the name of the parameter, separating it with :.

    1. // Match the order id
    2. $app->get(
    3. '/orders/display/{id:[0-9]+}',
    4. function ($id) {
    5. echo "<h1>This is order: #{$id}!</h1>";
    6. }
    7. );
    8. // Match a numeric (4) year and a title (alpha)
    9. $app->get(
    10. '/posts/{year:[0-9][4]}/{title:[a-zA-Z\-]+}',
    11. function ($year, $title) {
    12. echo '<h1>Title: $title</h1>';
    13. echo '<h2>Year: $year</h2>';
    14. }
    15. );

    Additional information: Info

    Redirections

    You can redirect one matched route to another using the Phalcon\Http\Response object, just like in a full application.

    1. $app->post('/old/url',
    2. function () use ($app) {
    3. $app->response->redirect('new/url');
    4. $app->response->sendHeaders();
    5. }
    6. );
    7. function () use ($app) {
    8. echo 'This is the new Welcome';
    9. }
    10. );

    Note we have to pass the $app object in our anonymous function to have access to the request object.

    When using controllers as handlers, you can perform the redirect just as easy:

    1. <?php
    2. use Phalcon\Mvc\Controller;
    3. class UsersController extends Controller
    4. {
    5. public function oldget($id)
    6. {
    7. return $this->response->redirect(
    8. 'users/get/' . $id
    9. );
    10. }
    11. public function get($id)
    12. {
    13. // ...
    14. }
    15. }

    Finally, you can perform redirections in your middleware (if you are using it). An example is below in the relevant section.

    URLs for Routes

    Another feature of the routes is setting up named routes and generating URLs for those routes. This is a two step process.

    • First we need to name our route. This can be achieved with the setName() method that is exposed from the methods/verbs in our application (get, post, etc.);
    1. // Set a route with the name 'show-order'
    2. $app
    3. ->get(
    4. '/orders/display/{id}',
    5. function ($id) use ($app) {
    6. // ... Find the order and show it
    7. }
    8. )
    9. ->setName('show-order');
    • We need to use the Phalcon\Url component to generate URLs for the named routes.
    1. // Use the named route and produce a URL from it
    2. $app->get(
    3. '/',
    4. function () use ($app) {
    5. $url = sprintf(
    6. '<a href="%s">Show the order</a>',
    7. $app->url->get(
    8. [
    9. 'for' => 'show-order',
    10. 'id' => 1234,
    11. ]
    12. )
    13. );
    14. echo $url;
    15. }
    16. );

    When a micro application is created, a services container is create implicitly.

    1. <?php
    2. use Phalcon\Mvc\Micro;
    3. $app = new Micro();
    4. $app->get(
    5. '/',
    6. function () use ($app) {
    7. $app->response->setContent('Hello!!');
    8. $app->response->send();
    9. }
    10. );

    You can also create a Di container yourself, and assign it to the micro application, therefore manipulating the services depending on the needs of your application.

    1. <?php
    2. use Phalcon\Mvc\Micro;
    3. use Phalcon\Di\FactoryDefault;
    4. use Phalcon\Config\Adapter\Ini as IniConfig;
    5. $di = new FactoryDefault();
    6. $di->set(
    7. 'config',
    8. function () {
    9. return new IniConfig('config.ini');
    10. }
    11. );
    12. $app = new Micro();
    13. $app->setDI($di);
    14. $app->get(
    15. '/',
    16. function () use ($app) {
    17. // Read a setting from the config
    18. echo $app->config->app_name;
    19. }
    20. );
    21. $app->post(
    22. '/contact',
    23. function () use ($app) {
    24. $app->flash->success('What are you doing Dave?');
    25. }
    26. );

    You can also use the array syntax to register services in the dependency injection container from the application object:

    1. <?php
    2. use Phalcon\Mvc\Micro;
    3. use Phalcon\Db\Adapter\Pdo\Mysql as MysqlAdapter;
    4. $app = new Micro();
    5. // Setup the database service
    6. $app['db'] = function () {
    7. return new MysqlAdapter(
    8. [
    9. 'host' => 'localhost',
    10. 'username' => 'root',
    11. 'password' => 'secret',
    12. 'dbname' => 'test_db',
    13. ]
    14. );
    15. };
    16. $app->get(
    17. '/blog',
    18. function () use ($app) {
    19. $news = $app['db']->query('SELECT * FROM news');
    20. foreach ($news as $new) {
    21. echo $new->title;
    22. }
    23. }
    24. );

    Responses

    A micro application can return many different types of responses. Direct output, use a template engine, calculated data, view based data, JSON etc.

    Handlers may return raw responses using plain text, object or a custom built component that implements the Phalcon\Http\ResponseInterface.

    1. $app->get(
    2. '/orders/display/{name}',
    3. function ($name) {
    4. echo "<h1>This is order: {$name}!</h1>";
    5. }
    6. );

    Including another file

    1. $app->get(
    2. '/orders/display/{name}',
    3. function ($name) {
    4. require 'views/results.php';
    5. }
    6. );

    Direct output JSON

    1. $app->get(
    2. '/orders/display/{name}',
    3. function ($name) {
    4. echo json_encode(
    5. [
    6. 'code' => 200,
    7. 'name' => $name,
    8. ]
    9. );
    10. }
    11. );

    New Response object

    You can use the setContent method of the response object to return the response back:

    1. $app->get(
    2. '/show/data',
    3. function () {
    4. // Create a response
    5. $response = new Phalcon\Http\Response();
    6. // Set the Content-Type header
    7. $response->setContentType('text/plain');
    8. // Pass the content of a file
    9. $response->setContent(
    10. file_get_contents('data.txt')
    11. );
    12. // Return the response
    13. return $response;
    14. }
    15. );

    Application Response

    You can also use the object to return responses to the caller. The response object has a lot of useful methods that make returning responses much easier.

    Return Application Response

    A different approach returning data back to the caller is to return the response object directly from the application. When responses are returned by handlers they are automatically sent by the application.

    1. <?php
    2. use Phalcon\Mvc\Micro;
    3. use Phalcon\Http\Response;
    4. $app = new Micro();
    5. // Return a response
    6. $app->get(
    7. '/welcome/index',
    8. function () {
    9. $response = new Response();
    10. $response->setStatusCode(401, 'Unauthorized');
    11. $response->setContent('Access is not authorized');
    12. return $response;
    13. }
    14. );

    JSON

    JSON can be sent back just as easy using the Phalcon\Http\Response object:

    1. $app->get(
    2. '/welcome/index',
    3. function () use ($app) {
    4. $data = [
    5. 'code' => 401,
    6. 'status' => 'error',
    7. 'message' => 'Unauthorized access',
    8. 'payload' => [],
    9. ];
    10. $response->setJsonContent($data);
    11. return $response;
    12. }
    13. );

    A application works closely with a Phalcon\Events\Manager if it is present, to trigger events that can be used throughout our application. The type of those events is micro. These events trigger in our application and can be attached to relevant handlers that will perform actions needed by our application.

    Available events

    The following events are supported:

    1. <?php
    2. use Phalcon\Mvc\Micro;
    3. use Phalcon\Events\Event;
    4. use Phalcon\Events\Manager as EventsManager;
    5. // Create a events manager
    6. $eventsManager = new EventsManager();
    7. $eventsManager->attach(
    8. 'micro:beforeExecuteRoute',
    9. function (Event $event, $app) {
    10. if ($app->session->get('auth') === false) {
    11. $app->flashSession->error("The user isn't authenticated");
    12. $app->response->redirect('/');
    13. $app->response->sendHeaders();
    14. // Return (false) stop the operation
    15. return false;
    16. }
    17. }
    18. );
    19. $app = new Micro();
    20. // Bind the events manager to the app
    21. $app->setEventsManager($eventsManager);

    Not found example

    You can easily check whether a user has been authenticated or not using the beforeExecuteRoute event.In the following example, we explain how to control the application security using events:

    1. <?php
    2. use Phalcon\Mvc\Micro;
    3. use Phalcon\Events\Event;
    4. use Phalcon\Events\Manager as EventsManager;
    5. // Create a events manager
    6. $eventsManager = new EventsManager();
    7. $eventsManager->attach(
    8. 'micro:beforeNotFound',
    9. function (Event $event, $app) {
    10. $app->response->redirect('/404');
    11. $app->response->sendHeaders();
    12. return $app->response;
    13. }
    14. );
    15. $app = new Micro();
    16. // Bind the events manager to the app
    17. $app->setEventsManager($eventsManager);

    Middleware

    Middleware are classes that can be attached to your application and introduce another layer where business logic can exist. They run sequentially, according to the order they are registered and not only improve maintainability, by encapsulating specific functionality, but also performance. A middleware class can stop execution when a particular business rule has not been satisfied, thus allowing the application to exit early without executing the full cycle of a request.

    The presence of a Phalcon\Events\Manager is essential for middleware to operate, so it has to be registered in our Di container.

    Attached events

    Middleware can be attached to a micro application in 3 different events. Those are:

    EventDescription
    beforeBefore the handler has been executed
    afterAfter the handler has been executed
    finishAfter the response has been sent to the caller

    before

    This event is perfect for stopping execution of the application if certain criteria is not met. In the below example we are checking if the user has been authenticated and stop execution with the necessary redirect.

    1. <?php
    2. $app = new Phalcon\Mvc\Micro();
    3. // Executed before every route is executed
    4. // Return false cancels the route execution
    5. $app->before(
    6. function () use ($app) {
    7. if (false === $app['session']->get('auth')) {
    8. $app['flashSession']->error("The user isn't authenticated");
    9. $app['response']->redirect('/error');
    10. // Return false stops the normal execution
    11. return false;
    12. }
    13. return true;
    14. }
    15. );

    after

    This event can be used to manipulate data or perform actions that are needed after the handler has finished executing. In the example below, we manipulate our response to send JSON back to the caller.

    1. $app->map(
    2. '/api/robots',
    3. function () {
    4. return [
    5. 'status' => 'OK',
    6. ];
    7. }
    8. );
    9. $app->after(
    10. function () use ($app) {
    11. // This is executed after the route is executed
    12. echo json_encode(
    13. $app->getReturnedValue()
    14. );
    15. }
    16. );

    finish

    This even will fire up when the whole request cycle has been completed. In the example below, we use it to clean up some cache files.

    1. $app->finish(
    2. function () use ($app) {
    3. if (true === file_exists('/tmp/processing.cache')) {
    4. unlink('/tmp/processing.cache');
    5. }
    6. }
    7. );

    Setup

    Attaching middleware to your application is very easy as shown above, with the before, after and finish method calls.

    1. $app->before(
    2. function () use ($app) {
    3. if (false === $app['session']->get('auth')) {
    4. $app['flashSession']->error("The user isn't authenticated");
    5. $app['response']->redirect('/error');
    6. // Return false stops the normal execution
    7. return false;
    8. }
    9. return true;
    10. }
    11. );
    12. $app->after(
    13. function () use ($app) {
    14. // This is executed after the route is executed
    15. echo json_encode(
    16. $app->getReturnedValue()
    17. );
    18. }
    19. );

    Attaching middleware to your application as classes and having it listen to events from the events manager can be achieved as follows:

    1. <?php
    2. use Phalcon\Events\Manager;
    3. use Phalcon\Mvc\Micro;
    4. use Website\Middleware\CacheMiddleware;
    5. use Website\Middleware\NotFoundMiddleware;
    6. use Website\Middleware\ResponseMiddleware;
    7. /**
    8. * Create a new Events Manager.
    9. */
    10. $eventsManager = new Manager();
    11. $application = new Micro();
    12. /**
    13. * Attach the middleware both to the events manager and the application
    14. */
    15. $eventsManager->attach(
    16. 'micro',
    17. new CacheMiddleware()
    18. );
    19. $application->before(
    20. new CacheMiddleware()
    21. );
    22. $eventsManager->attach(
    23. 'micro',
    24. new NotFoundMiddleware()
    25. );
    26. $application->before(
    27. new NotFoundMiddleware()
    28. );
    29. /**
    30. * This one needs to listen on the `after` event
    31. */
    32. $eventsManager->attach(
    33. 'micro',
    34. new ResponseMiddleware()
    35. );
    36. $application->after(
    37. new ResponseMiddleware()
    38. );
    39. /**
    40. * Make sure our events manager is in the DI container now
    41. */
    42. $application->setEventsManager($eventsManager);

    We need a Phalcon\Events\Manager object. This can be a newly instantiated object or we can get the one that exists in our DI container (if you have used the FactoryDefault one).

    We attach every middleware class in the micro hook in the Events Manager. We could also be a bit more specific and attach it to say the micro:beforeExecuteRoute event.

    We then attach the middleware class in our application on one of the three listening events discussed above (before, after, finish).

    Implementation

    Middleware can be any kind of PHP callable functions. You can organize your code whichever way you like it to implement middleware. If you choose to use classes for your middleware, you will need them to implement the Phalcon\Mvc\Micro\MiddlewareInterface

    1. <?php
    2. use Phalcon\Mvc\Micro;
    3. use Phalcon\Mvc\Micro\MiddlewareInterface;
    4. /**
    5. * CacheMiddleware
    6. *
    7. * Caches pages to reduce processing
    8. */
    9. class CacheMiddleware implements MiddlewareInterface
    10. {
    11. /**
    12. * Calls the middleware
    13. *
    14. * @param Micro $application
    15. * @returns bool
    16. */
    17. public function call(Micro $application)
    18. {
    19. $cache = $application['cache'];
    20. $router = $application['router'];
    21. $key = preg_replace(
    22. '/^[a-zA-Z0-9]/',
    23. '',
    24. $router->getRewriteUri()
    25. );
    26. // Check if the request is cached
    27. if ($cache->exists($key)) {
    28. echo $cache->get($key);
    29. return false;
    30. }
    31. return true;
    32. }
    33. }

    Events in Middleware

    The events that are triggered for our application also trigger inside a class that implements the . This offers great flexibility and power for developers since we can interact with the request process.

    API example

    Assume that we have an API that we have implemented with the Micro application. We will need to attach different Middleware classes in the application so that we can better control the execution of the application.

    The middleware that we will use are:

    • Firewall
    • NotFound
    • Redirect
    • CORS
    • Request
    • Response

    Firewall Middleware

    This middleware is attached to the before event of our Micro application. The purpose of this middleware is to check who is calling our API and based on a whitelist, allow them to proceed or not

    1. <?php
    2. use Phalcon\Events\Event;
    3. use Phalcon\Mvc\Micro;
    4. use Phalcon\Mvc\Micro\MiddlewareInterface;
    5. /**
    6. * FirewallMiddleware
    7. *
    8. * Checks the whitelist and allows clients or not
    9. */
    10. class FirewallMiddleware implements MiddlewareInterface
    11. {
    12. /**
    13. * Before anything happens
    14. *
    15. * @param Event $event
    16. * @param Micro $application
    17. *
    18. * @returns bool
    19. */
    20. public function beforeHandleRoute(Event $event, Micro $application)
    21. {
    22. $whitelist = [
    23. '10.4.6.1',
    24. '10.4.6.2',
    25. '10.4.6.3',
    26. '10.4.6.4',
    27. ];
    28. $ipAddress = $application->request->getClientAddress();
    29. if (true !== array_key_exists($ipAddress, $whitelist)) {
    30. $this->response->redirect('/401');
    31. $this->response->send();
    32. return false;
    33. }
    34. return true;
    35. }
    36. /**
    37. * Calls the middleware
    38. *
    39. * @param Micro $application
    40. *
    41. * @returns bool
    42. */
    43. public function call(Micro $application)
    44. {
    45. return true;
    46. }
    47. }

    Not Found Middleware

    When this middleware is processed, this means that the requesting IP is allowed to access our application. The application will try and match the route and if not found the beforeNotFound event will fire. We will stop the processing then and send back to the user the relevant 404 response. This middleware is attached to the before event of our Micro application

    1. <?php
    2. use Phalcon\Mvc\Micro;
    3. use Phalcon\Mvc\Micro\MiddlewareInterface;
    4. /**
    5. * NotFoundMiddleware
    6. *
    7. * Processes the 404s
    8. */
    9. class NotFoundMiddleware implements MiddlewareInterface
    10. {
    11. /**
    12. * The route has not been found
    13. *
    14. * @returns bool
    15. */
    16. public function beforeNotFound()
    17. {
    18. $this->response->redirect('/404');
    19. $this->response->send();
    20. return false;
    21. }
    22. /**
    23. * Calls the middleware
    24. *
    25. * @param Micro $application
    26. *
    27. * @returns bool
    28. */
    29. public function call(Micro $application)
    30. {
    31. return true;
    32. }
    33. }

    Redirect Middleware

    We attach this middleware again to the before event of our Micro application because we don’t want the request to proceed if the requested endpoint needs to be redirected.

    1. <?php
    2. use Phalcon\Events\Event;
    3. use Phalcon\Mvc\Micro;
    4. use Phalcon\Mvc\Micro\MiddlewareInterface;
    5. /**
    6. * RedirectMiddleware
    7. *
    8. * Checks the request and redirects the user somewhere else if need be
    9. */
    10. class RedirectMiddleware implements MiddlewareInterface
    11. {
    12. /**
    13. * Before anything happens
    14. *
    15. * @param Event $event
    16. * @param Micro $application
    17. *
    18. * @returns bool
    19. */
    20. public function beforeHandleRoute(Event $event, Micro $application)
    21. {
    22. if ('github' === $application->request->getURI()) {
    23. $application->response->redirect('https://github.com');
    24. $application->response->send();
    25. return false;
    26. }
    27. return true;
    28. }
    29. /**
    30. * Calls the middleware
    31. *
    32. * @param Micro $application
    33. *
    34. * @returns bool
    35. */
    36. public function call(Micro $application)
    37. {
    38. return true;
    39. }
    40. }

    CORS Middleware

    Again this middleware is attached to the before event of our Micro application. We need to ensure that it fires before anything happens with our application

    1. <?php
    2. use Phalcon\Events\Event;
    3. use Phalcon\Mvc\Micro;
    4. use Phalcon\Mvc\Micro\MiddlewareInterface;
    5. /**
    6. * CORSMiddleware
    7. *
    8. * CORS checking
    9. */
    10. class CORSMiddleware implements MiddlewareInterface
    11. {
    12. /**
    13. * Before anything happens
    14. *
    15. * @param Event $event
    16. * @param Micro $application
    17. *
    18. * @returns bool
    19. */
    20. public function beforeHandleRoute(Event $event, Micro $application)
    21. {
    22. if ($application->request->getHeader('ORIGIN')) {
    23. $origin = $application->request->getHeader('ORIGIN');
    24. } else {
    25. $origin = '*';
    26. }
    27. $application
    28. ->response
    29. ->setHeader('Access-Control-Allow-Origin', $origin)
    30. ->setHeader(
    31. 'Access-Control-Allow-Methods',
    32. 'GET,PUT,POST,DELETE,OPTIONS'
    33. )
    34. ->setHeader(
    35. 'Access-Control-Allow-Headers',
    36. 'Origin, X-Requested-With, Content-Range, ' .
    37. 'Content-Disposition, Content-Type, Authorization'
    38. )
    39. ->setHeader('Access-Control-Allow-Credentials', 'true');
    40. }
    41. /**
    42. * Calls the middleware
    43. *
    44. * @param Micro $application
    45. *
    46. * @returns bool
    47. */
    48. public function call(Micro $application)
    49. {
    50. return true;
    51. }
    52. }

    Request Middleware

    This middleware is receiving a JSON payload and checks it. If the JSON payload is not valid it will stop execution.

    1. <?php
    2. use Phalcon\Events\Event;
    3. use Phalcon\Mvc\Micro;
    4. use Phalcon\Mvc\Micro\MiddlewareInterface;
    5. /**
    6. * RequestMiddleware
    7. *
    8. * Check incoming payload
    9. */
    10. class RequestMiddleware implements MiddlewareInterface
    11. {
    12. /**
    13. * Before the route is executed
    14. *
    15. * @param Event $event
    16. * @param Micro $application
    17. *
    18. * @returns bool
    19. */
    20. public function beforeExecuteRoute(Event $event, Micro $application)
    21. {
    22. json_decode(
    23. $application->request->getRawBody()
    24. );
    25. if (JSON_ERROR_NONE !== json_last_error()) {
    26. $application->response->redirect('/malformed');
    27. $application->response->send();
    28. return false;
    29. }
    30. return true;
    31. }
    32. /**
    33. * Calls the middleware
    34. *
    35. * @param Micro $application
    36. *
    37. * @returns bool
    38. */
    39. public function call(Micro $application)
    40. {
    41. return true;
    42. }
    43. }

    Response Middleware

    This middleware is responsible for manipulating our response and sending it back to the caller as a JSON string. Therefore we need to attach it to the after event of our Micro application.

    We are going to be using the call method for this middleware, since we have nearly executed the whole request cycle.

    1. <?php
    2. use Phalcon\Mvc\Micro;
    3. use Phalcon\Mvc\Micro\MiddlewareInterface;
    4. /**
    5. * ResponseMiddleware
    6. *
    7. * Manipulates the response
    8. */
    9. class ResponseMiddleware implements MiddlewareInterface
    10. {
    11. /**
    12. * Before anything happens
    13. *
    14. * @param Micro $application
    15. *
    16. * @returns bool
    17. */
    18. public function call(Micro $application)
    19. {
    20. $payload = [
    21. 'code' => 200,
    22. 'status' => 'success',
    23. 'message' => '',
    24. 'payload' => $application->getReturnedValue(),
    25. ];
    26. $application->response->setJsonContent($payload);
    27. $application->response->send();
    28. return true;
    29. }
    30. }

    Models

    Models can be used in Micro applications, so long as we instruct the application how it can find the relevant classes with an autoloader.

    1. <?php
    2. $loader = new \Phalcon\Loader();
    3. $loader
    4. ->registerDirs(
    5. [
    6. __DIR__ . '/models/',
    7. ]
    8. )
    9. ->register();
    10. $app = new \Phalcon\Mvc\Micro();
    11. $app->get(
    12. '/products/find',
    13. function () {
    14. $products = \MyModels\Products::find();
    15. foreach ($products as $product) {
    16. echo $product->name, '<br>';
    17. }
    18. }
    19. );
    20. $app->handle(
    21. $_SERVER["REQUEST_URI"]
    22. );

    Inject model instances

    By using the class you can inject model instances into your routes:

    1. <?php
    2. $loader = new \Phalcon\Loader();
    3. $loader->registerDirs(
    4. [
    5. __DIR__ . '/models/',
    6. ]
    7. )->register();
    8. $app = new \Phalcon\Mvc\Micro();
    9. $app->setModelBinder(
    10. new \Phalcon\Mvc\Model\Binder()
    11. );
    12. $app->get(
    13. "/products/{product:[0-9]+}",
    14. function (Products $product) {
    15. // do anything with $product object
    16. }
    17. );
    18. $app->handle(
    19. $_SERVER["REQUEST_URI"]
    20. );

    Since Binder object is using internally Reflection Api which can be heavy, there is ability to set a cache so as to speed up the process. This can be done by using the second argument of setModelBinder() which can also accept a service name or just by passing a cache instance to the Binder constructor.

    Currently the binder will only use the models primary key to perform a findFirst() on. An example route for the above would be /products/1.

    Phalcon\Mvc\Micro does not have inherently a view service. We can however use the component to render views.

    1. <?php
    2. $app = new Phalcon\Mvc\Micro();
    3. $app['view'] = function () {
    4. $view = new \Phalcon\Mvc\View\Simple();
    5. $view->setViewsDir('app/views/');
    6. return $view;
    7. };
    8. // Return a rendered view
    9. $app->get(
    10. '/products/show',
    11. function () use ($app) {
    12. // Render app/views/products/show.phtml passing some variables
    13. echo $app['view']->render(
    14. 'products/show',
    15. [
    16. 'id' => 100,
    17. 'name' => 'Artichoke',
    18. ]
    19. );
    20. }
    21. );

    The above example uses the Phalcon\Mvc\View\Simple component, which uses relative paths instead of controllers and actions. You can use the component instead, but to do so you will need to change the parameters passed to render().

    1. <?php
    2. $app = new Phalcon\Mvc\Micro();
    3. $app['view'] = function () {
    4. $view = new \Phalcon\Mvc\View();
    5. $view->setViewsDir('app/views/');
    6. return $view;
    7. };
    8. // Return a rendered view
    9. $app->get(
    10. '/products/show',
    11. function () use ($app) {
    12. // Render app/views/products/show.phtml passing some variables
    13. echo $app['view']->render(
    14. 'products',
    15. 'show',
    16. [
    17. 'id' => 100,
    18. 'name' => 'Artichoke',
    19. ]
    20. );
    21. }
    22. );

    Error Handling

    The application also has an error method, which can be used to trap any errors that originate from exceptions. The following code snippet shows basic usage of this feature: