Using PHP Codesniffer With Laravel

Published on by

Using PHP Codesniffer With Laravel image

As of Laravel 9, the excellent Laravel Pint comes bundled with a new Laravel installation. I love Pint, and for most people, using it for all code-style formatting is enough. Before Laravel Pint, I preferred a combination of PHP CS Fixer and PHP Codesniffer—both are excellent in tandem and offer unique rules that can help enforce code style.

Line length style is one example of where PHP Codesniffer can complement tools like PHP CS Fixer, and we can use both to help developers stick to a consistent code style. If you're using PSR-12, the Lines section has the following information about line length:

There MUST NOT be a hard limit on line length.

The soft limit on line length MUST be 120 characters.

Lines SHOULD NOT be longer than 80 characters; lines longer than that SHOULD be split into multiple subsequent lines of no more than 80 characters each.

Our CI tools can enforce both the soft and hard limits of line length, but the way I interpret PSR-12 is that PHP Codesniffer should warn us and not return any errors for lines > 80 in length. We will cover this further on in the article.

Let's work through setting up PHP Codesniffer in a new Laravel project, and then in follow-up tutorials, we will learn how to create a custom PHP Codesniffer standard that we can share across all our projects.

Along the way, I'll show you some configuration settings I like to set to get the most out of PHP Codesniffer during development.

Setup

The first thing we'll do is created an example project so you can see how to incorporate PHP Codesniffer with Laravel from scratch:

laravel new phpcs-part-1 --git
cd phpcs-part-1

The git flag will install a new Laravel application, initialize git, and commit everything to version control. This leaves us a clean slate to see the changes we make during this tutorial.

Next, let's install PHP Codesniffer as a development dependency:

composer require --dev squizlabs/php_codesniffer

Note: At the time of writing, the required command installs version ^3.7, but your version might differ slightly, which is not a concern.

If you run phpcs from the command line, you'll see that it wants you to specify a path:

$ vendor/bin/phpcs
ERROR: You must supply at least one file or directory to process.
 
Run "phpcs --help" for usage information

Don't worry, we are about to add a config file to specify which paths PHPCS should include (and exclude), but if you run it with the default ruleset (Pear) you'll get a bunch of errors with a default Laravel install:

$ vendor/bin/phpcs -v app
Registering sniffs in the PEAR standard... DONE (28 sniffs registered)
....
 
FILE: /Users/paul/code/sandbox/phpcs-part-1/app/Providers/AppServiceProvider.php
--------------------------------------------------------------------------------
FOUND 2 ERRORS AFFECTING 2 LINES
--------------------------------------------------------------------------------
2 | ERROR | Missing file doc comment
7 | ERROR | Missing doc comment for class AppServiceProvider
--------------------------------------------------------------------------------

If you inspect the exit code, you'll see the errors cause PHPCS to exit with a non-zero code:

$ echo $?
2

I want to be clear: these errors are not because our code has bad formatting (quite the opposite) but because we are using the default PEAR standard, which Laravel doesn't follow out-of-the-box.

Let's look at using a different standard and creating a configuration file.

Exploring the Command Line Tools

Before we add a configuration file to our project, let's see how the same configuration would look using the command line. We know that we don't want to use the PEAR standard, so let's first run our PHPCS command with the built-in PSR-12 standard:

vendor/bin/phpcs --standard=PSR12 app

If you look carefully at the output, you'll notice that the PSR12 standard for PHP Codesniffer wants to fix some spacing around concatenated strings:

FILE: /Users/paul/code/sandbox/phpcs-part-1/app/Console/Kernel.php
----------------------------------------------------------------------
FOUND 2 ERRORS AFFECTING 1 LINE
----------------------------------------------------------------------
28 | ERROR | [x] Expected at least 1 space before "."; 0 found
28 | ERROR | [x] Expected at least 1 space after "."; 0 found
----------------------------------------------------------------------
PHPCBF CAN FIX THE 2 MARKED SNIFF VIOLATIONS AUTOMATICALLY
----------------------------------------------------------------------

The x means we can fix these sniffs automatically (not all PHP Codsniffer sniffs are automatically fixable) with the phpcbf bin that ships with Codesniffer. We will get to that in a second.

First, let's see about a few other command flags that I prefer to use. Once you start using PHP Codesniffer in development and CI, I notice that the full name of the sniff is not apparent. You might want to tweak that sniff, ignore it on a specific line, etc.

To always see the full sniff in the command line, you can use the `-s' flag:

vendor/bin/phpcs -s --standard=PSR12 app

Here's an example of what the output looks like with the full sniff:

FILE: /Users/paul/code/sandbox/phpcs-part-1/app/Console/Kernel.php
----------------------------------------------------------------------------------------------------------------
FOUND 2 ERRORS AFFECTING 1 LINE
----------------------------------------------------------------------------------------------------------------
28 | ERROR | [x] Expected at least 1 space before "."; 0 found
| | (PSR12.Operators.OperatorSpacing.NoSpaceBefore)
28 | ERROR | [x] Expected at least 1 space after "."; 0 found
| | (PSR12.Operators.OperatorSpacing.NoSpaceAfter)
----------------------------------------------------------------------------------------------------------------
PHPCBF CAN FIX THE 2 MARKED SNIFF VIOLATIONS AUTOMATICALLY
----------------------------------------------------------------------------------------------------------------

You can see the PSR12.Operators.OperatorSpacing.NoSpaceBefore sniff found in the above example. If we want to ignore this sniff in the Kernel.php file, we can add this to the app/Console/Kernel.php file around line 28:

// phpcs:disable PSR12.Operators.OperatorSpacing.NoSpaceAfter
$this->load(__DIR__.'/Commands');
// phpcs:enable

If we rerun the command—this time for the Kernel.php file—we can see that the NoSpaceAfter rule is disabled for that line, but we still get the NoSpaceBefore error:

vendor/bin/phpcs -s --standard=PSR12 app/Console/Kernel.php
 
FILE: /Users/paul/code/sandbox/phpcs-part-1/app/Console/Kernel.php
----------------------------------------------------------------------------------------------------------------
FOUND 1 ERROR AFFECTING 1 LINE
----------------------------------------------------------------------------------------------------------------
29 | ERROR | [x] Expected at least 1 space before "."; 0 found
| | (PSR12.Operators.OperatorSpacing.NoSpaceBefore)
----------------------------------------------------------------------------------------------------------------
PHPCBF CAN FIX THE 1 MARKED SNIFF VIOLATIONS AUTOMATICALLY
----------------------------------------------------------------------------------------------------------------

We can disable multiple rules for a given set of lines by comma-separating the sniffs like so:

// phpcs:disable PSR12.Operators.OperatorSpacing.NoSpaceAfter, PSR12.Operators.OperatorSpacing.NoSpaceBefore
$this->load(__DIR__.'/Commands');
// phpcs:enable

Finally, let's revert all the changes made to the app/Console/Kernel.php file. You'll discover that these two rules we have been using as an example directly conflict with Laravel Pint, which expects spaces before and after.

First, let's automatically fix the above sniffs using phpcbf:

vendor/bin/phpcbf --standard=PSR12 app
 
PHPCBF RESULT SUMMARY
----------------------------------------------------------------------------------------------
FILE FIXED REMAINING
----------------------------------------------------------------------------------------------
/Users/paul/code/sandbox/phpcs-part-1/app/Models/User.php 1 0
/Users/paul/code/sandbox/phpcs-part-1/app/Http/Controllers/Controller.php 1 0
/Users/paul/code/sandbox/phpcs-part-1/app/Console/Kernel.php 2 0
----------------------------------------------------------------------------------------------
A TOTAL OF 4 ERRORS WERE FIXED IN 3 FILES
----------------------------------------------------------------------------------------------

After running that, let's run Laravel Pint to see it do the reverse of what PHP Codesniffer fixed automatically:

$ vendor/bin/pint
FIXED 54 files, 1 style issue fixed
✓ app/Console/Kernel.php concat_space

For us to be able to use both tools during development and in CI environments, we need to pick a style and enforce it with one tool and disable it from the other. I prefer automatic fixes from Pint, so I disable the competing Sniff in PHP Codesniffer.

Configuration File

Let's resolve the conflict between Pint and Codesniffer by creating a configuration file. In an application, I would typically create a phpcs.xml file, but if you plan on letting others use your application as a starting point, you could consider using phpcs.xml.dist. Let's create it as phpcs.xml:

<?xml version="1.0"?>
<!-- @see https://pear.php.net/manual/en/package.php.php-codesniffer.annotated-ruleset.php -->
<ruleset name= "Laravel PHPCS Rules">
 
<description>PHPCS ruleset for Example app.</description>
 
<file>tests</file>
<file>app</file>
 
<!-- Show progress of the run -->
<arg value= "p"/>
 
<!-- Show sniff codes in all reports -->
<arg value= "s"/>
 
<!-- Our base rule: set to PSR12 -->
<rule ref="PSR12">
<exclude name="PSR12.Operators.OperatorSpacing.NoSpaceBefore"/>
<exclude name="PSR12.Operators.OperatorSpacing.NoSpaceAfter"/>
</rule>
 
<rule ref= "Generic.Files.LineLength">
<properties>
<property name="lineLimit" value="80"/>
<property name="absoluteLineLimit" value="120"/>
</properties>
</rule>
<rule ref="PSR1.Methods.CamelCapsMethodName.NotCamelCaps">
<exclude-pattern>tests/</exclude-pattern>
</rule>
 
</ruleset>

You can see that I've added our -s argument to the config, so all reports show sniff codes. We also target the app and test directories but feel free to configure other directories in your project that you want Codesniffer to check.

We've disabled the operator spacing sniffs, so Pint should be happy, and PHP Codesniffer will ignore these rules now.

We also customized the line length rule, using the language in PSR-12 as our guide for the line limit and maximum value. These sniffs report as warnings, so when you run this tool in CI you'll want to make sure that either warnings are suppressed or that CI still passes with warnings. The warnings are intended to help you, the developer, fix line lengths in development.

Finally, the NotCamelCaps sniff is excluded in the tests folder because I like writing PHPUnit tests with snake case:

/* @test */
public function it_does_something_awesome()

Now we can run phpcs without any arguments, and it will pick up our configuration file. You should only see line length warnings about line length and multiple imports.

vendor/bin/phpcs

Another violation you'll notice is the MultipleImport sniff, which is due to this line, for example:

class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
// ...
}

Feel free to disable this sniff if you want, or you can automatically fix it (using our configuration file) using the phpcbf command-line bin:

vendor/bin/phpcbf
.........F..........F.. 23 / 23 (100%)
 
PHPCBF RESULT SUMMARY
----------------------------------------------------------------------------------------------
FILE FIXED REMAINING
----------------------------------------------------------------------------------------------
/Users/paul/code/sandbox/phpcs-part-1/app/Models/User.php 1 0
/Users/paul/code/sandbox/phpcs-part-1/app/Http/Controllers/Controller.php 1 0
----------------------------------------------------------------------------------------------
A TOTAL OF 2 ERRORS WERE FIXED IN 2 FILES
----------------------------------------------------------------------------------------------

Conclusion

We covered quite a bit of ground, but you should be able to start using PHP Codesniffer from scratch with Laravel. It can be an excellent addition if you want to target some of the specific rules that PHP CS Fixer doesn't perform, and I've equipped you with how to disable conflicting rules or rules that you want to disable.

Paul Redmond photo

Staff writer at Laravel News. Full stack web developer and author.

Cube

Laravel Newsletter

Join 40k+ other developers and never miss out on new tips, tutorials, and more.

Laravel Forge logo

Laravel Forge

Easily create and manage your servers and deploy your Laravel applications in seconds.

Laravel Forge
Tinkerwell logo

Tinkerwell

The must-have code runner for Laravel developers. Tinker with AI, autocompletion and instant feedback on local and production environments.

Tinkerwell
No Compromises logo

No Compromises

Joel and Aaron, the two seasoned devs from the No Compromises podcast, are now available to hire for your Laravel project. ⬧ Flat rate of $7500/mo. ⬧ No lengthy sales process. ⬧ No contracts. ⬧ 100% money back guarantee.

No Compromises
Kirschbaum logo

Kirschbaum

Providing innovation and stability to ensure your web application succeeds.

Kirschbaum
Shift logo

Shift

Running an old Laravel version? Instant, automated Laravel upgrades and code modernization to keep your applications fresh.

Shift
Bacancy logo

Bacancy

Supercharge your project with a seasoned Laravel developer with 4-6 years of experience for just $2500/month. Get 160 hours of dedicated expertise & a risk-free 15-day trial. Schedule a call now!

Bacancy
Lucky Media logo

Lucky Media

Get Lucky Now - the ideal choice for Laravel Development, with over a decade of experience!

Lucky Media
Lunar: Laravel E-Commerce logo

Lunar: Laravel E-Commerce

E-Commerce for Laravel. An open-source package that brings the power of modern headless e-commerce functionality to Laravel.

Lunar: Laravel E-Commerce
LaraJobs logo

LaraJobs

The official Laravel job board

LaraJobs
SaaSykit: Laravel SaaS Starter Kit logo

SaaSykit: Laravel SaaS Starter Kit

SaaSykit is a Multi-tenant Laravel SaaS Starter Kit that comes with all features required to run a modern SaaS. Payments, Beautiful Checkout, Admin Panel, User dashboard, Auth, Ready Components, Stats, Blog, Docs and more.

SaaSykit: Laravel SaaS Starter Kit
Supercharge Your SaaS Development with FilamentFlow: The Ultimate Laravel Filament Boilerplate logo

Supercharge Your SaaS Development with FilamentFlow: The Ultimate Laravel Filament Boilerplate

Build your SaaS application in hours. Out-of-the-box multi-tenancy and seamless Stripe integration. Supports subscriptions and one-time purchases, allowing you to focus on building and creating without repetitive setup tasks.

Supercharge Your SaaS Development with FilamentFlow: The Ultimate Laravel Filament Boilerplate
Rector logo

Rector

Your partner for seamless Laravel upgrades, cutting costs, and accelerating innovation for successful companies

Rector
MongoDB logo

MongoDB

Enhance your PHP applications with the powerful integration of MongoDB and Laravel, empowering developers to build applications with ease and efficiency. Support transactional, search, analytics and mobile use cases while using the familiar Eloquent APIs. Discover how MongoDB's flexible, modern database can transform your Laravel applications.

MongoDB

The latest

View all →
Laravel Roundup - November image

Laravel Roundup - November

Read article
Bento Laravel SDK image

Bento Laravel SDK

Read article
Laravel 11.30 Released image

Laravel 11.30 Released

Read article
Bluesky notification channel for Laravel image

Bluesky notification channel for Laravel

Read article
Sprout Multitenancy Package for Laravel image

Sprout Multitenancy Package for Laravel

Read article
Creating a CLI Application With Laravel and Docker image

Creating a CLI Application With Laravel and Docker

Read article