Standards - Good Practices - Modern PHP (2015)

Modern PHP (2015)

Part II. Good Practices

Chapter 3. Standards

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.

PHP-FIG to the Rescue

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’ssymfony/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.

NOTE

It is very important to understand the PHP-FIG provides recommendations. These are not rules. These are not requirements. These are carefully crafted suggestions that make our lives as PHP developers (and PHP framework authors) easier.

Framework Interoperability

The PHP-FIG’s mission is framework interoperability. And framework interoperability means working together via interfaces, autoloading, and style.

Interfaces

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.

NOTE

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.

Autoloading

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.

Style

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.

What Is a PSR?

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:

§ PSR-1: Basic code style

§ PSR-2: Strict code style

§ PSR-3: Logger interface

§ PSR-4: Autoloading

NOTE

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.

PSR-1: Basic Code Style

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:

PHP tags

You must surround your PHP code with either the <?php ?> or <?= ?> tags. You must not use any other PHP tag syntax.

Encoding

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.

Objective

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.

Autoloading

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.

Class names

Your PHP class names must use the common CamelCase format. This format is also called TitleCase. Examples are CoffeeGrinder, CoffeeBean, and PourOver.

Constant names

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.

Method names

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, andtennantIsMyFavoriteDoctor.

PSR-2: Strict Code Style

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.

TIP

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.

Implement PSR-1

The PSR-2 code style requires that you implement the PSR-1 code style.

Indentation

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.

TIP

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.

Files and lines

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.

TIP

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).

Keywords

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.

Namespaces

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:

<?php

namespace My\Component;

use Symfony\Components\HttpFoundation\Request;

use Symfony\Components\HttpFoundation\Response;

class App

{

// Class definition body

}

Classes

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:

<?php

namespace My\App;

class Administrator extends User

{

// Class definition body

}

Methods

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:

<?php

namespace Animals;

class StrawNeckedIbis

{

public function flapWings($numberOfTimes = 3, $speed = 'fast')

{

// Method definition body

}

}

Visibility

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 beforethe visibility. If you declare a property or method as static, the static qualifier must appear after the visibility:

<?php

namespace Animals;

class StrawNeckedIbis

{

// Static property with visibility

public static $numberOfBirds = 0;

// Method with visibility

public function __construct()

{

static::$numberOfBirds++;

}

}

Control structures

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();

}

TIP

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.

PSR-3: Logger Interface

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.

NOTE

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.

Write a PSR-3 Logger

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:

<?php

namespace Psr\Log;

interface LoggerInterface

{

public function emergency($message, array $context = array());

public function alert($message, array $context = array());

public function critical($message, array $context = array());

public function error($message, array $context = array());

public function warning($message, array $context = array());

public function notice($message, array $context = array());

public function info($message, array $context = array());

public function debug($message, array $context = array());

public function log($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.

TIP

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.

Use a PSR-3 Logger

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.

Example 3-1. Using Monolog

<?php

use Monolog\Logger;

use Monolog\Handler\StreamHandler;

// Prepare logger

$log = new Logger('myApp');

$log->pushHandler(new StreamHandler('logs/development.log', Logger::DEBUG));

$log->pushHandler(new StreamHandler('logs/production.log', Logger::WARNING));

// Use logger

$log->debug('This is a debug message');

$log->warning('This is a warning message');

PSR-4: Autoloaders

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.

Why Autoloaders Are Important

How often have you seen code like this at the top of your PHP files?

<?php

include '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:

<?php

require_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.

The PSR-4 Autoloader Strategy

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.

TIP

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.

How to Write a PSR-4 Autoloader (and Why You Shouldn’t)

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.

Example 3-2. PSR-4 autoloader

<?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 autoloader

return;

}

// 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 it

if (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.