Class Autoloading
Learn how to split the PHP code into multiple files and use class autoloading to join them together.
When our application is small enough, we can put all the source code into a single file. Unfortunately, the code grows fast, and quite soon, this file will become too large to work with effectively.
Including files with the include
and require
expressions
We can extract some pieces of code into separate files and inject them back in using the include
or require
expressions. The include
expression takes the contents of a file and evaluates it as if the file contents were in the middle of the code. The require
expression does the same, except it raises an error instead of a warning if the file is missing or inaccessible.
<?php// Injecting files a.php and b.php in two different waysinclude 'a.php';require 'b.php';// Code that used definitions from a.php and b.phpecho $a;echo $b;
Repeated include issue
We might get errors if we try to declare functions or classes in the included files. Imagine we have four files with a diamond-shape dependency structure.
<?php// Require two files with the same dependencies.// It will result in an error.require 'alice.php';require 'bob.php';
When we run main.php
, we expect to see greetings for Alice and Bob. In reality, we’ll see the error “Fatal error: Cannot redeclare hello().” We see it because we included the file hello.php
twice and defined the function hello()
each time.
To avoid this error, we can use the include_once
and require_once
expressions. They do the same thing as include
and require
, but they do nothing if the file was already included. If, in the example above, we replace every require
with require_once,
it will start working as expected.
<?php// Require two files with the same dependencies using require_once.// It will work.require_once 'alice.php';require_once 'bob.php';
Autoloading classes
With require_once
, we must explicitly include every dependency file in every file we have. Even worse, whenever we want to use an existing class, we must remember its location and provide a relative path from the current file to the dependency file. That’s a tedious job we’d like to avoid.
Fortunately, PHP has a mechanism of semiautomatic file inclusion called class autoloading. It allows us to register a function that gets a string with a class name and includes a file containing this class. PHP will call this function every time our code tries to use an undeclared class. With an autoloader, PHP loads all the classes we use automatically.
The best practice for storing source code in PHP is to wrap all the logic in classes and store every class in a separate file to make it easy to autoload them. Also, the class files shouldn’t have any code outside the class definition because we can’t predict when PHP will load our file and execute this code.
This is how the previous example would look with class autoloading:
<?php// Register autoloading functionspl_autoload_register(function (string $class) {include $class . '.php';});// Use classes defined in other files.// The files will be autoloaded based on the class name.Alice::greet();Bob::greet();
PSR-4: Standard recommendation for autoloading
We might want to make a perfect class autoloader for the project now. We shouldn’t do that. Class autoloading is a common task, so there are existing solutions we can and should use. This way, we can focus on project-specific logic.
In the PHP community, there’s a PHP Framework Interop Group that consists of those who maintain major PHP frameworks, applications, and tools. They create standard recommendations that make their projects work better together. Although sticking to these standards is unnecessary, we would benefit from it.
The most important of their standards is PSR-4. It tells us how to name files that depend on class names to support autoloading.
Specification
- Every class, interface, or trait must have a namespace.
- There should be a part at the beginning of a namespace called namespace prefix that corresponds to a base directory. 3/ Other namespace parts should correspond to subdirectories inside the base directory.
- The file name must be the terminal class name plus the
.php
extension.
Examples
PSR-4 Examples
Fully Qualified Class Name | Namespace Prefix | Base Directory | Resulting File Path |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Why it matters
By sticking to this standard, we can reuse existing class autoloaders so we don’t have to waste time recreating them.
In the next lesson, we’ll learn about the Composer tool and how to use it as a class autoloader.
Note: Before namespaces were common in PHP, there was a PSR-0 standard. This standard is almost the same as PSR-4 but treats underscores in the class name as directory separators. This way, the class
PHPUnit_Framework_TestCase
is located in the file PHPUnit/Framework/TestCase.php. This standard is deprecated, but the old code can still have such class names.
Conclusion
The include
and require
expressions are used to load PHP code located in other files. There are also the include_once
and require_once
expressions that are used to avoid loading a single file twice.
PHP lets us define functions to load class files automatically. We can use existing class autoloaders because of the PSR-4 standard. Itdefines each class in a separate file, which maps paths to the class namespace and name.