Home Web Development Phalcon Cookbook

Phalcon Cookbook

By Serghei Iakovlev , David Schissler
books-svg-icon Book
eBook $35.99 $24.99
Print $43.99
Subscription $15.99 $10 p/m for three months
$10 p/m for first 3 months. $15.99 p/m after that. Cancel Anytime!
What do you get with a Packt Subscription?
This book & 7000+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook + Subscription?
Download this book in EPUB and PDF formats, plus a monthly download credit
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook?
Download this book in EPUB and PDF formats
Access this title in our online reader
DRM FREE - Read whenever, wherever and however you want
Online reader with customised display settings for better reading experience
What do you get with video?
Download this video in MP4 format
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with video?
Stream this video
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with Audiobook?
Download a zip folder consisting of audio files (in MP3 Format) along with supplementary PDF
What do you get with Exam Trainer?
Flashcards, Mock exams, Exam Tips, Practice Questions
Access these resources with our interactive certification platform
Mobile compatible-Practice whenever, wherever, however you want
BUY NOW $10 p/m for first 3 months. $15.99 p/m after that. Cancel Anytime!
eBook $35.99 $24.99
Print $43.99
Subscription $15.99 $10 p/m for three months
What do you get with a Packt Subscription?
This book & 7000+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook + Subscription?
Download this book in EPUB and PDF formats, plus a monthly download credit
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook?
Download this book in EPUB and PDF formats
Access this title in our online reader
DRM FREE - Read whenever, wherever and however you want
Online reader with customised display settings for better reading experience
What do you get with video?
Download this video in MP4 format
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with video?
Stream this video
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with Audiobook?
Download a zip folder consisting of audio files (in MP3 Format) along with supplementary PDF
What do you get with Exam Trainer?
Flashcards, Mock exams, Exam Tips, Practice Questions
Access these resources with our interactive certification platform
Mobile compatible-Practice whenever, wherever, however you want
  1. Free Chapter
    Getting Used to Phalcon
About this book
Phalcon is a high-performance PHP framework delivered as a PHP extension. This provides new opportunities for speed and application design, which until recently have been unrealized in the PHP ecosystem. Packed with simple learning exercises, technology prototypes, and real-world usable code, this book will guide you from the beginner and setup stage all the way to advanced usage. You will learn how to avoid niche pitfalls, how to use the command-line developer tools, how to integrate with new web standards, as well as how to set up and customize the MVC application structure. You will see how Phalcon can be used to quickly set up a single file web application as well as a complex multi-module application suitable for long-term projects. Some of the recipes focus on abstract concepts that are vital to get a deep comprehension of Phalcon and others are designed as a vehicle to deliver real-world usable classes and code snippets to solve advanced problems. You’ll start out with basic setup and application structure and then move onto the Phalcon MVC and routing implementation, the power of the ORM and Phalcon Query Language, and Phalcon’s own Volt templating system. Finally, you will move on to caching, security, and optimization.
Publication date:
November 2016
Publisher
Packt
Pages
386
ISBN
9781784396886

 

Chapter 1. Getting Used to Phalcon

In this chapter, we will cover the following topics:

  • Getting your IDE to work nicely with Phalcon

  • Creating the application directory structure

  • Setting up your request entry point

  • Easily loading code on demand

  • Initializing Phalcon to handle a request

  • Understanding the request life cycle

 

Introduction


Despite the fact that Phalcon is a framework for PHP application development, it isn't itself PHP-based. Therefore, some difficulties may appear when using syntax highlighting or code autocompletion. In this chapter, we will try to study how to set up autocompletion in a modern Integrated Development Environment (IDE) and how to enable code autocompletion.

An essential step in the architecture process when developing an application is a clear understanding of the project directory structure and the role of each of the directories. We will use a variation of a Phalcon project directory structure, which is easy to deploy and upgrade. The directory structure, which we work with in this chapter, isn't a rule set in stone. In future you will, time after time, use different directory structures, which will be different than the one we will be using. That structure is only one of the options and a starting point for your needs, and it easily responds to changes caused by certain technical conditions.

Phalcon is not only the fastest framework in the world, it is one of the easiest frameworks. As a rule, you don't need any special knowledge or additional software. However, when developing an application with Phalcon, you will need to create a base system configuration and application skeleton. In this chapter, we will learn how to deploy an application with the use of Phalcon, how to make its base configuration, and how to create a flexible and efficient application skeleton, which you can use in future.

Any object-oriented PHP application needs classes. You have to tell your class autoloader where your classes are situated and which way they can be searched for. We will look at some possible options of class auto-loading in Phalcon and their usage methods.

Many developers, despite their experience, have difficulty when working with a Request component. Phalcon isn't an exception. We will discover how to make a base application configuration so that Phalcon starts handling requests. We will learn how Phalcon handles these requests and in what sequence they are executed. We will learn which components take part in a request life cycle, where it is possible to catch the request, how to specify its state, as well as how to take any suitable actions.

 

Getting your IDE to work nicely with Phalcon


Most IDEs have some form of code completion as part of the program. To enable auto-completion for Phalcon's namespaces, we will need to help the IDE recognize those namespaces. We will also see how to enable Volt syntax highlighting and Phalcon API auto-completion for major IDEs.

Getting ready

To get started, we should have Git installed. To enable auto-completion in your IDE, you need to get the Phalcon stubs. For these purposes, clone the Phalcon Developer Tools repository:

git clone git@github.com:phalcon/phalcon-devtools.git

How to do it…

The following are the steps needed to complete this recipe.

Ways to enable Phalcon API autocompletion in major IDEs

PhpStorm
  1. Using the Project Tool window in PhpStorm, select External Libraries, right-click on the section, and select Configure PHP Include Paths... from the drop-down menu.

  2. Then, using the External Libraries dialog box, click the + button. Next, select the Specify Other... option in the drop-down menu and specify the location of the Phalcon stubs. The stubs are located in the phalcon-devtools repository you just cloned under the /ide subfolder. You should specify the path phalcon-devtools/ide/stubs/Phalcon and click on Apply.

NetBeans
  1. In NetBeans, you need to open the Projects window (Ctrl + F1) and select Include Path. Right-click on the section and select Properties from the drop-down menu.

  2. Then, open the Project Properties window and click on the Add Folder... button. Next, specify the location of the Phalcon stubs. The stubs are located in the phalcon-devtools repository you just cloned under the /ide subfolder. You should specify the path phalcon-devtools/ide/stubs/Phalcon and click on the OK button.

Ways to enable Volt syntax highlighting in major IDEs

Netbeans
  1. Click on the Tools menu entry and select Options.

  2. In the opened window, select Miscellaneous | Files.

  3. In the File Associations group box, click on the button New… next to the File Extensions option, enter the extension volt, and click on OK.

  4. Select TWIG (text/x-twig) in the Associated File Type (MIME) drop-down menu.

PhpStorm
  1. Open File | Settings. Next select Editor | File Types.

  2. In the Recognized File Types group box, select Twig and add the extension *.volt into the Registered Patterns group box by pressing the + button.

Syntax highlighting for Sublime Text or TextMate

Installation via Package Control

If you have Sublime Package Control, you know what to do. If not, well, it's a package manager for Sublime Text, and it's awesome!

After installing Sublime Package Control and restarting the editor:

  1. Open the Command Palette (Ctrl + Shift + P or Cmd + Shift + P).

  2. Type Install Package and press Enter.

  3. Find Volt and press Enter.

Manual installation

For manual installation, clone the Volt syntax highlight for the Sublime Text 2/Textmate repository:

git@github.com:phalcon/volt-sublime-textmate.git

Depending on your OS, copy the volt-sublime-textmate/Volt directory to any of the following locations:

  1. Sublime Text 2:

    1. Mac OS X:

      ~/Library/Application Support/Sublime Text 2/Packages
    2. Linux:

      ~/.Sublime Text 2/Packages
    3. Windows:

      %APPDATA%/Sublime Text 2/Packages/
  2. Sublime Text 3:

    1. Mac OS X:

      ~/Library/Application Support/Sublime Text 3/Packages
    2. Linux:

      ~/.Sublime Text 3/Packages
    3. Windows:

      %APPDATA%/Sublime Text 3/Packages/
  3. TextMate:

    /Library/Application Support/TextMate/Bundles

How it works…

Some IDEs need help understanding the framework syntax. To get the IDE to understand, we download a list of all the Phalcon stubs. Then, when we add it to the include path, NetBeans (or PhpStorm) will automatically check the file and show us the autocomplete options. To enable the Volt or Zephir syntax highlight, we have to configure our IDE or text editor accordingly.

 

Creating the application directory structure


In this recipe, we will take a brief look at the commonly used directory structure of a single module application. A structure of this type is aimed at providing a great starting point for different applications.

Getting ready

We don't need any special means to implement this recipe. We will create the project structure on site, at the development stage. Important points of this recipe are the names of directories and their location.

How to do it…

Follow these steps to complete this recipe:

  1. Create a root directory for your project, where myprojectname is the name of your project:

    /var/www/myprojectname
  2. Create three second-level directories in the root directory:

    /var/www/myprojectname/app
    /var/www/myprojectname/public
    /var/www/myprojectname/.phalcon
  3. Create the following subdirectories in the public directory:

    /var/www/myprojectname/public/css
    /var/www/myprojectname/public/img
    /var/www/myprojectname/public/js
  4. Create the following subdirectories in the app directory:

    /var/www/myprojectname/app/cache
    /var/www/myprojectname/app/config
    /var/www/myprojectname/app/controllers
    /var/www/myprojectname/app/library
    /var/www/myprojectname/app/logs
    /var/www/myprojectname/app/models
    /var/www/myprojectname/app/tasks
    /var/www/myprojectname/app/views
  5. Create the following subdirectories in the cache directory:

    /var/www/myprojectname/app/cache/annotations
    /var/www/myprojectname/app/cache/data
    /var/www/myprojectname/app/cache/metadata
    /var/www/myprojectname/app/cache/volt
  6. Make sure that the just created structure eventually appears as follows:

    myprojectname
    ├── app

    │   │── cache
    │   │   ├── annotations
    │   │   ├── data
    │   │   ├── metadata
    │   │   └── volt
    │   ├── config
    │   ├── controllers
    │   ├── library
    │   ├── logs
    │   ├── models
    │   ├── tasks
    │   └── views
    ├── .phalcon
    └── public
        ├── css
        ├── img
        └── js

How it works…

The project root directory contains the following directories: app, public, and .phalcon (note that the last one has a point at the beginning of its name).

In the app directory, there will be, as you would expect, the main application code. We will describe this folder in detail shortly.

The public directory will contain the entry point of your application and directories for various static assets (JavaScript files, images, and CSS files).

The .phalcon directory will be used by the Phalcon developer tools for internal purposes (migration creation, code generation, and so on). This is the Phalcon system directory. There is no need to create something in it.

The app directory contains a subdirectory for controllers as well as their views and models. Additionally, there is the tasks directory, which is intended for cli tasks, the library directory for the general application components (plug-ins, helpers, and other custom classes), the logs directory, where we will store our log files, and the cache directory for storing cached data of different application components.

In the cache directory, there are the following directories: annotations to store the file cache of model annotations, data to store common file cache, metadata to store the model structure cache, and volt to store the view cache.

Note that we have created different directories to store screenshots. These directories are utilized when we are using the File Cache. If you will be using a cache adapter other than the File one, you don't need to create the directory structure here.

 

Setting up your request entry point


After installing Phalcon, we need to create a Bootstrap class in which we'll set about our application tuning. We will create a flexible yet robust Bootstrap file to receive requests for the application.

Getting ready

The first and most important file you need to create for the initial loading of your application is index.php. This is the file in which all your HTTP requests will be redirected. Create this file in the public directory of your project, if you haven't got one:

/var/www/myBlog/public/index.php

How to do it…

Follow these steps to complete this recipe:

  1. Put the following code into the index.php file:

    <?php
    
    define('APP_PATH', realpath(dirname(__DIR__)));
    
    try {
      include APP_PATH . '/app/library/Bootstrap.php';
      $di = new \Phalcon\Di\FactoryDefault();
      $bootstrap = new \MyBlog\Bootstrap($di);
    
      echo $application->run();
    } catch (\Exception $e) {
      $logger = $di->getShared('logger');
      $logger->error($e->getMessage());
      $logger->error($e->getTraceAsString());
    }
  2. Create a Bootstrap class skeleton in your app/library/Bootstrap.php file:

    <?php
    
    namespace MyBlog;
    
    class Bootstrap extends \Phalcon\Mvc\Application
    {
      protected $loaders = [
        'environment',
        'logger',
        'loader',
        'database',
        'views',
        // other loaders ...
      ];
    
      protected $config;
    
      public function __construct(\Phalcon\DiInterface $di = null)
      {
        $di = $di ?: new \Phalcon\Di\FactoryDefault();
        $this->config = include APP_PATH . '/app/config/config.php';
    
    
        // Store config in the DI container
        $di->setShared('config', $this->config);
    
        parent::__construct($di);
      }
    }
  3. Create an application config file in the app/config/config.php file:

    <?php
    
    return new \Phalcon\Config([
        'application' => [
            'controllersDir' => APP_PATH . '/app/controllers/',
            'modelsDir'      => APP_PATH . '/app/models/',
            'viewsDir'       => APP_PATH . '/app/views/',
            'libraryDir'     => APP_PATH . '/app/library/',
            'logsDir'        => APP_PATH . '/app/logs/',
            'baseUri'        => '/',
            'debug'          => true
        ],
        'database'    => [
            'adapter'  => 'Mysql',
            'host'     => 'localhost',
            'username' => 'root',
            'password' => '',
            'dbname'   => 'my_blog',
            'charset'  => 'utf8'
        ],
        'metaData' => [
            'metaDataDir' => APP_PATH . '/app/cache/metaData/',
        ],
        'modelsCache' => [
            'lifetime' => 86400 * 30,
            'cacheDir' => APP_PATH . '/app/cache/data/',
            'prefix'   => 'myblog-cache-data-'
        ],
        'volt' => [
            'cacheDir'    => APP_PATH . '/app/cache/volt/',
            'compiledExt' => '.php',
            'separator'   => '_',
        ]
    ]);
  4. Create an application initialization method, Bootstrap::run, in the app/library/Bootstrap.php file:

    public function run()
    {
      $em = new \Phalcon\Events\Manager();
      $this->setEventsManager($eventsManager);
      $di = $this->_dependencyInjector;
    
      foreach ($this->loaders as $service) {
        $serviceName = ucfirst($service);
        $this->{'init' . $serviceName}($di, $this->config, $em);
      }
    
      $di->setShared('eventsManager', $em);
    
      return $this->handle()->getContent();
    }
  5. Next, create all the methods required for the application service initialization. Use the Bootstrap::loaders class property, which was created in Step 2, to define the methods. Use the CamelCase style, adding the init prefix to the name of every method:

    protected initEnvironment($di, $config, $em)
    {
      if (true == $config->application->debug) {
        ini_set('display_errors', 1);
        ini_set('display_startup_errors', 1);
        error_reporting(E_ALL | E_STRICT);
    
        if (extension_loaded('xdebug')) {
          ini_set('xdebug.collect_params', 4);
        }
      } else {
        ini_set('display_errors', 0);
        ini_set('display_startup_errors', 0);
        error_reporting(E_ALL ^ E_NOTICE);
      }
    
      if (is_readable(APP_PATH . '/vendor/autoload.php')) {
        include APP_PATH . '/vendor/autoload.php';
      }
    }
    
    protected initLogger($di, $config, $em)
    {
      $di->set('logger', function ($file = 'main', $format = null) use ($config) {
          $path   = $config->application->logsDir . $file . '.log';
          $format = $format ?: $config->application->logFormat;
          $logger = new \Phalcon\Logger\Adapter\File($path);
    
          if ($format) {
            $formatter = new \Phalcon\Logger\Formatter\Line($format);
            $logger->setFormatter($formatter);
          }
    
          return $logger;
      });
    }
    
    protected initLoader($di, $config, $em)
    {
      $di->setShared('loader', function () use ($config, $em) {
        $loader = new \Phalcon\Loader();
        $namespaces = [
          'MyBlog\Models' => $config->application->modelsDir,
          'MyBlog\Controllers' => $config->application->controllersDir,
          'MyBlog' => $config->application->libraryDir
        ];
    
        $loader->registerNamespaces($namespaces);
        $loader->setEventsManager($em);
        $loader->register();
    
        return $loader;
      });
    }
  6. Use the implementation just described to create all the methods, which you need for service initialization. It is evident that all the values of the loaders array are in actual fact the initializer names, which we call sequentially. The order of these values defines the method call order. You can use the following code template as an example:

    protected initMethodName($di, $config, $eventsManager)
    {
        // Some initialization
    
        return ...; // may be omitted to return null
    }

How it works…

The first thing that should be observed is that we have used a Bootstrap class. Phalcon uses a Dependency Injection (DI) container to handle all the services you may need to use in your application. We have passed it as a parameter to the Bootstrap class constructor, in which we will set up our application.

In the class constructor, we create a DI container. If it wasn't passed as a parameter, include the application configuration by placing it into a container for future use and create an instance, Phalcon\Mvc\Application. Note that we already use the APP_PATH constant, previously defined. Let's remember how it was defined:

define('APP_PATH', realpath(dirname(__DIR__)));

We have also created a loader class property, which we will discuss later.

We have added the application configuration in a separate file. Usually, it is worthwhile due to the fact that according to the application growth, the configuration of the application elements and services will grow. It is much more convenient to have the whole configuration in one place, rather than scattered all over the application. Here we have specified the main application paths and parameters of the database connection, metadata cache, models, and views. We have defined five directories for our application; these are controllers, models, views, class library, and logs. Besides this, we have specified directories for the metadata cache of models and for the data itself, as well as a view cache directory. If these directories do not exist, it is time to create them. All we need for our application is presented here.

In the Bootstrap::run method we have created the event manager, which we can use at any point, subscribed to its events, and passed it to the application. You must not forget the loaders field we have already defined. We will use it right at the present moment. When iterating through the array, we form the method name, ucfirst($service). Then we register a new event in the event manager (of course, we can subscribe to this event now) and call this method by passing the DI container, the application configuration, and the event manager into it.

Next, let's have a look at the most important of initializer methods. The first one is the application environment configuration. There is nothing out of the ordinary here; we define the application error reporting level in an emergency. Note that error output enabling is designed and suitable only for development and must not be used in ready-made production systems. Finally, we include the Composer autoloader, if there is one, and configure the xdebug extension to make our application debugging more convenient. Note that it is highly recommended to use at least Xdebug v2.2.3 for better compatibility with Phalcon.

Next, we've just created a quite powerful logger, which we can use in different ways. The code snippet is as follows:

$this->di->get('logger', ['some_file'])->debug('Some text ...');

This code, which if used, for example, in a controller, will tell the logger to create the following file on the disk if it is missing there:

/var/www/myBlog/app/logs/some_file.log

The path for the log creation is formed according to the configuration just defined. The logger will write the following to that file:

[Tue, 28 Jul 15 22:09:02 -0500][DEBUG] Some text ...

And the following code:

$this->di->get('logger')->debug('Some text ...');

We will use the main.log file. Additionally, we are able to define the message format in our log files. In order to get that done, we need to pass the format as the second array element on the get method, like this:

get('logger', ['some_file', "%date% - %message%"])

Also, we can define the format in our configuration file.

The next method we need is the autoloader initialization method. We have created one more method in our Bootstrap class and initialized the class autoloading component in this method. It means that we have registered the necessary namespaces there. We have defined the namespace as MyBlog for all future library classes, located in app/library/, in relation to the application root (think back to our configuration file), and have added paths for the namespaces of models and controllers, which are located in app/models/ and app/controllers/, respectively, to the autoloading. Phalcon Loader will register the namespaces we may need, and PHP will search for some classes within the MyBlog\Models namespace in /var/www/myBlog/app/models/, within the MyBlog\Controllers namespace in /var/www/myBlog/app/controllers/, and any other classes contained within the MyBlog namespace in /var/www/myBlog/app/library/ (for example, MyBlog\Acl or MyBlog\Utils\Backup). In future, as the structure of our application grows, we will always be able to enhance or modify this method.

Finally, when looking at the Bootstrap::run method, it becomes apparent that after the successful initialization of all the necessary services, the following parent method, $this->handle()->getContent(), is called and returns its result back to the caller. That's just the thing.

The line in the entry point (index.php) will display the application output:

echo $application->run();

If the application throws an exception, we'll write a detailed report into our log file in the catch block. We may take it a step further by defining the errorHandler method in the loaders field of the Bootstrap class, and implement the initErrorHandler method.

 

Easily loading code on demand


Here you will learn how to load code on demand by using the PSR-4 compatible autoloading strategy. We notify the autoloader where the classes are located to load them on demand.

Getting ready

If you are familiar with PHP, you've surely heard of PSR-0 and PSR-4 standards, which make it possible to load the required classes automatically at the moment of their call without using such instructions as require and include.

The behavior of Phalcon\Loader is based on the PHP's capability of autoloading classes; if any class used in code doesn't exist, a special handler will try to find and load it. Phalcon\Loader is designed just for that operation. The loading of only the files needed for a particular class to operate has a positive impact on the application's performance. It helps to avoid wasteful computation, and reduces program memory requirements. This technology is called Lazy Initialization.

As of October 21, 2014, PSR-0 has been marked as deprecated. PSR-4 is now recommended as an alternative. Starting from version 3.0.0, the support for prefixes strategy in Phalcon\Loader is removed from Phalcon. For that very reason, loading with the use of the PSR-0 standard will be omitted here.

How to do it…

Follow these steps to complete this recipe:

  1. Create an autoloader instance in the following way:

    define('APP_PATH', realpath(dirname(dirname(__DIR__))));
    
    $loader = new \Phalcon\Loader();
    $loader->registerNamespaces([
      'MyBlog\Models'      => APP_PATH . '/app/models/',
      'MyBlog\Controllers' => APP_PATH . '/app/controllers/',
      'MyBlog\Library'     => APP_PATH . '/engine/',
    ]);
    
    $loader->register();
  2. Now, after configuring the loader in the way shown in the earlier code instance, create the class MyBlog\Models\Users, located in app/models/Users.php:

    <?php
    
    namespace MyBlog\Models;
    
    use Phalcon\Mvc\Model;
    
    class User extends Model
    {
    
    }
  3. Edit MyBlog\Controllers\IndexController in app/controllers/IndexController.php:

    <?php
    
    namespace MyBlog\Controllers;
    
    use Phalcon\Mvc\Controller;
    
    class IndexController extends Controller
    {
    
    }
  4. Edit MyBlog\Library\SomeClass in the engine/SomeClass.php:

    <?php
    
    namespace MyBlog\Library;
    
    class SomeClass
    {
    
    }

How it works…

The Phalcon\Loader::registerNamespaces method gets an associative array, identifying which keys are namespace prefixes and their values are directories where the classes are located in. For instance, after configuring the loader as shown earlier, we can use the following class, MyBlog\Models\Users, located in app/models/Users.php, MyBlog\Controllers\IndexController in app/controllers/IndexController.php, and MyBlog\Library\SomeClass in engine/SomeClass.php.

Let's consider in depth how the class search is carried out when using the namespaces strategy. Let's assume that you have registered the MyBlog\Library namespace for the engine directory, and then called the class MyBlog\Library\Some\Example. After registration, the autoloader knows that the MyBlog\Library namespace belongs to the engine directory. Next, in the remaining part of the class name (\Some\Example), the namespace separator (\) will be replaced with the directory separator (/), and the file extension will be added. Eventually, this will result in forming the path engine/Some/Example.php. So, in such a simple and quite fast way, the class loading with the use of the namespace strategy is performed.

It is defined in the PSR-4 standard that the vendor/package pair can refer to any directory or even more. That's why you can easily register the same namespace to be served by several directories:

define('ROOT_PATH', realpath(dirname(dirname(__DIR__))));

$loader = new \Phalcon\Loader();

$loader->registerNamespaces([
  'Phalcon' => ROOT_PATH . '/app/library/Phalcon/',
]);

$loader->register();

We have used the Phalcon namespace here, regardless of the fact that it is already registered by the framework. There is no conflict here. Using the Phalcon namespace, PHP will try to find the class among the classes provided by the framework, and after failing to find it, it will try to find it in the directory app/library/Phalcon/.

Note that when registering a namespace, which already exists, and an identical class name in this namespace, you will not be able to call this class. For example, if you register the namespace as previously described, and create the Phalcon\Crypt class, located in app/library/Phalcon/Crypt.php, you will not be able to call it in the following way: $cryp = new Phalcon\Crypt(). This is due to the fact that the Phalcon PHP extension is initialized at the earlier stage and PHP knows already about the Phalcon\Crypt class.

But let's return to our Phalcon namespace, which we have registered before. Now, if we try to create a new class:

$myHelper = new Phalcon\MyHelper();

Phalcon will search for it in the file app/library/Phalcon/MyHelper.php. Just due to the fact that Phalcon\Loader uses the fully PSR-4 compatible autoloader, we have no problems with class autoloading when applying such libraries as Phalcon Incubator and Phalcon Developer Tools, using the Phalcon namespace.

Furthermore, Phalcon\Loader provides other class-loading strategies, which are not PSR-4 compatible. We'll consider them briefly later.

Phalcon\Loader, as with most autoloaders, provides class loading with the use of directories. This class loading strategy isn't PSR-4 compatible, but it is efficient, and in certain situations, adequate. The loading with the use of directories comes down to the enumeration of all possible directories in an attempt to search for your classes. The Phalcon\Loader::registerDirs method receives the array of directories, in which the search will perform:

define('ROOT_PATH', realpath(dirname(dirname(__DIR__))));

$loader = new \Phalcon\Loader();
$loader->registerDirs([
  ROOT_PATH . '/components/',
  ROOT_PATH . '/adapters/',
  ROOT_PATH . '/engine/',
]);

$loader->register();

We have told the autoloader that our classes are located in three directories: components, adapters, and engine. Note that this strategy is the slowest. Class registering with the use of directories means that, by calling any class, Phalcon will search through these directories to find a class with the same name as the required class. As our project grows, this type of search will have an impact on performance. When using this class loading strategy, it is important to be mindful of the order of your directories, because you could create two classes with the same name in two different directories. If there is one class named Example in each of the following directories, components and engine, then, by calling the class:

$myComponent = new Example();

The first found class will be used which is the one from the components directory.

The third option, which can help you register your classes with the use of the Phalcon\Loader component, is registering classes. This autoloading method is not PSR-4 compatible, but in some cases it is the fastest. This solution may be efficient when using strategies which don't allow for easy retrieval of the file using the namespace and the class directory. The following describes how we can register classes in this way:

define('ROOT_PATH', realpath(dirname(dirname(__DIR__))));
$loader = new \Phalcon\Loader();
$loader->registerClasses([
  ROOT_PATH . '/components/Awesome/Example.php',
  ROOT_PATH . '/adapters/Base/BaseAdaper.php'
]);
$loader->register();

The Phalcon\Loader:registerClasses method gets the array of files, among which the search will be performed. Here we have told the autoloader about two classes, Example and BaseAdapter, and specified the full path to them. For example, now when calling the class $example = new Example(), the class located in components/Awesome/Example.php will be used. Although this method is the fastest, your file list will grow significantly, and with it the time it takes for the search to be carried out, so it will reduce the performance.

The Phalcon\Loader component allows you to combine autoloading options. There is no reason why you shouldn't use them all together, if you needed:

define('ROOT_PATH', realpath(dirname(dirname(__DIR__))));
$loader = new \Phalcon\Loader();
$loader->registerNamespaces([
  'MyBlog\Models'      => ROOT_PATH . '/app/models/',
  'MyBlog\Controllers' => ROOT_PATH . '/app/controllers/',
  'MyBlog\Library'     => ROOT_PATH . '/engine/',
]);
$loader->registerDirs([
  ROOT_PATH . '/components/',
  ROOT_PATH . '/adapters/',
  ROOT_PATH . '/engine/',
]);
$loader->registerClasses([
  ROOT_PATH . '/vendor/awesome/plugins/Example.php',
  ROOT_PATH . '/adapters/Base/BaseAdaper.php'
]);
$loader->register();

In such a manner, we can register the namespaces, the directories, and the classes.

In summary, it should be mentioned that the strategies that are based on the namespaces are faster than those based on the directories. In some cases, the strategy based on the class registering is faster, but only if your project has a small number of classes. In a relatively large project, the fastest strategy is to register the namespaces.

Note

The loader is case-sensitive.

Finally, if the APC is enabled, it will be used for the requested file (and this file will be cached).

There's more…

For more information about Lazy Initialization, go to:

https://en.wikipedia.org/wiki/Lazy_initialization.

For more detailed information about the PSR-0 standard refer to:

http://www.php-fig.org/psr/psr-0/, about the PSR-4 standard refer to http://www.php-fig.org/psr/psr-4/.

 

Setting up your request entry point


Before you start handling requests, you need to configure your application work environment and prepare all the required components. In this recipe, you will learn what should be done before asking Phalcon to handle an incoming request.

Getting ready

To embark on this recipe, you need to have the web server Apache or Nginx + PHP-FPM installed. Besides this, you should be able to create or change the configuration of the virtual host for your web server and have the proper authority to do so.

How to do it…

Follow these steps to complete this recipe:

  1. Create the Nginx virtual host setting as follows:

    upstream backend {
      server unix:/var/run/php5-fpm.sock;
    }
    
    # redirect the request to the non-www domain
    # to choose which domain you prefer
    server {
      server_name www.mysite.com;
      return 301 $scheme://mysite.com$request_uri;
    }
    
    server {
      listen 80;
    
      server_name mysite.com;
    
      error_log  /var/log/nginx/mysite.error.log;
      access_log /var/log/nginx/mysite.access.log;
    
      index index.php;
      root /var/www/mysite/public;
    
      try_files $uri $uri/ @rewrite;
    
      location @rewrite {
        rewrite ^(.*)$ /index.php?_url=/$1 last;
      }
    
      location ~ \.php$ {
        try_files $uri =404;
    
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
    
        fastcgi_pass backend;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
        fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;
        fastcgi_param   HTTP_REFERER     $http_referer;
        # production | development | staging | testing
        fastcgi_param APP_ENV development;
        fastcgi_buffers 16 16k;
        fastcgi_buffer_size 32k;
    
        include fastcgi_params;
      }
    
      location ~* ^/(css|img|js|flv|swf|download)/(.+)$ {
        root /var/www/mysite/public;
      }
    
      location ~ /\. {
        return 403;
      }
    }
  2. If you have the Apache web server installed, create the virtual host configuration as follows:

    <VirtualHost *:80>
      ServerAdmin admin@example.host
      DocumentRoot "/var/www/mysite/public"
      DirectoryIndex index.php
      ServerName mysite.com
      ServerAlias www.mysite.com
    
      # production | development | staging | testing
      SetEnv APP_ENV development
    
      <Directory "/var/www/mysite/public">
        Options All
        AllowOverride All
        Allow from all
    
        RewriteEngine On
    
        RewriteCond %{REQUEST_FILENAME} !-d
        RewriteCond %{REQUEST_FILENAME} !-f
        RewriteRule ^((?s).*)$ index.php?_url=/$1 [QSA,L]
      </Directory>
    
      <Files .*>
        Order Deny,Allow
        Deny From All
      </Files>
    </VirtualHost>
  3. Open your bootstrap file and add the URL manager configuration to it:

    $di->set('url', function () {
        $url = new \Phalcon\Mvc\Url();
        $url->setBaseUri('/');
    
        return $url;
    });
  4. Then, add the primary routing configuration:

    $di->setShared('router', function () {
      $router = new \Phalcon\Mvc\Router();
    
      if (!isset($_GET['_url'])) {
        $router->setUriSource(Router::URI_SOURCE_SERVER_REQUEST_URI);
      }
    
      $router->removeExtraSlashes(true);
    
      $router->add(
          '/',
          [
            'controller' => 'index',
            'action'     => 'index'
          ]
      );
    
      return $router;
    });
  5. Create a controller, associating it with the routing map defined in the preceding code:

    use Phalcon\Mvc\Controller;
    
    class IndexController extends Controller
    {
      public function indexAction()
      {
        var_dump($_SERVER);
        return;
      }
    }

How it works…

We configure the virtual host for our web server. Then, we configure the component Phalcon\Mvc\Url. Note that, depending on the installed application root, there may emerge a need to define the base URL. For example, if the document root is /var/www/mysite, and your application is installed at /var/www/mysite/phalcon, then your base URI (baseUri) is /phalcon/. When using virtual hosts or if the application is installed in the root directory, the baseUri parameter is /. By default, Phalcon detects the necessary baseUri by itself, but in order to improve performance we recommend to specify it manually.

For creating URIs, the Phalcon\Mvc\Router component is used by default. Your application can run with the following default routing pattern:

/:controller/:action/:params.

Therefore, it's easy to create URIs on this model or by other rules, set in the routing map. When creating an object of the type Phalcon\Mvc\Router, you can tell it not to create the default routing. For that purpose, you need to pass false to the constructor:

$router = new \Phalcon\Mvc\Router(false)

Next, we create a router with the default configuration and add a pattern to it, which refers to the site root. By default, the current handling URI is taken from the variable $_GET['_url'], Phalcon is wired that way, as are the standard mod-rewrite rules. Here we use a little trick—we check up whether there is the _url ($_GET['_url']) key in the superglobal array $_GET. In case it is missing, the virtual host configuration doesn't redirect the type index.php?_url=. Thus, it's very easy to create a flexible component configuration.

Finally, we create a controller to test our routing with an action, which coincides with the added routing pattern.

There's more…

You can learn about the Apache setup instructions at http://httpd.apache.org/docs/, and the complete Nginx documentation at http://nginx.org/en/docs/. For the PHP-FPM configuration directives description, refer to http://php.net/manual/en/install.fpm.configuration.php.

For Phalcon installation notes, routing documentation, and generating URLs, refer to https://docs.phalconphp.com/.

 

Understanding the request life cycle


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.

Getting ready

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.

How to do it…

Follow these steps to complete this recipe:

  1. Open the application service configuration and create an event manager, which we will use during further steps:

    $em = new Phalcon\Events\Manager();
  2. 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;
    });
  3. 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 named RouterListener 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());
    }
  4. Create a ProductsController in the app/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,
            ]);
        }
    }
  5. 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>
  6. 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;
    });
  7. Then, we have to create the Event Listener. For this step, create an ActionListener class in the library 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));
      }
    }
  8. 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;
    });
  9. Finally, create the listener named ViewListener in the library 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

How it works...

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.

There's more…

You can see more detailed information about the Events Manager at the Phalcon documentation site:

https://docs.phalconphp.com/en/latest/reference/events.html

See also

  • The Easily loading code on demand recipe, in this chapter

  • The Setting up your request entry point recipe, in this chapter

About the Authors
  • Serghei Iakovlev

    Serghei Iakovlev has been working as a Ruby/PHP developer for over 7 years and has been specializing in web application development. As a supporter and advocate of Behavior Driven Development (BDD), he is also a member of the Codeception team. Serghei can be found neck deep in innovative technologies, which introduced him to Phalcon. He is responsible for the coordination of bug fixes, testing, developer tools, incubator, and supports other Phalcon-related projects. When hes not working, Serghei enjoys watching documentaries, science fiction movies, and sometimes plays Mahjong or Go.

    Browse publications by this author
  • David Schissler

    David Schissler was born and raised in Northern California. He received a significant portion of his education from Santa Rosa Junior College, Chico State University, and ultimately received a Bachelor of Computer Science from Sonoma State University. David started his career as a programmer working with VB.net before transitioning solely into web development. He is a longtime Linux user and is an avid supporter and user of completely open source development platforms, including, the firmware, operating system, programming languages, and text editors. David is a world traveler who has lived on three different continents for extended periods of time. He is quite comfortable living out of a single bag while developing web software, writing books, and traveling the world.

    Browse publications by this author
Latest Reviews (4 reviews total)
Very bad quality. Examples are not working, out of context or just simply confusing.
Didn't receive the book...
Great book! it's well written. I would recommend! Thank you.
Phalcon Cookbook
Unlock this book and the full library FREE for 7 days
Start now