Michael Dyrynda
Home Blog Podcasts
 
Automatically fixing code styles before you commit your code February 14th, 2016

Introduction

Before making any changes to the existing ConFOMO codebase, I thought it best to get an understanding of the style of the code I ought to be writing in order to make reviewing pull requests a quicker process. In addition, I took a look at some of Matt's other projects and came up with a set of PHP-CS-Fixer rules to ensure the style is enforced, with no additional effort on my part.

Configuring PHP-CS-Fixer

Once I had identified the necessary rules, I put together the necessary .php_cs configuration file.

1<?php
2 
3return Symfony\CS\Config\Config::create()
4 ->level(Symfony\CS\FixerInterface::PSR2_LEVEL)
5 ->fixers([
6 // Logical NOT operators (!) should have one trailing whitespace.
7 'logical_not_operators_with_successor_space',
8 // PHP multi-line arrays should have a trailing comma.
9 'multiline_array_trailing_comma',
10 // Ordering use statements (alphabetically).
11 'ordered_use',
12 // Remove line breaks between use statements.
13 'remove_lines_between_uses',
14 // An empty line feed should precede a return statement.
15 'return',
16 // PHP arrays should use the PHP 5.4 short-syntax.
17 'short_array_syntax',
18 // Cast "(boolean)" and "(integer)" should be written as "(bool)" and "(int)". "(double)" and "(real)" as "(float)".
19 'short_scalar_cast',
20 // PHP single-line arrays should not have a trailing comma.
21 'single_array_no_trailing_comma',
22 // Arrays should be formatted like function/method arguments, without leading or trailing single line space.
23 'trim_array_spaces',
24 // Unalign double arrow symbols.
25 'unalign_double_arrow',
26 // Unalign equals symbols.
27 'unalign_equals',
28 // Unused use statements must be removed.
29 'unused_use',
30]);

This uses the default PSR-2 rules, along with some additional rules I've picked up from review of existing codebases. Note that these rules apply specifically to the ConFOMO codebase, so you'll need to adapt them for your own projects.

Configuring a git pre-commit hook

A pre-commit hook is a simple bash script that will be executed when you run git commit, but before the operation is actually executed. This script will look for any PHP files that were added, copied, or modified files and run them through php-cs-fixer - assuming that it is installed globally and available in your $PATH.

Create the file .git/hooks/pre-commit in your project directory with the following content and make it executable with chmod g+x .git/hooks/pre-commit.

1#!/bin/bash
2 
3echo "Running PHP-CS-Fixer"
4 
5CONFIG_FILE=.php_cs
6 
7if [ ! -e $CONFIG_FILE ];
8then
9 echo "$CONFIG_FILE does not exist. Please configure php-cs-fixer."
10 exit 1
11fi
12 
13while read -r file;
14do
15 file="$(echo -e "${file:1}" | sed -e 's/^[[:space:]]*//')"
16 if [[ $file = *.php ]];
17 then
18 /usr/bin/env php-cs-fixer fix "$file"
19 git add "$file"
20 fi
21done < <(git diff --cached --name-status --diff-filter=ACM)

Sample files

Here's a sample PHP file before it was run through the pre-commit hook:

1<?php
2 
3namespace SomeNamespace;
4 
5// This file is actually named src/Testing.php
6// and src/ is PSR-4 mapped to SomeNamespace.
7class Testing {
8 /**
9 * @var boolean
10 */
11 private $property;
12 
13 public function __construct($property = false)
14 {
15 $this->property = $property;
16 
17 $singleLineArray = [ 'spaces', 'and', 'trailing', 'comma', 'should', 'be', 'trimmed', ];
18 
19 $multiLineArrray = [
20 'the' => 'double',
21 'arrow' => 'symbol',
22 'should' => 'not',
23 'be' => 'aligned',
24 ];
25 
26 $shortArrayConversion = array( 'change', 'this', 'to', 'short-array', 'syntax', );
27 
28 $logicalNotTest = !empty($singleLineArray);
29 
30 $boolean = TRUE;
31 
32 if ($boolean) {
33 echo "it's good";
34 } else if (!$boolean) {
35 echo "it's not good";
36 } else {
37 echo "This is pointless";
38 }
39 }
40}

And the same file after it was automatically run through the fixer:

1<?php
2 
3namespace SomeNamespace;
4 
5// This file is actually named src/Testing.php
6// and src/ is PSR-4 mapped to SomeNamespace.
7class Testing
8{
9 /**
10 * @var bool
11 */
12 private $property;
13 
14 public function __construct($property = false)
15 {
16 $this->property = $property;
17 
18 $singleLineArray = ['spaces', 'and', 'trailing', 'comma', 'should', 'be', 'trimmed'];
19 
20 $multiLineArrray = [
21 'the' => 'double',
22 'arrow' => 'symbol',
23 'should' => 'not',
24 'be' => 'aligned',
25 ];
26 
27 $shortArrayConversion = ['change', 'this', 'to', 'short-array', 'syntax'];
28 
29 $logicalNotTest = ! empty($singleLineArray);
30 
31 $boolean = true;
32 
33 if ($boolean) {
34 echo "it's good";
35 } elseif (! $boolean) {
36 echo "it's not good";
37 } else {
38 echo "This is pointless";
39 }
40 }
41}

Conclusion

In using the pre-commit hook, the rules as defined by the project have been automatically applied. This was done with only minor effort on my part. If the PHP project you're working on has a .php_cs file in its repository, it might be worth adding this pre-commit hook. Your open source containers will love you for it!

Insider information

Matt live-streamed his PR process, so I got a bit of information about his PR thought process and some of the conventions he follows in his own projects. Check it out if you've got a spare 40 minutes.

I'm a real developer ™
Michael Dyrynda

@michaeldyrynda

I am a software developer specialising in PHP and the Laravel Framework, and a freelancer, blogger, and podcaster by night.

Proudly hosted with Vultr

Syntax highlighting by Torchlight