PHP coding standards in practice

Coding standards are guidelines about programming style and practices developers should follow when writing code. They usually cover indentation, white space, comments, naming conventions, etc. Moreover, coding standards are very important for producing consistent, readable and maintainable code, especially if you are part of a team. Remember the countless argues about tabs vs spaces? Well, that’s history if you and your team agree to use a standard.

The PHP-FIG has defined the PSR-1 and PSR-2 coding style guides. A few more like Symfony, Zend, Squiz also exist, so you can choose whatever suits you better and start reading the specification. Fortunately, you don’t have to remember all those rules, since there are tools, like the PHP-CS-Fixer, that can validate and fix your code. Even better, you can create a pre-commit hook that does it automatically, before you commit your changes to your repository.

PHP Coding Standards Fixer

The PHP-CS-Fixer has been written by Fabien Potencier, the creator of the Symfony framework, and Dariusz Rumiński. It’s open source and released under the MIT license. You can download it from cs.sensiolabs.org or install it via Composer, Homebrew.

Requirements

PHP 5.6.0 or greater

Installation

Global (Composer)

composer global require friendsofphp/php-cs-fixer

Global (Homebrew)

brew install homebrew/php/php-cs-fixer

In the vendor directory (Composer)

composer require — dev friendsofphp/php-cs-fixer

Basic Commands

If you used the installation in the vendor directory, the path to the fixer is ./vendor/friendsofphp/php-cs-fixer/php-cs-fixer. If you chose the global installation, running php-cs-fixer should be enough. To run the fixer without writing php at the beginning of the command, set its execute permissions with chmod +x path/to/php-cs-fixer.

General help: php-cs-fixer help

Help for a given command: php-cs-fixer help <command>

Help for a given ruleset (e.g. Symfony): php-cs-fixer describe @Symfony

Help for a given rule (e.g. yoda_style): php-cs-fixer describe yoda_style

Use the PSR-2 rules and output the changes, without applying them: php-cs-fixer fix path/to/dir_or_file — rules=@PSR2 — dry-run — diff — using-cache=no

Use the Symfony rules and output the changes, without applying them: php-cs-fixer fix path/to/dir_or_file — rules=@Symfony — dry-run — diff — using-cache=no

Usage Example

Let’s start with fixing the very simple class Str. Below you can see the project’s structure

├── composer.json
├── composer.lock
├── src
│ └── Str.php
└── vendor
 ├── autoload.php
 ├── bin
 ├── composer
 ├── doctrine
 ├── friendsofphp
 ├── gecko-packages
 ├── paragonie
 ├── php-cs-fixer
 └── symfony

and its code, before the fixes

<?php
namespace App;
class Str {
       private $str;
       public function __construct(string $str) {
               $this->str = $str;
       }
       public function concat($str) {
               return new static($this->str . ' ' . $str);
       }
       public function __toString() {
               return $this->str;
       }
}

Step-by-step Fixes

Step 1: Test what the tool can fix by applying the PSR-2 and the Symfony standards. The —-dry-run flag will run the fixer without making changes to your files. The —-diff flag will be used to let the fixer output all the changes it makes.

php-cs-fixer fix src — rules=@PSR2,@Symfony — dry-run — diff — verbose — using-cache=no

Loaded config default.
F
Legend: ?-unknown, I-invalid file syntax, file ignored, S-Skipped, .-no changes, F-fixed, E-error
   1) src//Str.php (blank_line_after_opening_tag, class_definition,
   no_whitespace_in_blank_line, blank_line_after_namespace, braces)
      ---------- begin diff ----------
--- Original
+++ New
@@ @@
-<?php namespace App;
-class Str {
-        private $str;
-
-        public function __construct(string $str) {
-                $this->str = $str;
-        }
+<?php
-        public function concat($str) {
-                return new static($this->str . ' ' . $str);
-        }
+namespace App;
-        public function __toString() {
-                return $this->str;
-        }
+class Str
+{
+    private $str;
+
+    public function __construct(string $str)
+    {
+        $this->str = $str;
+    }
+
+    public function concat($str)
+    {
+        return new static($this->str . ' ' . $str);
+    }
+
+    public function __toString()
+    {
+        return $this->str;
+    }
 }
      ----------- end diff -----------
Checked all files in 0.073 seconds, 10.000 MB memory used

Let’s decode the output:

F
Legend: ?-unknown, I-invalid file syntax, file ignored, S-Skipped, .-no changes, F-fixed, E-error
1) src//Str.php (blank_line_after_opening_tag, class_definition,
no_whitespace_in_blank_line, blank_line_after_namespace, braces)

The F means that the tool can fix the code. The rules it will use are the following: blank_line_after_opening_tag, class_definition, no_whitespace_in_blank_line, blank_line_after_namespace, braces.

You can skip any rule by adding a dash before its name. For example, to skip the braces rule, write the following command:

./vendor/friendsofphp/php-cs-fixer/php-cs-fixer fix src — rules=@PSR2,@Symfony,-braces — dry-run — diff — verbose — using-cache=no

Step 2: Remove the —-dry-run flag and let the tool do its job.

./vendor/friendsofphp/php-cs-fixer/php-cs-fixer fix src rules=@PSR2,@Symfony —-verbose —-using-cache=no

Loaded config default.
F
Legend: ?-unknown, I-invalid file syntax, file ignored, S-Skipped, .-no changes, F-fixed, E-error
   1) php-cs-fixer-project/src//Str.php (blank_line_after_opening_tag,
   class_definition, no_whitespace_in_blank_line, blank_line_after_namespace, braces)
Fixed all files in 0.084 seconds, 10.000 MB memory used

…and Voilà!

<?php
namespace App;
class Str
{
    private $str;

    public function __construct(string $str)
    {
        $this->str = $str;
    }

    public function concat($str)
    {
        return new static($this->str . ' ' . $str);
    }​

    public function __toString()
    {
        return $this->str;
    }
}

Not bad, right? But, how can you share the same rules with the rest of your team? It’s pretty simple. Create a config file named .php_cs in your project’s root directory, put the rules in it and execute the php-cs-fixer using the —-config=.php_cs option, like the following example. php-cs-fixer fix —-config=.php_cs

Here is a config you can use. It requires the --allow-risky=yes option for applying some rules, so feel free to adapt it to your needs.

<?php
$finder = PhpCsFixer\Finder::create()
    ->exclude('vendor')
    ->in(__DIR__);

return PhpCsFixer\Config::create()
    ->setRules([
        '@Symfony' => true,
        'array_syntax' => ['syntax' => 'short'],
        'align_multiline_comment' => [
            'comment_type' => 'all_multiline',
        ],
        'cast_spaces' => ['space' => 'none'],
        'concat_space' => ['spacing' => 'one'],
        'is_null' => ['use_yoda_style' => false],
        'no_useless_return' => true,
        'not_operator_with_space' => false,
        'ordered_class_elements' => true,
        'ordered_imports' => true,
        'yoda_style' => [
            'equal' => false,
            'identical' => false,
            'less_and_greater' => false
        ],
        'full_opening_tag' => false,
        'php_unit_construct' => true,
        'php_unit_strict' => true,
        'phpdoc_order' => true,
    ])
    ->setUsingCache(false)
    ->setFinder($finder);

Wrap Up

  1. Use a coding style guide, especially when you are member of a team
  2. Use PHP-CS-Fixer to fix your code automatically

Leave a Reply

Your email address will not be published. Required fields are marked *