In a previous lesson here on Nettuts+, you learn about PSR; however, that article didn't detail the process of integrating that coding style into your projects. Let's fix that!
Note: this article assumes that you've read PSR-Huh?, and understand what PSR refers to. Let's begin with the first standard: PSR-0.
PSR-0 - The Autoloading Standard
The PHPCS plugin is the most helpful tool I've used.
In the past, we included PHP files in one of two ways:
- Using a giant block of include statements at the top of each file.
- List all includes in a single file and include that single file within your project.
There are pros and cons to both of these approaches, but, I think we can all agree that neither are optimal or modern solutions. PHP5 introduced the concept of autoloading files based on their class names; so, PSR-0 aims to keep filenames consistent.
Namespaces have nothing to do with filenames or autoloading; you can technically declare different namespaces in the same file. For example, the following code is perfectly valid.
<?php namespace Nettuts; Class Hello { public function __construct() { echo "Nettuts+"; } } namespace Gabriel; Class Hello { public function __construct() { echo "Gabriel"; } } $h = new \Nettuts\Hello(); $h = new \Gabriel\Hello();
There are two Hello
classes in this single file, but they reside within different namespaces. The final two lines of this code instantiate the Hello()
classes on their respective namespaces. The first outputs "Nettuts+", while the second echos "Gabriel." Namespaces allow you to differentiate between two classes with the same name, much like you might be used to with folders on your desktop. The PSR-0 standard simply leverages the benefits of namespaces, making it easy to autoload your classes. By consistently naming your files, you can create a function that locates the necessary files automatically.
To be PSR-1 compliant, you also must follow PSR-0.
Be sure to read the full standard, but to summarize:
- Each class must be namespaced with the project's (or creator's) name.
- Underscores within the class' name should be converted to directory separators.
- Files must have the
.php
extension.
For example, a class reference of:
\Nettuts\Database\SQL_Postgres
if following PSR-0, should translate to this path:
./Nettuts/Database/SQL/Postgres.php
How might we implement this functionality? The most obvious solution is to use Composer, which ships with a PSR-0 compliant autoloader. If you leverage Composer in your projects (and you should), then opt for its autoloader, rather than writing your own.
A PSR-0 compliant loader allows you to specify a base path, informing the loader which directory to look in first. To get started, create a simple composer.json
file that contains the following JSON:
{ "autoload": { "psr-0": { "Nettuts": "./", "Gmanricks": "vendor/" } } }
This JSON file tells Composer that we want to use the PSR-0 standard to autoload all Nettuts
-namespaced files with the current directory (the root folder) as the base path. We also want to autoload all classes with the Gmanricks
namespace, relative to the vendor
folder (e.g. ./vendor/Gmanricks/ClassName
).
Now, type "composer install
" to generate the autoload classes, or "composer dump-autoload
" on subsequent edits to regenerate the autoload classes. Also, don't forget to require the autoloader somewhere early in your project.
<?php require 'vendor/autoload.php';
Composer is your best option, but there may be scenarios when you want a small, simple autoloader. The PHP-FIG provides a sample autoloader that you can use:
function __autoload($className) { $className = ltrim($className, '\\'); $fileName = ''; $namespace = ''; if ($lastNsPos = strrpos($className, '\\')) { $namespace = substr($className, 0, $lastNsPos); $className = substr($className, $lastNsPos + 1); $fileName = str_replace('\\', DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR; } $fileName .= str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php'; require $fileName; }
It's important to note that this loader attempts to load all classes using the PSR standard in the current directory.
Now that we're successfully autoloading classes, let's move on to the next standard: the basic coding standard.
PSR-1 - The Basic Coding Standard
PSR-1 defines general coding guidelines, which can be divided into two parts.
Naming Conventions
Namespaces allow you to differentiate between two classes with the same name.
As with any programming language, following naming conventions ultimately makes your code easier to read and maintain. Here's a few rules to follow:
- Class names use PascalCase.
- Method names should be in camelCase.
- Constants require all capital letters, separating each word with an underscore (e.g.
CONSTANT_VARIABLE
).
Code Conventions:
There's more to it than naming conventions; follow these guidelines as well:
- Only use
<?php
or<?=
in your code. Don't close PHP within a class. - Files should either declare symbols or use them.
- Files must be in UTF-8 format without BOM for PHP code
Most of these are self-explanatory, but the middle convention is slightly confusing. It essentially dictates that any declaration, be it functions, classes, etc., should be separated into their own files. This not only promotes best practices like code-reuse and separation, but it keeps your code neat and clean.
It's worth mentioning that each PSR standard builds upon the previous PSR standard. As such, to be PSR-1 compliant, you also must follow PSR-0. By following these two standards, your code will be properly namespaced and autoloaded. There really isn't a reason not to follow them.
Yes, some developers complain about PSR and prefer to follow other conventions, but by following this standard, you can share code with everyone without worrying about its consistency. Having said that, nobody is forcing your hand here. It's simply a recommended guideline.
The next standard, PSR-2, dives into the specifics of how you should structure your code.
PSR-2 - The Advanced Coding Standard
PSR-2 dives into the specifics of how you should structure your code.
Next, we come to the one standard that PHP developers struggle with most; in fact, it's the reason why I chose to write this article.
PSR-2 defines many rules, many of which are listed below:
- Four spaces should be used instead of tabs.
- The ideal line length should be under 80 characters, but a soft limit of 120 characters should be imposed on all lines.
- There should be one blank line under the
namespace
anduse
declarations. - A method's or class' opening brace must be on its own line.
- A method's or class' closing brace must go on the line immediately after the body.
- All properties and methods require a visibility level.
- The '
abstract
' / 'final
' keywords should appear before the visibility while 'static
' goes after. - Control structure keywords must be followed by one space.
- A control statement's opening brace should appear on the same line as the statement.
Be sure to view the entire spec for a complete overview.
PSR-2 is just as important as PSR-1 (and PSR-0). It intends to make code easy to read and maintain. But, as they say, "The devil is in the details." There are a lot of details to remember, which can be difficult if your programming habits differ from what the standard defines. Thankfully, if you're on board, there are tools that help you adhere to PSR-0, PSR-1 and PSR-2. Perhaps the best tool is the Sublime Text plugin, PHPCS.
PHPCS - PHP Code Sniffer
The PHPCS plugin is the most helpful tool I've used, when it comes to getting code into shape. It allows you to not only ensure that your code follows the PSR standards, but it also uses PHP's linter to check for syntax errors. This is a great time saver; you no longer have to worry about syntax errors when you test your code in the browser.
Install the package through Sublime Package Control (it's called Phpcs), or, alternatively, with Git, using the following commands:
cd ~/Library/Application\ Support/Sublime\ Text\ 2/Packages/ git clone git://github.com/benmatselby/sublime-phpcs.git Phpcs
This installs the plugin, but you need a few dependencies before you can configure PHPCS. Once again, the easiest way to install them is with Composer. Browse to a directory of your choice and create a composer.json
file with the following JSON:
{ "name": "Nettuts PHPCS Demo", "require": { "squizlabs/php_codesniffer": "*", "fabpot/php-cs-fixer": "*", "phpmd/phpmd": "*" } }
This installs the three dependencies into the current folder. Open a terminal window to your installation location and type composer install
, and it will download the necessary packages.
Now you can configure the plugin in Sublime Text. Navigate to 'Preferences' > 'Package Settings' > 'PHP Code Sniffer' > 'Settings - User'.
The plugin needs to know where the three dependencies reside, as well as the standard that we want our code to adhere to:
{ "phpcs_additional_args": { "--standard": "PSR2", "-n": "" }, "phpcs_executable_path": "DEPENDENCY_PATH/vendor/bin/phpcs", "phpmd_executable_path": "DEPENDENCY_PATH/vendor/bin/phpmd", "php_cs_fixer_executable_path": "DEPENDENCY_PATH/vendor/bin/php-cs-fixer" }
These settings inform PHPCS that we want to ahere to the PSR2 standard and provide each dependency's path. Don't forget to replace DEPENDENCY_PATH
with your actual path.
Restart Sublime, and the code sniffer will scan your code when you save your PHP files.
Right-clicking in the editor will also list several new options, such as clearing error marks and attempting to fix the non-standard issues. However, considering that the point of this article is to get you used to the standard, I suggest manually fixing your code and avoiding the automatic fixer feature.
Conclusion
The PSR standards were created so that code could easily be reused from project to project, without sacrificing on code style consistency. While they may feel overwhelming at first, you can use the ideas and tools from this article to help you make the transition.
To reiterate one last time: nobody is forcing you to change the way that you code in PHP. It's simply a guide, originally meant for framework interoperability. That said, at Nettuts+, we consider it a best practice to follow. Now make up your own mind! If you have any questions, let's hear them below!
Comments