Enforce strict type checking in PHP with PHP Code Sniffer

Since version 7.0, PHP has the ability to use strict type checking. Let us look at the differences between loose and strict type checking. Afterward, we can have a look at enforcing strict type checking in every file of our project.

To illustrate the difference be loose type checking and strict type checking, consider the following method:

function add(int $a, int $b): int 
{
    return $a + $b;
}

Loose type checking

With loose type checking, all these calls would yield a result:

add(3, 3);   // 6
add(3, '3'); // 6
add(3, 3.0); // 6
add(3, true); // 4

Internally, PHP casts each parameter into an integer. As long as it is able to do so, the function call will succeed and a value will be returned.

This behavior is pretty normal if you are familiar with PHP. However, because the method was defined with type hints, one would suspect these types to be enforced. This is not the case by default and can lead to hard-to-find bugs.

Strict type checking

Strict type checking can be enabled by starting the PHP file with the following code:

<?php
declare(strict_types=1);

With strict type checking enabled, only the first call would succeed. All other calls will trigger a TypeError.

In my view, strict types are more predictable and thus better. They might also break backward compatibility and that is why there is no built-in way to enforce strict types for a whole project.

PHP CS to the rescue

PHP Code Sniffer is a tool to check and fix code style issues throughout a code base. Thanks to the slevomat/code-standard extension it can be used to sniff out even more style problems.

Firstly, install PHP CS and the extension using composer:

composer require squizlabs/php_codesniffer ~3.0
composer require slevomat/coding-standard ~4.0

Then, create a ruleset.xml file in the root of your project with the following contents:

<?xml version="1.0"?>
<ruleset name="name-of-your-project">
    <!-- relative path from PHPCS source location -->
    <config name="installed_paths" value="vendor/slevomat/coding-standard"/>

    <!-- specific sniffs to include -->
    <rule ref="vendor/slevomat/coding-standard/SlevomatCodingStandard/Sniffs/TypeHints/DeclareStrictTypesSniff.php" />
</ruleset>

Now, add the following 2 lines to the scripts-section of composer.json:

"check-style": "phpcs -p --standard=ruleset.xml --runtime-set ignore_errors_on_exit 1 --runtime-set ignore_warnings_on_exit 1 src tests",
"fix-style": "phpcbf -p --standard=ruleset.xml --runtime-set ignore_errors_on_exit 1 --runtime-set ignore_warnings_on_exit 1  src tests"

Usage

Now, with composer check-style and composer fix-style you are able to enforce the declaration of strict type checking in all files. Use these commands in the continuous integration platform of your choice and loose types will (hopefully) never trip you up again!

Further reading