Using PHP-CS-Fixer to fix up your PHP code

Posted on November 12, 2014 | By Matt Stauffer

Warning: This post is over a year old. I don't always update old posts with new information, so some of this information may be out of date.

Every time someone mentions Confomo, I’m inspired to work on it for a day or two. And then I let it slide. And then I forget about it for weeks.

This time around, I decided to clean it up a bit according to my most recent coding standards. I’ve been on a PSR-2 kick lately, so I figured why not finally try out fabpot’s PHP-CS-Fixer.

Note: I wrote this post Friday, and was just getting around to editing and posting it today--and then I saw that it reached 1.0 today. Great timing!

Installing PHP-CS-Fixer

You can use Composer’s global require to install php-cs-fixer:

$ composer global require fabpot/php-cs-fixer @stable

Make sure your Composer vendor bin is in your path (in .bash_profile or .zsh_profile or whatever):

export PATH="$PATH:$HOME/.composer/vendor/bin"

If you’re using a Mac, you can also just install it with Homebrew:

$ brew tap josegonzalez/homebrew-php
$ brew install php-cs-fixer

Check out the Github page for full installation instructions for these and other methods.

Running PHP-CS-Fixer

By default, it runs “all PSR-2 fixers and some additional ones.” You can toggle the level you want to run with the --level flag, which I’ll be setting to psr2 so that the “additional” checks, which are targeted at Symfony and go above-and-beyond PSR2, don’t throw me off. (It runs the entire stack by default, which is called level “symfony” and includes things like “Align equals signs in subsequent lines”).

Let’s try it out! First we’ll do a non-changing dry run to see which files it’s going to change:

$ cd Sites/confomo
$ php-cs-fixer fix ./ --level=psr2 --dry-run

... and the result:

   1) app/commands/GrabTwitterProfilePicsCommand.php
   2) app/config/app.php
   3) app/config/auth.php
   (... and more, trimmed for blog post)
Fixed all files in 14.968 seconds, 5.250 MB memory used

Looks like about everything. Note that this doesn't show me what will change, but just a list of which files will change. OK, let’s go:

$ php-cs-fixer fix ./ --level=psr2

Ahh… the sweet, sweet sound of thousands of tabs converting to spaces. Well, most of them. There are quite a few file types and structures that PHP-CS-Fixer had a little trouble parsing, so I had to still go in and do quite a bit of manual cleanup afterwards--but it got the job started for me.

Saving configuration

Like every good tool, PHP-CS-Fixer has a dotfile for configuration. It's going to be a file named .php_cs that's actually a PHP file, an instance of the SymfonyCSConfigInterface. Check out this example from the docs:

<?php

$finder = Symfony\CS\Finder\DefaultFinder::create()
    ->exclude('somedir')
    ->in(__DIR__)
;

return Symfony\CS\Config\Config::create()
    ->fixers(array('indentation', 'elseif'))
    ->finder($finder)
;

You can configure the levels, the "fixers", the files, and the directories you want to analyze through that file.

Conclude

That’s it!

Choose your favorite method of installing, choose your level of sniffing, do a dry run to see what files it’ll fix, run it, rejoice, and read the docs for more configuration options.

Questions? Comments? Hit me up on Twitter at @stauffermatt.

Postscript

I was curious about how much of PSR-2 really got implemented as a result of PHP-CS-Fixer's PSR-2 toggle. I could've read through the checks, but instead I made the ugliest, most non-psr-1 and non-psr-2 compliant script I could, and ran it through it.

<?php

use Exception, Awesome;
namespace Awesome\Stuff;
class bad_class
    extends \Exception{
    const camelCase = 'abc';
    public $awesome = 'unvisible'; public $_great = 'fabulous';

    function Do_something_snaked_cased(){
        // This is one really frigging long line. I wonder if PHP-CS-Fixer will trim this really frigging long line? It says no hard limit so I think it won't.

        if( isset ($abc) || TRUE )
        {
            // do stuff

        }

    }

    final static public function woop() {}

    function _invisible( $stuff = [] , $other_stuff ) {
        switch($stuff)
        {
        case 0:
        echo 'stuff';
        break;
        }
    }

}

echo "echo";

?>

I ran it and got the following:

<?php

use Exception;
use Awesome;
namespace Awesome\Stuff;

class test
    extends \Exception
{
    const camelCase = 'abc';
    public $awesome = 'unvisible';
    public $_great = 'fabulous';

    public function Do_something_snaked_cased()
    {
        // This is one really frigging long line. I wonder if PHP-CS-Fixer will trim this really frigging long line? It says no hard limit so I think it won't.

        if (isset($abc) || TRUE) {
            // do stuff
        }
    }

    final public static function woop()
    {
    }

    public function _invisible($stuff = [], $other_stuff)
    {
        switch ($stuff) {
        case 0:
        echo 'stuff';
        break;
        }
    }
}

echo "echo";

As you can see, it cleaned up my indendation (original was tabs, although it seems the code embedder for my blog is converting them to spaces on display), my use block, my line spacing, my brace spacing, the spaces around parentheses, and the closing ?>. It didn't help with case (camel v upper v studly v snake), leading underscores, switch indendation, paremeter orders, capitalization of TRUE/FALSE/NULL, split line class definition, and probably a few other I didn't notice.

It makes sense: It can't make fundamental changes to the properties of your app. It's not smart enough to know, for example, where const camelCase is used, so it can't feel comfortable changing it. So PHP-CS-Fixer changes the things it can change without breaking your code, which is great, but leaves anything else alone--meaning it's a good first step, but you still have to be running your own sniffers at the same time.

For a real answer and not this bogus mythbusters stuff I'm doing, check out the Github page for a listing of all the fixers PHP-CS-Fixer makes available.


Comments? I'm @stauffermatt on Twitter


Tags: php