Skip to main content

This site requires you to update your browser. Your browsing experience maybe affected by not having the most up to date version.

 

Converting a SilverStripe codebase to PSR-2

In this post Steve Boyd demonstrates how to convert a SilverStripe 3.x codebase to the PSR-2 coding standard and create a git pre-commit hook to ensure that all future commits adhere to PSR-2. 

Read post

In this post Steve Boyd demonstrates how to convert a SilverStripe 3.x codebase to the PSR-2 coding standard and create a git pre-commit hook to ensure that all future commits adhere to PSR-2.

What is PSR-2 and why do we want it?

Adopting coding standards makes it easier for teams to work together. There is joint consensus for how code should be formatted, making it easier for others to peer review the code we write.

PSR-2 is the PHP coding standard recommended by PHP-FIG, a group dedicated to addressing the commonalities and standardisation of PHP projects, making it the most common PHP coding standard. You can read about PSR-2 in detail here.

We've also implemented PSR-2 as a standard in SilverStripe 4, so getting into it now will save you some pain when upgrading your code later.

Easy autoformating

Because of the standards popularity, there is great auto-formatting support available in a wide range of IDEs (Integrated development environments). This also means that it's easy to get a new team to agree to use these coding standards when starting a new project.

Thanks to the abundance of automated code formatting tools, you don't even need to write PSR-2 compliant code yourself. You simply write code however you feel comfortable, press a couple of keys, and revel in the joy of your IDE automatically formatting your code for you.

Slightly looser PSR-2 ruleset

SilverStripe 3.x used to use some old conventions, and they've recently been switching over to using PSR-2. From SilverStripe 3.5 onwards you'll likely have a phpcs.xml.dist ruleset that ships with your installer, which will contain the examples we're going to run through in detail below.

Note that while PSR-2 hasn't been implemented throughout the core SilverStripe 3 codebase, it has been in SilverStripe 4. If you implement PSR-2 in your own project code now you'll ensure the transition to SilverStripe 4 is made easier by removing this difference as early as possible.

Our customised PSR-2 ruleset is going to ignore a couple of rules:

  • Exclude camelcaps methods - (so that you can still use 'hardcoded' functions like Controller::Form())
  • Excludes camelcaps classes - (so that you can use the conventional controller functions like Page_Controller)

There are also a couple of rules that you can optionally ignore because of how most SilverStripe 3.x codebases are:

  • Exclude only one class per file - it used to be a SilverStripe 3 coding standard to include multiple classes per file, e.g. Page and Page_Controller, and while we're now aiming to follow PSR-2 in SilverStripe 3 it isn't implemented everywhere yet
  • Exclude mandatory namespace

SilverStripe code on a laptop screen

Using PHPCS + PHPCBF to convert the codebase to PSR-2

PHPCS and PHPCBF are a pair of common command-line tools used to analyse and autoformat PHP code.

Add the file phpcs.xml to the root of your project, or modify the existing phpcs.xml.dist file if you have one already (note that a phpcs.xml file will take priority over a phpcs.xml.dist file).

phpcs.xml

<?xml version="1.0"?>
<ruleset name="PSR2-SS3">

   <description>A modified version of PSR2 that excludes some rules for SilverStripe 3.x</description>

   <!-- Include the whole PSR-2 standard -->
   <rule ref="PSR2">

       <!--
       - Exclude mandatory namespace
       - Exclude only one class per file
       -->
       <exclude name="PSR1.Classes.ClassDeclaration"/>

       <!--
       - Exclude camel caps methods - e.g. Controller::Form()
       -->
       <exclude name="PSR1.Methods.CamelCapsMethodName"/>

       <!--
       - Excludes camel caps classes - e.g. Page_Controller
       -->
       <exclude name="Squiz.Classes.ValidClassName"/>

   </rule>

</ruleset>

Update your composer.json file, adding "squizlabs/php_codesniffer" in "require-dev".

This will install the phpcs and phpcbf binaries in ./vendor/bin.

composer.json

{
 ...
 "require-dev": {
   "squizlabs/php_codesniffer": "^3"
 },
 ...
}

Run composer installto install CodeSniffer.

Showing and fixing errors

Run the following to display errors in a directory:

./vendor/bin/phpcs --colors --extensions=php -n -p [directory]

You'll only want to do this on files for your application, not third-party directories installed by composer.  In silverstripe 3.x, this will usually be:

./vendor/bin/phpcs --colors --extensions=php -n -p mysite

From there you can can choose to fix the errors yourself, or let PHP Code Beautifier and Fixer (phpcbf) to do it for you. The syntax is the same as phpcs:

./vendor/bin/phpcbf --colors --extensions=php -n -p mysite 

Then run phpcs again to check that all errors were automatically fixed. You may need to manually fix some errors that phpcbf cannot automatically fix.

Commit your files and you're done!

Because this is going to change a large number of files, I highly recommend you don't do any other feature/bugfix/refactor development, as the git diff is going to be potentially huge.

Creating a git pre-commit hook

You can maintain PSR-2 standards within your team by using a git pre-commit hook. This ensures that all php code meets the PSR-2 standard before being committed.

Update your composer.json file, add "scripts" - "post-install-cmd" - "php composer-script.php".  What this will do is run the file composer-script.php when you run composer install.

composer.json

{
 ...
 "scripts": {
   "post-install-cmd": [
     "php composer-script.php"
   ]
 }
 ...
}

The function of composer-script.php is to create or update the file ./.git/hooks/pre-commit and copy the contents of the file pre-commit.sh in there.

composer-script.php

<?php
// This script will copy a pre-commit hook from this repo to a developers local git
// The pre-commit hook will be run whenever a developer runs "git commit"
// This script will run after running composer install, via the "post-install-cmd" in composer.json
$gitHookFilename = '.git/hooks/pre-commit';
$shellHookFilename = 'pre-commit.sh';
if (file_exists($gitHookFilename)) {
   unlink($gitHookFilename);
}
copy($shellHookFilename, $gitHookFilename);
chmod($gitHookFilename, 0744); 

pre-commit.sh

#!/bin/sh

PROJECT=`php -r "echo dirname(dirname(dirname(realpath('$0'))));"`
STAGED_FILES_CMD=`git diff --cached --name-only --diff-filter=ACMR HEAD | grep \\\\.php`

# Determine if a file list is passed
if [ "$#" -eq 1 ]
then
 oIFS=$IFS
 IFS='
 '
 SFILES="$1"
 IFS=$oIFS
fi
SFILES=${SFILES:-$STAGED_FILES_CMD}
 echo "Checking PHP Lint (php -l) ..."
for FILE in $SFILES
do
 php -l -d display_errors=1 $PROJECT/$FILE
 if [ $? != 0 ]
 then
   echo "Fix the error before commit."
   exit 1
 fi
 FILES="$FILES $PROJECT/$FILE"
done

if [ "$FILES" != "" ]
then
 echo "Running Code Sniffer (phpcs) ..."
./vendor/bin/phpcs --colors -n -p $FILES
 if [ $? != 0 ]
 then
   echo "Fix the error before commit."
   echo "You can do this automatically with"
   echo "./vendor/bin/phpcbf [FILE]"
   exit 1
 fi
fi

exit $?

It runs when you git commit

The git pre commit hook runs when a developer runs the command git commit.

The file pre-commit.sh will run php lint and phpcs to ensure that the committed *.php files meet the PSR-2 code standard. It will automatically use the phpcs.xml file in the root directory.

Files that do not meet the standard will be displayed in the terminal output with the part of the code that is failing. No files will be committed.

Once these files have been fixed to pass the PSR-2 standard then they'll be allowed to be committed.

One last thing; if you do this on a new repository then you'll need to have have at least one commit so that HEAD revision exists in git before this pre-commit script works properly.

Happy coding! 

If you've enjoyed this blog post, there's a similar blog post for JavaScript linting which you could add to your project.

If you want to keep updated on what is happening in the SilverStripe Community, join the Community slack channel!

Slack sign up 01

About the author
Steve Boyd

Steve is a Developer at SilverStripe who writes code with performance in mind while still making it easy to read. Steve values simplicity and loves interacting with people.

Post your comment

Comments

No one has commented on this page yet.

RSS feed for comments on this page | RSS feed for all comments