Conforming to coding standards with linters

By:

on

July 15, 2015

At the the DrupalNorth code sprint, I spent some time chatting about code linters, and how to use them to ensure your code conforms to coding standards. So, I thought I'd share the process that works for me.

If you find a better process, please blog about it and post a link in the comments!

This tutorial assumes:

  • You write or modify code in a language like PHP, JavaScript, CSS, Bash, etc.

What is a linter?

Simply put, a linter is a static analysis tool that you can run to ensure that your code is free from syntax and/or style errors.

Types of linters

To help me prioritize fixing linter messages, I usually classify linters into two types:

  • Linters that check for syntax errors, and,
  • Linters that check for coding standards violations.

I consider linters that check for syntax errors manditory, since I can't really think of a time when I would want code which contains syntax errors (in PHP and JavaScript, syntax errors cause unrecoverable fatal errors if the file containing the code is ever parsed (i.e.: white screen of death, JavaScript engine halting)). I will not commit code unless these types of linters give the okay.

I consider linters that check for coding standards violations strongly recommended (as they affect the maintainability of your code), but technically optional (because code that doesn't conform to standards still works). I try to avoid committing code that doesn't pass these linters; but there are some circumstances when that is unavoidable (e.g.: if I have to commit a contributed module, or if it's a hotfix and there's a plan to make the code more-maintainable later).

Checking for syntax errors

Many interpreted languages and shells have a syntax-checker built into them, for example:

  • php -l $file will check a PHP script for syntax errors,
  • bash -n $file will check a BASH shell script for syntax errors,
  • ruby -c $file will check a Ruby script for syntax errors,
  • perl -c $file will check a Perl script for syntax errors,
  • zsh -n $file will check a ZSH shell script for syntax errors, and,
  • fish -n $file will check a Fish shell script for syntax errors.

I wasn't able to find any built in syntax linters for JavaScript, Python, and PowerShell, but please leave a comment if you know of one.

Checking for coding standards

Github has put together a showcase of clean code linters, but note that they are limited to ones developed on Github.

As a Drupal developer, I usually use:

Since Drupal's coding standards differ slightly from other communities, all of these tools need to be configured slightly before you can use them. I'll explain how to install and configure all of these below.

Note that PHP CodeSniffer, the Drupal coding standards sniffs, and the Drupal Best Practice sniffs are used by the Drupal Automated Project Review tool.

PHP CodeSniffer

To set up PHP CodeSniffer, I'm assuming that:

  • You know how to use the command-line,
  • PHP is installed on your machine and in your PATH,
  • You have installed Composer,
  • You want to install PHP CodeSniffer globally (i.e.: for all projects), and,
  • Composer's ~/.composer/vendor/bin folder is in your PATH.

Installing

  1. Tell Composer to install PHP CodeSniffer globally:

     composer global require 'squizlabs/php_codesniffer'
    
  2. Install the Coder module globally, so we can use it's Drupal coding standards and best practices sniffs:

     composer global require 'drupal/coder'
    
  3. Register the Drupal and DrupalPractice Standards with PHP CodeSniffer:

     phpcs --config-set installed_paths ~/.composer/vendor/drupal/coder/coder_sniffer
    

Use

To check that a file conforms to the Drupal coding standards, run:

    phpcs --standard=Drupal $file

To check that a file conforms to the Drupal best practices, run:

    phpcs --standard=DrupalPractice $file

Setting a default coding standard

It is possible to set a default coding standard (i.e.: so you can just run phpcs $file), but:

  • You still have to run

      phpcs --standard=DrupalPractice $file
    

    to check that the file conforms to best practices, and,

  • If you ever have to work on non-Drupal projects, you'll have to explicitly state:

      phpcs --standard=PEAR $file
    

    for php's default functionality.

Instead of setting a default, you could create an alias in your shell. For example, if you use Bash, adding...

    alias drupalcs='phpcs --standard=Drupal'

... to your .bash_profile or .bashrc will let you run...

    drupalcs $file

... instead of...

    phpcs --standard=Drupal $file

If you use another shell, this is left as an exercise to the reader.

Fix coding standards violations with the PHP Code Beautifier (phpcbf)

PHP CodeSniffer also ships with a command called phpcbf (PHP Code Beautifier), which can fix some coding standards violations automatically. For example, to convert a file to the Drupal coding standards, run:

    phpcbf --standard=Drupal $file

A word of warning: phpcbf does not handle sections of commented-out code ("dead code") very well. If you plan to use phpcbf on a file or codebase, I would recommend deleting the dead code before you start. If deleting dead code makes you feel uneasy, remember that your version control system is designed to let you easily access it!

Ignoring files, or parts of a file

You might want to ignore coding standards violations on legacy code that the team has decided isn't worth the effort to convert to coding standards.

  • You can ignore whole files by adding a

      // @codingStandardsIgnoreFile
    

    comment at the top of the file, immediately after the opening PHP tag.

  • You can ignore certain parts of a file by surrounding that part of the file with

      // @codingStandardsIgnoreStart
    

    and

      // @codingStandardsIgnoreEnd
    

    comments.

See the PHPCodeSniffer advanced usage instructions for more information.

ESLint

To set up ESLint, I'm assuming that:

  • You know how to use the command-line,
  • You have installed Node.js,
  • Node.js' npm ("Node Package Manager") executable is in your PATH, and,
  • You want to install ESLint globally (i.e.: for all projects).

Installing

  1. Tell npm to install ESLint globally:

     npm i -g eslint
    

    On certain machines, you may need to run this command with sudo; but try it without sudo first.

Configuring

ESLint needs a .eslintrc file (and usually, a .eslintignore file as well) in your project's root directory.

If you're working on a Drupal 8 project, ESLint will just work, because it's configuration files ship with Drupal 8.0.x core. For other projects, you'll need to copy .eslintrc from Drupal 8 core.

Drupal 8.0.x core's .eslintignore probably won't work for your project, but I've put together some templates for your D7 projects, and you can also refer to the official .eslintignore documentation if you need to customize them further.

Use

To check that a JavaScript file conform to Drupal coding standards, run:

    eslint $file

CSSLint

To set up CSSLint, I'm assuming that:

  • You know how to use the command-line,
  • You have installed Node.js,
  • Node.js' npm ("Node Package Manager") executable is in your PATH, and,
  • You want to install CSSLint globally (i.e.: for all projects).

Installing

  1. Tell npm to install ESLint globally:

     npm i -g csslint
    

    On certain machines, you may need to run this command with sudo; but try it without sudo first.

Configuring

CSSLint needs a .csslintrc file in your project's root directory.

If you're working on a Drupal 8 project, CSSLint will just work, because it's configuration file ships with Drupal 8 core. For other projects, you'll need to copy .csslintrc from Drupal 8 core.

Use

To check that a CSS file conforms to Drupal coding standards, run:

    csslint $file

Running the coding-standards linters

Once PHP CodeSniffer, ESLint, and CSSLint have been installed and configured, you can:

  • Check that a PHP file conforms to Drupal coding standards with:

      phpcs --standard=Drupal $file
    
  • Check that a PHP file uses Drupal best practices with:

      phpcs --standard=DrupalPractice $file
    
  • Check that a JavaScript file conforms to Drupal coding standards with:

      eslint $file
    
  • Check that a CSS file conforms to Drupal coding standards with:

      csslint $file
    

A note on the history of Drupal coding standards checkers

In the past, most of us used the Coder module to check that our work conformed to the Drupal coding standards and documentation standards. However, the Coder module relied on parsing PHP, JavaScript, etc. code using regular expressions, which were hard to understand, write, and maintain, and couldn't catch all cases (because almost all programming languages are parsed with tokenizers and context-free grammars).

The move to PHP CodeSniffer started with the Drupal Code Sniffer (drupalcs) module, which eventually was imported into the Coder module's 7.x-2.x branch. It concentrated on checking PHP code only.

As the Drupal Community developed our own CSS coding standards and JavaScript coding standards, we needed a way to automatically check those too. Rather than writing our own parsers, we decided to "get off the island" and use what the wider web development community was using.

Stay tuned

Next week, I will blog about automatically running linters when you commit code, which can be pretty useful.