A web application consists of many PHP classes, and each class typically resides in a separate file. This introduces the need of including the files.
For example, let's assume we have the file named Application.php
which contains the definition for the \Zend\Mvc\Application
class
from the previous section. Before you can
create an instance of the Application
class somewhere in your code,
you have to include the contents of Application.php file (you can do this with the
help of require_once
statement, passing it the full path to the file):
<?php
require_once "/path/to/zendframework/zend-mvc/src/Application.php";
use Zend\Mvc\Application;
$application = new Application();
As your application grows in size, it may be difficult to include each needed file. Zend Framework 3 itself consists of hundreds of files, and it can be very difficult to load the entire library and all its dependencies this way. Moreover, when executing the resulting code, PHP interpreter will take CPU time to process each included file, even if you don't create an instance of its class.
To fix this problem, in PHP, the class autoloading feature has been introduced.
The PHP function spl_autoload_register()
allows you to register
an autoloader function. For complex websites, you even can create
several autoloader functions, which are chained in a stack.
During script execution, if PHP interpreter encounters a class name which has not been defined yet, it calls all the registered autoloader functions in turn, until either the autoloader function includes the class or "not found" error is raised. This allows for "lazy" loading, when PHP interpreter processes the class definition only at the moment of class invocation, when it is really needed.
To give you an idea of how an autoloader function looks like, below we provide a simplified implementation of an autoloader function:
<?php
// Autoloader function.
function autoloadFunc($className)
{
// Class map static array.
static $classMap = [
'\\Zend\\Mvc\\Application' => '/path/to/zendframework/zend-mvc/src/Zend/Mvc/Application.php',
'\\Application\\Module' => '/path/to/app/dir/module/Application/Module.php',
//...
];
// Check if such a class name presents in the class map.
if(isset(static::$classMap[$className])) {
$fileName = static::$classMap[$className];
// Check if file exists and is readable.
if (is_readable($fileName)) {
// Include the file.
require $fileName;
}
}
}
// Register our autoloader function.
spl_autoload_register("autoloadFunc");
In the above example, we define the autoloadFunc()
autoloader function,
which we will further refer to as the class map autoloader.
The class map autoloader uses the class map for mapping between class name and absolute path to PHP file containing that class. The class map is just a usual PHP array containing keys and values. To determine the file path by class name, the class map autoloader just needs to fetch the value from the class map array. It is obvious, that the class map autoloader works very fast. However, the disadvantage of it is that you have to maintain the class map and update it each time you add a new class to your program.
Because each library's vendor uses its own code naming and file organization conventions, you will have to register a different custom autoloader function per each dependent library, which is rather annoying (and actually this is an unneeded work). To resolve this problem, the PSR-4 standard was introduced.
PSR stands for PHP Standards Recommendation.
The PSR-4 standard defines the recommended code structure that an application or library must follow to guarantee autoloader interoperability. In two words, the standard says that:
The class namespaces should be organized in the following way:
\<Vendor Name>\(<Namespace>)*\<Class Name>
Namespaces can have as many nesting levels as desired, but the Vendor Name should be the top-level namespace.
Namespaces should map to directory structure. Each namespace separator ('\')
is converted to a OS-specific DIRECTORY_SEPARATOR
constant when loading from the file system.
The class name is suffixed with .php extension when loading the file from the file system.
For example, for the Zend\Mvc\Application
class,
you will have the following directory structure:
/path/to/zendframework/zend-mvc/src
/Zend
/Mvc
Application.php
The disadvantage of this is that you need to put your code in multiple nested directories (Zend and Mvc).
To fix this, the PSR-4 allows you to define that a contiguous
series of one or more leading namespace and sub-namespace names corresponds to a "base directory".
For example, if you have the \Zend\Mvc\Application
fully qualified class name, and if you define that
the series \Zend\Mvc
corresponds to the "/path/to/zendframework/zend-mvc/src" directory, you can organise
your files as follows:
/path/to/zendframework/zend-mvc/src
Application.php
For the code conforming to the PSR-4 standard, we can write and register an autoloader, which we will refer to as the "standard" autoloader:
<?php
// "Standard" autoloader function.
function standardAutoloadFunc($className)
{
// Replace the namespace prefix with base directory.
$prefix = '\\Zend\\Mvc';
$baseDir = '/path/to/zendframework/zend-mvc/src/';
if (substr($className, 0, strlen($prefix)) == $prefix) {
$className = substr($className, strlen($prefix)+1);
$className = $baseDir . $className;
}
// Replace namespace separators in class name with directory separators.
$className = str_replace('\\', DIRECTORY_SEPARATOR, $className);
// Add the .php extension.
$fileName = $className . ".php";
// Check if file exists and is readable.
if (is_readable($fileName)) {
// Include the file.
require $fileName;
}
}
// Register the autoloader function.
spl_autoload_register("standardAutoloadFunc");
The standard autoloader works as follows. Assuming that the class namespace
can be mapped to the directory structure one-by-one, the function calculates
the path to PHP file by transforming back-slashes (namespace separators) to
forward slashes (path separators) and concatenating the resulting path with
the absolute path to the directory where the library is located. Then the
function checks if such a PHP file really exists, and if so, includes it
with the require
statement.
It is obvious, that the standard autoloader works slower than the class map autoloader. However, its advantage is that you don't need to maintain any class map, which is very convenient when you develop new code and add new classes to your application.
Zend Framework 3 conforms to PSR-4 standard, making it possible to use standard autoloading mechanism across all its components. It is also compatible with other PSR-4 conforming libraries like Doctrine or Symfony.
Composer can generate autoloader functions (both class map autoloaders and PSR-4 standard autoloaders) for the code you install with it.
Zend Framework 3 uses the autoloader implementation provided by Composer. When you install a package with
Composer, it automatically creates the file APP_DIR/vendor/autoload.php,
which uses the spl_autoload_register()
PHP function to register an autoloader. This way all PHP classes
located in APP_DIR/vendor
directory are correctly autoloaded.
To autoload PHP classes located in your own modules (like Application
module), you'll have to specify
the autoload
key in your composer.json
file:
"autoload": {
"psr-4": {
"Application\\": "module/Application/src/"
}
},
Then the only thing need to be done is to include that file in your website entry script index.php
:
// Composer autoloading
include __DIR__ . '/../vendor/autoload.php';
The autoload.php file is generated each time you install a package with Composer. Besides that, to make Composer generate the autoload.php file, you may need to execute the
dump-autoload
command:
php composer.phar dump-autoload
In Zend Skeleton Application, you can see how the PSR-4 standard is applied
in practice. For the default module of your website, the Application
module, PHP classes which
are registered with the standard autoloader are stored under the APP_DIR/module/Application/src
directory ("src" abbreviation means "source").
We will refer to the
src
directory as module's source directory.
For example, lets look at the IndexController.php
file of Application
module (figure 3.2).
As you can see, it contains the IndexController
class 1 belonging to Application\Controller
namespace.
To be able to follow the PSR-4 standard and use the standard autoloader with this PHP class,
we have to put it under the Controller
directory under the module's source
directory.
1) IndexController
class is the default controller for the skeleton website.
We will talk about controllers later in chapter Model-View-Controller.