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.
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.
Follow these steps to complete this recipe:
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();
Now, after configuring the loader in the way shown in the earlier code instance, create the class
MyBlog\Models\Users
, located inapp/models/Users.php
:<?php namespace MyBlog\Models; use Phalcon\Mvc\Model; class User extends Model { }
Edit
MyBlog\Controllers\IndexController
inapp/controllers/IndexController.php
:<?php namespace MyBlog\Controllers; use Phalcon\Mvc\Controller; class IndexController extends Controller { }
Edit
MyBlog\Library\SomeClass
in theengine/SomeClass.php
:<?php namespace MyBlog\Library; class SomeClass { }
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.
Finally, if the APC is enabled, it will be used for the requested file (and this file will be cached).
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/.