Prior to PHP Standards Recommendation (PSR), there were no truly uniformed standards for writing PHP code. For instance, for coding style, some people preferred Zend Framework Coding Standard, and some liked PEAR Coding Standards, and still others chose to create their own naming conventions and coding style.
A group of people, representing various popular PHP projects came together in 2009 and formed something called Framework Interoperability Group(FIG). The purpose of FIG is for project representatives to talk about the commonalities between their projects and find ways to work together.
At the time of this writing, there are six accepted PSRs: two of them are about autoloading, two of them are related to PHP coding style and the remaining are about interfaces.
In this chapter, we will discuss each PSR briefly. The purpose of this chapter is to introduce you to the ideas of PSRs. For further details on each one, the respective link are provided.
Both PSR-0 and PSR-4 are standards for autoloading. If you aren't familiar with autoloading, it is basically a way for PHP to include classes without writing cluttered include/require
statements everywhere.
Let's take a look at the history of autoloading. This will give you a clear picture how autoloading in PHP has involved during the years.
In PHP language, we have to make sure a class's definition is loaded prior to using it. Normally, we will create our PHP classes in their own class files for better organization. Then we will load them with require
or include
statements in the files they are being called.
include 'manager.php';
$manager = new Manager();
This approach quickly raises some issues. Imagine you have tens of external classes to be used in a file and you start writing lines of require/include
statements right at the beginning of a source file. They are ugly and clutter our codebase with repetitive lines of include statements.
Starting in PHP 5, a new magic function was introduced to solve this issue:
void __autoload ( string $class);
__autoload
is essentially a helper function, doing what we were doing with include statements. We can define this function anywhere in our codebase, and PHP will automatically use this function to load a class's file when an undefined class is called. This is the last chance to load a class definition before PHP fails with an error.
function __autoload($class)
{
$filename = 'classes/' . $class . '.php';
if (file_exists($filename)) {
include_once($filename);
}
}
$manager = new Manager();
__autoload
quickly became obsolete due to the fact that it can only allows one autoloader function. What this means is that since autoload
is the one and the only one magic that PHP engine will call, we need to define this particular magic function wherever we want an autoloading feature. Theoretically, this includes every file in a solid object-oriented codebase.
PHP 5.1.2 was shipped with another autoloading function( spl_autoload_register
) for coping with __autoload
's limitation. spl_autoload_register
is a replacement for __autoload
, and it provides more flexibility. It works by registering PHP user land functions with the autoload queue. It effectively creates a queue of autoload functions, and runs through each of them in the order in which they are defined. This means we can have multiple autoloader functions and there is no need for creating autoload
function in each one of our source files anymore.
Autoloading was such a great idea that every project started to use it. Inevitably everyone created their own version of autoloader as uniform standards were lacking. Clearly, PHP desperately needed a standard for autoloader, which is how PSR-0 was born.
Today, Autoloader standard has evolved significantly. Even PSR-0 is officially depreciated due to some constraints, such as its unfriendiness to Composer.
The latest accepted autoloader standard is PSR-4. You should follow PSR-4 to create your desired autoloader. For specification of PSR-4, please read more from its official page.
PSR-1 and PSR-2 are for PHP coding standards. PSR-1 focuses on the basics, whereas PSR-2 expands upon PSR-1 and provides a more comprehensive coding style guide.
PSR-1 lists a set of simple rules for naming conventions and file structures. Its main purpose is to ensure a high level of technical interoperability between shared PHP codes. In a project that is incorporated with various packages, it can be a mess if each uses different coding standard, which is what PRS-1 was designed to solve.
A quick overview of PSR-1:
Building on top of PSR-1, PSR-2 provides more comprehensive guidelines with more detailed rules as basic as code indention. It also covers varicose aspects of coding style, from naming conventions to namespace, classes, properties, methods, control structures and closures. It is possible to find any specification you need from PSR-2. Adapting your codebase to this standard for interoperability is highly encouraged.
After autoloading and coding standards, we can finally associate PSR with PHP code. These are PSR-3 and PSR-7. PSR-3 contains a logger interface and PSR-7 contains interfaces for HTTP message interfaces.
PSR-3
PHP desperately needed a standard for Logger interface before PSR-3. Logging is such a common task that every project built its own version of logger. Without a standard, the only way to use a third-party logger was to write a wrapper around it, so it could work with our existing codebases. It was not only a painful process, but also felt wrong, because after all, they were all doing the same type of work: logging. We should be able to switch them around.
PSR-3 provides common interface for logging libraries. As long as they implement the PSR-3 logger interface, they should be theoretically interchangeable with any other PSR-3 logger libraries.
Let's take a look how PSR-3 Logger interface improves our code reusability in a concrete example.
Suppose we have written a simple authentication class User below. It appends an audit message to a log file once an user logins in successfully. It is using our custom logger class, which exposes a single method addMessage
.
class User
{
private $logger;
public function __construct($logger)
{
$this->logger = $logger;
}
public function login($username, $password)
{
if ($this->validUsernameAndPassword($username, $password)) {
$this->logger->addMessage('login');
}
}
private function validUsernameAndPassword($username, $password)
{
// ... ...
}
}
Our custom Logger class is injected to User class following dependency injection principle, this seems to make our User reusable. We can switch to other logger class simply via the constructor. But if we take a look closely, we can't do that, User class is still highly coupled to our custom Logger class, it is aware of custom method addMessage
. If we use another third-party logger library in our code, it won't work, because they do not have a method called addMessage
.
We can modify our code to use PSR-3 Logger interface instead. According to Dependency Inversion principle SOLID, we should depend upon abstractions rather than concretions. PSR-3 Logger interface provides a perfect abstraction for our case.
class User
{
private $logger;
public function __construct(PsrLogLoggerInterface $logger)
{
$this->logger = $logger;
}
public function login($username, $password)
{
if ($this->validUsernameAndPassword($username, $password)) {
$this->logger->info('login');
}
}
private function validUsernameAndPassword($username, $password)
{
// ... ...
}
}
By changing a few lines of our code, we have replaced our custom logger with PsrLogLoggerInterface. Now our code is highly reusable. We can use, switch to, or change to, any third-party logger library that is compliant with with PSR-3 Logger interface.
PSR-7
HTTP messages are essential for web applications. Every action a user takes is a combination of a HTTP request and HTTP response. PSR-7 is the latest accepted standard. It provides abstractions around HTTP messages and the elements composing them. It will have a huge impact on projects that implement details of HTTP messages, since HTTP is a rather complex subject and most of vendors have their own implementation, it is a lot of refactoring for vendors to adapt PSR-7.
As a user of HTTP messages, we can now deal with HTTP messages universally thanks to PSR-7. Similar to PSR-3, PSR-7 makes our lives much easier to build a reusable codebase.
We have summarized each PSR briefly and you should now have an understanding of what each PSR is for.
You should refer to the official page whenever you need detailed specifications of each PSR.
Hopefully this simple tutorial helped you with your development. If you like our post, please follow us on Twitter and help spread the word. We need your support to continue. Did we miss out anything? Do leave a comment below to let us know.