There is a mind-boggling number of PHP components and frameworks. There are macro frameworks like Symfony and Laravel. There are micro frameworks like Silex and Slim. And there are legacy frameworks like CodeIgniter that were built long before modern PHP components existed. The modern PHP ecosystem is a veritable melting pot of code that helps us developers build amazing applications.
Unfortunately, older PHP frameworks were developed in isolation and do not share code with other PHP frameworks. If your project uses one of these older PHP frameworks, you’re stuck with the framework and must live inside the framework’s ecosystem. This centralized environment is OK if you are happy with the framework’s tools. However, what if you use the CodeIgniter framework but want to cherry-pick a helper library from the Symfony framework? You’re probably out of luck unless you write a one-off adapter specifically for your project.
What we’ve got here is a failure to communicate.
Cool Hand Luke
Do you see the problem? Frameworks created in isolation were not designed to communicate with other frameworks. This is extremely inefficient, both for developers (creativity is limited by framework choice) and for frameworks themselves (they re-invent code that already exists elsewhere). I have good news, though. The PHP community has evolved from a centralized framework model to a distributed ecosystem of efficient, interoperable, and specialized components.
Several PHP framework developers recognized this problem and began a
conversation at php|tek (a popular PHP conference) in 2009. They
discussed how to improve intraframework communication and efficiency.
Instead of writing a new and tightly coupled logging class, for example,
what if a PHP framework could share a decoupled logging class like
monolog? Instead of writing its own HTTP request and response classes,
what if a PHP framework could instead cherry-pick the excellent HTTP request
and response classes from the Symfony Framework’s symfony/httpfoundation
component? For this to work, PHP frameworks must speak a common language that allows them to
communicate and share with other frameworks. They need standards.
The PHP framework developers who serendipitously met at php|tek eventually created the PHP Framework Interop Group (PHP-FIG). The PHP-FIG is a group of PHP framework representatives who, according to the PHP-FIG website, “talk about the commonalities between our projects and find ways we can work together.” The PHP-FIG creates recommendations that PHP frameworks can voluntarily implement to improve communication and sharing with other frameworks.
The PHP-FIG is a self-appointed group of framework representatives. Its members are not elected, and they are not special in any way other than their willingness to improve the PHP community. Anyone can request membership. And anyone can submit feedback to PHP-FIG recommendations that are in the proposal process. Final PHP-FIG recommendations are typically adopted and implemented by many of the largest and most popular PHP frameworks. I highly encourage you to get involved with the PHP-FIG, if only to send feedback and help shape the future of your favorite PHP frameworks.
The PHP-FIG’s mission is framework interoperability. And framework interoperability means working together via interfaces, autoloading, and style.
PHP frameworks work together via shared interfaces. PHP interfaces allow frameworks to assume what methods are provided by third-party dependencies without worrying about how the dependencies implement the interface.
Refer to Chapter 2 for an in-depth explanation of PHP interfaces.
For example, a framework is happy to share a third-party logger object
assuming the shared logger object implements the
emergency(), alert(), critical(), error(), warning(),
notice(), info(), and debug() methods. Exactly how these methods
are implemented is irrelevant. Each framework cares only that the third-party
dependency does implement these methods.
Interfaces enable PHP developers to build, share, and use specialized components instead of monolithic frameworks.
PHP frameworks work together via autoloading. Autoloading is the process by which a PHP class is automatically located and loaded on-demand by the PHP interpreter during runtime.
Before PHP standards, PHP components and frameworks implemented their
own unique autoloaders using the magic \__autoload() method or the more
recent spl_autoload_register() method. This required us to
learn and use a unique autoloader for each component and framework.
Nowadays, most modern PHP components and frameworks are compatible with
a common autoloader standard. This means we can mix and match multiple
PHP components with only one autoloader.
PHP frameworks work together via code style. Your code style determines spacing, capitalization, and bracket placement (among other things). If PHP frameworks agree on a standard code style, PHP developers don’t need to learn a new style every time they use a new PHP framework. Instead, PHP framework code is immediately familiar. A standard code style also lowers the barrier for new project contributors, who can spend more time squashing bugs and less time learning an unfamiliar style.
Standard code style also improves our own projects. Every developer has a unique style with more than a few idiosyncrasies, and these become a problem when multiple developers work on the same codebase. A standard code style helps all team members immediately understand the same codebase regardless of its author.
PSR is an acronym for PHP standards recommendation. If you’ve recently read a PHP-related blog, you have probably seen the terms PSR-1, PSR-2, PSR-3, and so on. These are PHP-FIG recommendations. Their names begin with PSR- and end with a number. Each PHP-FIG recommendation solves a specific problem that is frequently encountered by most PHP frameworks. Instead of PHP frameworks continually re-solving the same problems, frameworks can instead adopt the PHP-FIG’s recommendations and build upon shared solutions.
The PHP-FIG has published five recommendations as of this book’s publication:
If you counted only four recommendations, you are correct. The PHP-FIG deprecated its first PSR-0 recommendation. This first recommendation was replaced by the newer PSR-4 recommendation.
Notice how the PHP-FIG recommendations coincide nicely with the three interoperability methods I mentioned earlier: interfaces, autoloading, and code style. This is not a coincidence.
I’m really excited about the PHP-FIG recommendations. They are the bedrock beneath the modern PHP ecosystem. They define the means with which PHP components and frameworks interoperate. I admit, PHP standards are not the most scintillating of topics, but they are (in my mind) prerequisite to understanding modern PHP.
If you want to write PHP code that is compatible with community standards, start with PSR-1. It’s the easiest PHP standard to use. It’s so easy, you’re probably already using it without even trying. PSR-1 provides simple guidelines that are easy to implement with minimal effort. The point of PSR-1 is to provide a baseline code style for participating PHP frameworks. You must satisfy these requirements to be compatible with PSR-1:
You must surround your PHP code with either the <?php ?>
or <?= ?> tags. You must not use any other PHP tag syntax.
All PHP files must be encoded with the UTF-8 character set without a byte order mark (BOM). This sounds complicated, but your text editor or IDE can do this for you automatically.
A single PHP file can either define symbols (a class, trait, function, constant, etc.) or perform an action that has side effects (e.g., create output or manipulate data). A PHP file should not do both. This is a simple task and requires only a little foresight and planning on your part.
Your PHP namespaces and classes must support the PSR-4 autoloader standard. All you have to do is choose appropriate names for your PHP symbols and make sure their definition files are in the expected location. We’ll chat about PSR-4 soon.
Your PHP class names must use the common CamelCase
format. This format is also called TitleCase. Examples are
CoffeeGrinder, CoffeeBean, and PourOver.
Your PHP constants must use all uppercase characters.
They may use underscores to separate words if necessary. Examples are
WOOT, LET_OUR_POWERS_COMBINE, and GREAT_SCOTT.
Your PHP method names must use the common camelCase
format. This means the method name’s first character is lowercase, and
the first letter of each subsequent word in the method name is
uppercase. Examples are phpIsAwesome, iLoveBacon, and
tennantIsMyFavoriteDoctor.
After you implement PSR-1, the next step is to implement PSR-2. The PSR-2 standard further defines PHP code style with stricter guidelines.
The PSR-2 code style is a godsend for PHP frameworks that have many contributors from around the world, all of whom bring their own unique style and preferences. A common strict code style lets developers write code that is easily and quickly understood by other contributors.
Unlike PSR-1, the PSR-2 recommendation contains stricter guidelines. Some of PSR-2’s guidelines may not be what you prefer. However, PSR-2 is the preferred code style of many popular PHP frameworks. You don’t have to use PSR-2, but doing so will drastically improve the ability for other developers to read, use, and contribute to your PHP code.
You should use the stricter PSR-2 code style. Even though I call it strict, it’s easy enough to write. Eventually it’ll become second nature. Also, there are tools available to automatically format existing PHP code into the PSR-2 style.
The PSR-2 code style requires that you implement the PSR-1 code style.
This is a hot topic that is typically divided into two camps. The first camp prefers to indent code with a single tab character. The second (and much cooler) camp prefers to indent code with several space characters. The PSR-2 recommendation says PHP code should be indented with four space characters.
From personal experience, space characters are better suited for indentation because a space is a definitive measure that largely renders the same in different code editors. A tab, however, can vary in width and renders differently in different code editors. Use four space characters to indent code to ensure the best visual continuity for your code.
Your PHP files must use Unix linefeed (LF) endings, must end with a
single blank line, and must not include a trailing ?> PHP tag. Each line
of code should not exceed 80 characters. Ultimately, each line of code
must not exceed 120 characters. Each line must not have trailing white space.
This sounds like a lot of work, but it’s really not. Most code editors can
automatically wrap code to a specific width, strip trailing whitespace,
and use Unix line endings. All of these should happen automatically with
little to no thought on your part.
Omitting the trailing ?> PHP tag was odd to me at first.
However, it is good practice to omit the closing tag to avoid
unexpected output errors. If you do include the ?> closing tag, and also a
blank line after the closing tag, the blank line is considered output
and can cause errors (e.g., when you set HTTP headers).
I know many PHP developers who type TRUE, FALSE, and NULL in
uppercase characters. If you do this, try to unlearn this practice and
instead use only lowercase characters from now on. The PSR-2 recommendation
says that you should type all PHP keywords in lowercase.
Each namespace declaration must be followed by one blank
line. Likewise, when you import or alias namespaces with the use
keyword, you must follow the block of use declarations with one blank
line. Here’s an example:
<?phpnamespaceMy\Component;useSymfony\Components\HttpFoundation\Request;useSymfony\Components\HttpFoundation\Response;classApp{// Class definition body}
Like indentation, class definition bracket placement is another topic
that attracts heated debate. Some prefer the opening bracket to reside
on the same line as the class name. Others prefer the opening bracket to
reside on a new line after the class name. The PSR-2 recommendation
says a class definition’s opening bracket must reside on a new line
immediately after the class definition name as shown in the following example. The class definition’s closing bracket must reside on a new
line after the end of the class definition body. This is probably what
you have been doing already so it’s not as big a deal. If your class extends
another class or implements an interface, the extends and implements keywords
must appear on the same line as the class name:
<?phpnamespaceMy\App;classAdministratorextendsUser{// Class definition body}
Method definition bracket placement is the same as class definition bracket placement. The method definition’s opening bracket resides on a new line immediately after the method name. The method definition’s closing bracket resides on a new line immediately after the method definition body. Pay close attention to the method arguments. The first parenthesis does not have a trailing space, and the last parenthesis does not have a preceding space. Each method argument (except the last) is followed immediately by a comma and one space character:
<?phpnamespaceAnimals;classStrawNeckedIbis{publicfunctionflapWings($numberOfTimes=3,$speed='fast'){// Method definition body}}
You must declare a visibility for
each class property and method. A visibility is one of public,
protected, or private; visibility determines how a property or method
is accessible within and outside of its class. Old-school PHP developers
may be accustomed to prefixing class properties with the var keyword
and prefixing private methods with the underscore _ character. Do not
do this. Use one of the visibilities listed previously instead. If you declare a
class property or method as abstract or final, the abstract and final
qualifiers must appear before the visibility. If you declare a property or
method as static, the static qualifier must appear after the visibility:
<?phpnamespaceAnimals;classStrawNeckedIbis{// Static property with visibilitypublicstatic$numberOfBirds=0;// Method with visibilitypublicfunction__construct(){static::$numberOfBirds++;}}
This is probably the one guideline that trips me up the most. All control structure keywords must be followed by a single space character.
A control structure keyword is if, elseif, else, switch,
case, while, do while, for, foreach, try, or catch.
If the control structure keyword requires a set of parentheses, make sure
the first parenthesis is not followed by a space character, and make
sure the last parenthesis is not preceded by a space character. Unlike in
class and method definitions, opening brackets that appear after a control
structure keyword must remain on the same line as the control structure keyword.
The control structure keyword’s closing bracket must reside on a new line.
Here’s a brief example that demonstrates these guidelines:
<?php$gorilla=new\Animals\Gorilla;$ibis=new\Animals\StrawNeckedIbis;if($gorilla->isAwake()===true){do{$gorilla->beatChest();}while($ibis->isAsleep()===true);$ibis->flyAway();}
You can automate PSR-1 and PSR-2 code style compatibility. Many
code editors automatically format your code according to PSR-1 and PSR-2.
There are tools available to help you audit and format your code
against PHP standards, too. One such tool is the PHP Code Sniffer,
also called phpcs. This tool (used directly on the command line or via your IDE)
reports inconsistencies between your code and a given PHP code standard. You
can install phpcs with most package managers (e.g., PEAR, Homebrew, Aptitude, or Yum).
You can also use Fabien Potencier’s PHP-CS-Fixer to correct most incompatibilities automatically. This tool is not perfect, but it’ll get you most of the way toward PSR compatibility with little or no effort on your part.
The third PHP-FIG recommendation is not a set of guidelines like its predecessors. PSR-3 is an interface, and it prescribes methods that can be implemented by PHP logger components.
A logger is an object that writes messages of varying importance
to a given output. Logged messages are used to diagnose, inspect, and
troubleshoot application operation, stability, and performance. Examples
include writing debug information to a text file during development,
capturing website traffic statistics into a database, or emailing fatal
error diagnostics to a website administrator. The most popular PHP
logger component is monolog/monolog,
created by Jordi Boggiano.
Many PHP frameworks implement logging in some capacity. Before the PHP-FIG, each framework solved logging differently, often with a proprietary implementation. In the spirit of interoperability and specialization—recurring motifs in modern PHP—the PHP-FIG established the PSR-3 logger interface. Frameworks that accept PSR-3 compatible loggers accomplish two important things: logging concerns are delegated to a third party, and end users can provide their preferred logger component. It’s a win-win for everyone.
A PHP logger component compatible with the PSR-3 recommendation must include
a PHP class that implements the interface named Psr\Log\LoggerInterface.
The PSR-3 interface replicates the RFC 5424 syslog protocol
and prescribes nine methods:
<?phpnamespacePsr\Log;interfaceLoggerInterface{publicfunctionemergency($message,array$context=array());publicfunctionalert($message,array$context=array());publicfunctioncritical($message,array$context=array());publicfunctionerror($message,array$context=array());publicfunctionwarning($message,array$context=array());publicfunctionnotice($message,array$context=array());publicfunctioninfo($message,array$context=array());publicfunctiondebug($message,array$context=array());publicfunctionlog($level,$message,array$context=array());}
Each interface method maps to a corresponding RFC 5424 protocol level and accepts
two arguments. The first $message argument must be a string or an object
with a __toString() method. The second $context argument is optional and
provides an array of placeholder values that replace tokens in the first argument.
Use the $context argument to construct complicated logger messages.
You use placeholders in the message text. A placeholder looks
like {placeholder_name}; it contains a {, the placeholder name,
and a }. A placeholder does not contain spaces. The $context argument
is an associative array; its keys are placeholder names (without brackets),
and its values replace the related placeholders in the message text.
To write a PSR-3 logger, create a new PHP class that implements the
Psr\Log\LoggerInterface interface and provide a concrete
implementation for each interface method.
If you are creating your own PSR-3 logger, stop and reconsider if you are spending your time wisely. I strongly discourage you from writing your own logger. Why? Because there are some truly amazing PHP logger components already available!
If you need a PSR-3 logger, just use monolog/monolog.
Don’t waste time looking elsewhere. The Monolog PHP component
fully implements the PSR-3 interface, and it’s easily extended with custom message
formatters and handlers. Monolog’s message handlers let you send log messages to
text files, syslog, email, HipChat, Slack, networked servers, remote APIs, databases,
and pretty much anywhere else you can imagine. In the very unlikely event Monolog
does not provide a handler for your desired output destination, it’s
super-easy to write and integrate your own Monolog message handler. Example 3-1 demonstrates how easy it is to setup Monolog and log messages to a text file.
<?phpuseMonolog\Logger;useMonolog\Handler\StreamHandler;// Prepare logger$log=newLogger('myApp');$log->pushHandler(newStreamHandler('logs/development.log',Logger::DEBUG));$log->pushHandler(newStreamHandler('logs/production.log',Logger::WARNING));// Use logger$log->debug('This is a debug message');$log->warning('This is a warning message');
The fourth PHP-FIG recommendation describes a standardized autoloader strategy. An autoloader is a strategy for finding a PHP class, interface, or trait and loading it into the PHP interpreter on-demand at runtime. PHP components and frameworks that support the PSR-4 autoloader standard can be located by and loaded into the PHP interpreter with only one autoloader. This is a big deal given the modern PHP ecosystem’s affinity for many interoperable components.
How often have you seen code like this at the top of your PHP files?
<?phpinclude'path/to/file1.php';include'path/to/file2.php';include'path/to/file3.php';
All too often, right? You’re probably familiar with the require(), require_once(), include(),
and include_once() functions. These functions load an external PHP file into the
current script, and they work wonderfully if you have only a few PHP scripts.
However, what if you need to include a hundred PHP scripts? What if you need to include a thousand PHP
scripts? The require() and include() functions do not scale well, and this is why
PHP autoloaders are important. An autoloader is a strategy for finding a PHP class,
interface, or trait and loading it into the PHP interpreter on-demand at runtime, without
explicitly including files as the example does.
Before the PHP-FIG introduced its PSR-4 recommendation, PHP component and framework authors
used the __autoload() and spl_autoload_register() functions to register custom
autoloader strategies. Unfortunately, each PHP component and framework used a unique
autoloader, and every autoloader used different logic to locate and load PHP classes,
interfaces, and traits. Developers using these components and frameworks were obliged
to invoke each component’s autoloader when bootstrapping a PHP application. I use Sensio Labs’
Twig template component all the time. It’s awesome. Without PSR-4, however, I have
to read Twig’s documentation and figure out how to register its custom autoloader
in my application’s bootstrap file, like this:
<?phprequire_once'/path/to/lib/Twig/Autoloader.php';Twig_Autoloader::register();
Imagine having to research and register unique autoloaders for every PHP component in your application. The PHP-FIG recognized this problem and proposed the PSR-4 autoloader recommendation to facilitate component interoperability. Thanks to PSR-4, we can autoload all of our application’s PHP components with only one autoloader. This is amazing. Most modern PHP components and frameworks are compatible with PSR-4. If you write and distribute your own components, make sure they are compatible with PSR-4, too! Participating components include Symfony, Doctrine, Monolog, Twig, Guzzle, SwiftMailer, PHPUnit, Carbon, and many others.
Like any PHP autoloader, PSR-4 describes a strategy to locate and load PHP classes, interfaces, and traits during runtime. The PSR-4 recommendation does not require you to change your code’s implementation. Instead, PSR-4 only suggests how your code is organized into filesystem directories and PHP namespaces. The PSR-4 autoloader strategy relies on PHP namespaces and filesystem directories to locate and load PHP classes, interfaces, and traits.
The essence of PSR-4 is mapping a top-level namespace prefix to a specific
filesystem directory. For example, I can tell PHP that classes, interfaces,
or traits beneath the \Oreilly\ModernPHP namespace live beneath the src/
physical filesystem directory. PHP now knows that any classes, interfaces, or traits
that use the \Oreilly\ModernPHP namespace prefix correspond to directories and files
beneath the src/ directory. For example, the \Oreilly\ModernPHP\Chapter1 namespace
corresponds to the src/Chapter1 directory, and the \Oreilly\ModernPHP\Chapter1\Example
class corresponds to the src/Chapter1/Example.php file.
PSR-4 lets you map a namespace prefix to a filesystem directory. The namespace prefix can be one top-level namespace. The namespace prefix can also be a top-level namespace and any number of subnamespaces. It’s quite flexible.
Remember when we talked about vendor namespaces in Chapter 2? The PSR-4 autoloader strategy is most relevant to component and framework authors who distribute code to other developers. A PHP component’s code lives beneath a unique vendor namespace, and the component’s author specifies which filesystem directory corresponds to the component’s vendor namespace—exactly as I demonstrated earlier. We’ll explore this concept more in Chapter 4.
We know that PSR-4 compatible code has a namespace prefix that maps to a base filesystem directory. We also know that subnamespaces beneath the namespace prefix map to subdirectories beneath the base filesystem directory. Example 3-2 shows an autoloader implementation, borrowed from the PHP-FIG website, that finds and loads classes, interfaces, and traits based on the PSR-4 autoloader strategy.
<?php/*** An example of a project-specific implementation.** After registering this autoload function with SPL, the following line* would cause the function to attempt to load the \Foo\Bar\Baz\Qux class* from /path/to/project/src/Baz/Qux.php:** new \Foo\Bar\Baz\Qux;** @param string $class The fully qualified class name.* @return void*/spl_autoload_register(function($class){// project-specific namespace prefix$prefix='Foo\\Bar\\';// base directory for the namespace prefix$base_dir=__DIR__.'/src/';// does the class use the namespace prefix?$len=strlen($prefix);if(strncmp($prefix,$class,$len)!==0){// no, move to the next registered autoloaderreturn;}// get the relative class name$relative_class=substr($class,$len);// replace the namespace prefix with the base directory, replace namespace// separators with directory separators in the relative class name, append// with .php$file=$base_dir.str_replace('\\','/',$relative_class).'.php';// if the file exists, require itif(file_exists($file)){require$file;}});
Copy and paste this into your application, change the $prefix and $base_dir
variables, and you have yourself a working PSR-4 autoloader. However,
if you find yourself writing your own PSR-4 autoloader, stop and ask
yourself if what you are doing is really necessary. Why? Because we
can use PSR-4 autoloaders that are automagically generated by the
Composer dependency manager. Conveniently enough, that’s exactly
what we’ll talk about next in Chapter 4.