PHPCS: PHP CodeSniffer
Install
PHPCS (PHP CodeSniffer) checks your code against a coding standard such as PSR-12 and reports every violation with file, line and a fixable [x] mark. Its twin PHPCBF fixes the safe ones for you. Pick one installation method below — they all give you the same two binaries, phpcs and phpcbf.
Install on Linux
Brief command chain to install PHPCS on Linux (Ubuntu/Debian):
sudo apt install -y composer php -v
Notice the PHP version.
In this example it is 8.1, so we will install the PHP CodeSniffer for PHP 8.1.
sudo apt install php8.1-xml composer require --dev squizlabs/php_codesniffer:^3.13 vendor/bin/phpcs --version
Usage example: check a file for PSR-2 violations and fix them automatically:
vendor/bin/phpcbf --standard=phpcs.xml --sniffs=PSR2.Methods.FunctionCallSignature -- src/ru/qa/.php/Page.php
PHPCBF RESULT SUMMARY --------------------------------------------------------------------------------- FILE FIXED REMAINING --------------------------------------------------------------------------------- /home/andrei/github/aredel/container/src/ru/qa/.php/Page.php 81 0 --------------------------------------------------------------------------------- A TOTAL OF 81 ERRORS WERE FIXED IN 1 FILE --------------------------------------------------------------------------------- Time: 582ms; Memory: 12MB
Detailed path that I followed to install phpcs on Linux:
You may need to install composer first, then require the package.
sudo apt install composer
[sudo] password for andrei: Reading package lists... Done Building dependency tree... Done Reading state information... Done The following packages were automatically installed and are no longer required: libfwupd2 libfwupdplugin5 libgcab-1.0-0 libllvm13 libsmbios-c2 libwpe-1.0-1 libwpebackend-fdo-1.0-1 nvidia-firmware-535-server-535.230.02 Use 'sudo apt autoremove' to remove them. The following additional packages will be installed: jsonlint php-composer-ca-bundle php-composer-metadata-minifier php-composer-pcre php-composer-semver php-composer-spdx-licenses php-composer-xdebug-handler php-intl php-json-schema php-mbstring php-psr-container php-psr-log php-react-promise php-symfony-console php-symfony-deprecation-contracts php-symfony-filesystem php-symfony-finder php-symfony-polyfill-php80 php-symfony-process php-symfony-service-contracts php-symfony-string php8.1-intl php8.1-mbstring Suggested packages: fossil mercurial subversion php-zip php-symfony-event-dispatcher php-symfony-lock php-symfony-service-implementation The following NEW packages will be installed: composer jsonlint php-composer-ca-bundle php-composer-metadata-minifier php-composer-pcre php-composer-semver php-composer-spdx-licenses php-composer-xdebug-handler php-intl php-json-schema php-mbstring php-psr-container php-psr-log php-react-promise php-symfony-console php-symfony-deprecation-contracts php-symfony-filesystem php-symfony-finder php-symfony-polyfill-php80 php-symfony-process php-symfony-service-contracts php-symfony-string php8.1-intl php8.1-mbstring 0 upgraded, 24 newly installed, 0 to remove and 7 not upgraded. Need to get 1 416 kB of archives. After this operation, 6 299 kB of additional disk space will be used. Do you want to continue? [Y/n] Y Get:1 http://fi.archive.ubuntu.com/ubuntu jammy/universe amd64 php-composer-ca-bundle all 1.3.1-1 [10,4 kB] Get:2 http://fi.archive.ubuntu.com/ubuntu jammy/universe amd64 php-composer-metadata-minifier all 1.0.0-2 [3 594 B] Get:3 http://fi.archive.ubuntu.com/ubuntu jammy/universe amd64 php-composer-semver all 3.2.9-1 [18,2 kB] Get:4 http://fi.archive.ubuntu.com/ubuntu jammy/universe amd64 php-composer-spdx-licenses all 1.5.6-1 [13,2 kB] Get:5 http://fi.archive.ubuntu.com/ubuntu jammy/universe amd64 php-psr-log all 3.0.0-1 [6 862 B] Get:6 http://fi.archive.ubuntu.com/ubuntu jammy/universe amd64 php-composer-pcre all 1.0.1-1 [7 810 B] Get:7 http://fi.archive.ubuntu.com/ubuntu jammy/universe amd64 php-composer-xdebug-handler all 2.0.4-1build1 [16,6 kB] Get:8 http://fi.archive.ubuntu.com/ubuntu jammy/universe amd64 php-json-schema all 5.2.11-1 [33,9 kB] Get:9 http://fi.archive.ubuntu.com/ubuntu jammy/universe amd64 jsonlint all 1.8.3-2 [12,1 kB] Get:10 http://fi.archive.ubuntu.com/ubuntu jammy/universe amd64 php-symfony-deprecation-contracts all 2.4.0-1ubuntu2 [4 700 B] Get:11 http://fi.archive.ubuntu.com/ubuntu jammy-updates/main amd64 php8.1-mbstring amd64 8.1.2-1ubuntu2.26 [483 kB] Get:12 http://fi.archive.ubuntu.com/ubuntu jammy/universe amd64 php-mbstring all 2:8.1+92ubuntu1 [1 844 B] Get:13 http://fi.archive.ubuntu.com/ubuntu jammy/universe amd64 php-symfony-polyfill-php80 all 1.24.0-1ubuntu2 [6 642 B] Get:14 http://fi.archive.ubuntu.com/ubuntu jammy/universe amd64 php-psr-container all 2.0.2-1 [3 878 B] Get:15 http://fi.archive.ubuntu.com/ubuntu jammy/universe amd64 php-symfony-service-contracts all 2.4.0-1ubuntu2 [8 136 B] Get:16 http://fi.archive.ubuntu.com/ubuntu jammy-updates/universe amd64 php8.1-intl amd64 8.1.2-1ubuntu2.26 [142 kB] Get:17 http://fi.archive.ubuntu.com/ubuntu jammy/universe amd64 php-intl all 2:8.1+92ubuntu1 [1 840 B] Get:18 http://fi.archive.ubuntu.com/ubuntu jammy/universe amd64 php-symfony-string all 5.4.4+dfsg-1ubuntu8 [36,8 kB] Get:19 http://fi.archive.ubuntu.com/ubuntu jammy/universe amd64 php-symfony-console all 5.4.4+dfsg-1ubuntu8 [84,1 kB] Get:20 http://fi.archive.ubuntu.com/ubuntu jammy/universe amd64 php-symfony-filesystem all 5.4.4+dfsg-1ubuntu8 [23,9 kB] Get:21 http://fi.archive.ubuntu.com/ubuntu jammy/universe amd64 php-symfony-finder all 5.4.4+dfsg-1ubuntu8 [24,9 kB] Get:22 http://fi.archive.ubuntu.com/ubuntu jammy/universe amd64 php-symfony-process all 5.4.4+dfsg-1ubuntu8 [28,6 kB] Get:23 http://fi.archive.ubuntu.com/ubuntu jammy/universe amd64 php-react-promise all 2.7.0-2 [16,4 kB] Get:24 http://fi.archive.ubuntu.com/ubuntu jammy/universe amd64 composer all 2.2.6-2ubuntu4 [425 kB] Fetched 1 416 kB in 1s (1 581 kB/s) Selecting previously unselected package php-composer-ca-bundle. (Reading database ... 256805 files and directories currently installed.) Preparing to unpack .../00-php-composer-ca-bundle_1.3.1-1_all.deb ... Unpacking php-composer-ca-bundle (1.3.1-1) ... Selecting previously unselected package php-composer-metadata-minifier. Preparing to unpack .../01-php-composer-metadata-minifier_1.0.0-2_all.deb ... Unpacking php-composer-metadata-minifier (1.0.0-2) ... Selecting previously unselected package php-composer-semver. Preparing to unpack .../02-php-composer-semver_3.2.9-1_all.deb ... Unpacking php-composer-semver (3.2.9-1) ... Selecting previously unselected package php-composer-spdx-licenses. Preparing to unpack .../03-php-composer-spdx-licenses_1.5.6-1_all.deb ... Unpacking php-composer-spdx-licenses (1.5.6-1) ... Selecting previously unselected package php-psr-log. Preparing to unpack .../04-php-psr-log_3.0.0-1_all.deb ... Unpacking php-psr-log (3.0.0-1) ... Selecting previously unselected package php-composer-pcre. Preparing to unpack .../05-php-composer-pcre_1.0.1-1_all.deb ... Unpacking php-composer-pcre (1.0.1-1) ... Selecting previously unselected package php-composer-xdebug-handler. Preparing to unpack .../06-php-composer-xdebug-handler_2.0.4-1build1_all.deb ... Unpacking php-composer-xdebug-handler (2.0.4-1build1) ... Selecting previously unselected package php-json-schema. Preparing to unpack .../07-php-json-schema_5.2.11-1_all.deb ... Unpacking php-json-schema (5.2.11-1) ... Selecting previously unselected package jsonlint. Preparing to unpack .../08-jsonlint_1.8.3-2_all.deb ... Unpacking jsonlint (1.8.3-2) ... Selecting previously unselected package php-symfony-deprecation-contracts. Preparing to unpack .../09-php-symfony-deprecation-contracts_2.4.0-1ubuntu2_all.deb ... Unpacking php-symfony-deprecation-contracts (2.4.0-1ubuntu2) ... Selecting previously unselected package php8.1-mbstring. Preparing to unpack .../10-php8.1-mbstring_8.1.2-1ubuntu2.26_amd64.deb ... Unpacking php8.1-mbstring (8.1.2-1ubuntu2.26) ... Selecting previously unselected package php-mbstring. Preparing to unpack .../11-php-mbstring_2%3a8.1+92ubuntu1_all.deb ... Unpacking php-mbstring (2:8.1+92ubuntu1) ... Selecting previously unselected package php-symfony-polyfill-php80. Preparing to unpack .../12-php-symfony-polyfill-php80_1.24.0-1ubuntu2_all.deb ... Unpacking php-symfony-polyfill-php80 (1.24.0-1ubuntu2) ... Selecting previously unselected package php-psr-container. Preparing to unpack .../13-php-psr-container_2.0.2-1_all.deb ... Unpacking php-psr-container (2.0.2-1) ... Selecting previously unselected package php-symfony-service-contracts. Preparing to unpack .../14-php-symfony-service-contracts_2.4.0-1ubuntu2_all.deb ... Unpacking php-symfony-service-contracts (2.4.0-1ubuntu2) ... Selecting previously unselected package php8.1-intl. Preparing to unpack .../15-php8.1-intl_8.1.2-1ubuntu2.26_amd64.deb ... Unpacking php8.1-intl (8.1.2-1ubuntu2.26) ... Selecting previously unselected package php-intl. Preparing to unpack .../16-php-intl_2%3a8.1+92ubuntu1_all.deb ... Unpacking php-intl (2:8.1+92ubuntu1) ... Selecting previously unselected package php-symfony-string. Preparing to unpack .../17-php-symfony-string_5.4.4+dfsg-1ubuntu8_all.deb ... Unpacking php-symfony-string (5.4.4+dfsg-1ubuntu8) ... Selecting previously unselected package php-symfony-console. Preparing to unpack .../18-php-symfony-console_5.4.4+dfsg-1ubuntu8_all.deb ... Unpacking php-symfony-console (5.4.4+dfsg-1ubuntu8) ... Selecting previously unselected package php-symfony-filesystem. Preparing to unpack .../19-php-symfony-filesystem_5.4.4+dfsg-1ubuntu8_all.deb ... Unpacking php-symfony-filesystem (5.4.4+dfsg-1ubuntu8) ... Selecting previously unselected package php-symfony-finder. Preparing to unpack .../20-php-symfony-finder_5.4.4+dfsg-1ubuntu8_all.deb ... Unpacking php-symfony-finder (5.4.4+dfsg-1ubuntu8) ... Selecting previously unselected package php-symfony-process. Preparing to unpack .../21-php-symfony-process_5.4.4+dfsg-1ubuntu8_all.deb ... Unpacking php-symfony-process (5.4.4+dfsg-1ubuntu8) ... Selecting previously unselected package php-react-promise. Preparing to unpack .../22-php-react-promise_2.7.0-2_all.deb ... Unpacking php-react-promise (2.7.0-2) ... Selecting previously unselected package composer. Preparing to unpack .../23-composer_2.2.6-2ubuntu4_all.deb ... Unpacking composer (2.2.6-2ubuntu4) ... Setting up php8.1-mbstring (8.1.2-1ubuntu2.26) ... Creating config file /etc/php/8.1/mods-available/mbstring.ini with new version Setting up jsonlint (1.8.3-2) ... Setting up php-psr-container (2.0.2-1) ... Setting up php-composer-pcre (1.0.1-1) ... Setting up php-symfony-service-contracts (2.4.0-1ubuntu2) ... Setting up php-mbstring (2:8.1+92ubuntu1) ... Setting up php-composer-ca-bundle (1.3.1-1) ... Setting up php-react-promise (2.7.0-2) ... Setting up php-psr-log (3.0.0-1) ... Setting up php-symfony-polyfill-php80 (1.24.0-1ubuntu2) ... Setting up php-composer-metadata-minifier (1.0.0-2) ... Setting up php-composer-semver (3.2.9-1) ... Setting up php-json-schema (5.2.11-1) ... Setting up php-composer-spdx-licenses (1.5.6-1) ... Setting up php8.1-intl (8.1.2-1ubuntu2.26) ... Creating config file /etc/php/8.1/mods-available/intl.ini with new version Setting up php-symfony-deprecation-contracts (2.4.0-1ubuntu2) ... Setting up php-symfony-finder (5.4.4+dfsg-1ubuntu8) ... Setting up php-composer-xdebug-handler (2.0.4-1build1) ... Setting up php-intl (2:8.1+92ubuntu1) ... Setting up php-symfony-process (5.4.4+dfsg-1ubuntu8) ... Setting up php-symfony-filesystem (5.4.4+dfsg-1ubuntu8) ... Setting up php-symfony-string (5.4.4+dfsg-1ubuntu8) ... Setting up php-symfony-console (5.4.4+dfsg-1ubuntu8) ... Setting up composer (2.2.6-2ubuntu4) ... Processing triggers for man-db (2.10.2-1) ... Processing triggers for php8.1-cli (8.1.2-1ubuntu2.26) ...
With Composer, per project (recommended — pins the version for the team):
composer require --dev phpcsstandards/php_codesniffer
Using version ^2.2 for phpcsstandards/php_codesniffer ./composer.json has been created Running composer update phpcsstandards/php_codesniffer Loading composer repositories with package information Updating dependencies Lock file operations: 1 install, 0 updates, 0 removals - Locking phpcsstandards/php_codesniffer (2.2.0) Writing lock file Installing dependencies from lock file (including require-dev) Package operations: 1 install, 0 updates, 0 removals - Downloading phpcsstandards/php_codesniffer (2.2.0) - Installing phpcsstandards/php_codesniffer (2.2.0): Extracting archive Package phpcsstandards/php_codesniffer is abandoned, you should avoid using it. Use squizlabs/php_codesniffer instead. Generating autoload files
composer remove phpcsstandards/php_codesniffer
phpcsstandards/php_codesniffer could not be found in require but it is present in require-dev Do you want to remove it from require-dev [yes]? yes ./composer.json has been updated Running composer update phpcsstandards/php_codesniffer Loading composer repositories with package information Updating dependencies Lock file operations: 0 installs, 0 updates, 1 removal - Removing phpcsstandards/php_codesniffer (2.2.0) Writing lock file Installing dependencies from lock file (including require-dev) Package operations: 0 installs, 0 updates, 1 removal - Removing phpcsstandards/php_codesniffer (2.2.0) Generating autoload files
composer require --dev squizlabs/php_codesniffer
Using version ^2.2 for squizlabs/php_codesniffer ./composer.json has been updated Running composer update squizlabs/php_codesniffer Loading composer repositories with package informati Updating dependencies Lock file operations: 1 install, 0 updates, 0 remova - Locking squizlabs/php_codesniffer (2.2.0) Writing lock file Installing dependencies from lock file (including re Package operations: 1 install, 0 updates, 0 removals - Downloading squizlabs/php_codesniffer (2.2.0) - Installing squizlabs/php_codesniffer (2.2.0): Ex Generating autoload files 1 package you are using is looking for funding. Use the `composer fund` command to find out more!
vendor/bin/phpcs --version
PHP Fatal error: Array and string offset access syntax with curly braces is no longer supported in /home/andrei/github/aredel/container/vendor/squizlabs/php_codesniffer/CodeSniffer/CLI.php on line 418
composer remove squizlabs/php_codesniffer
squizlabs/php_codesniffer could not be found in require but it is present in require-dev Do you want to remove it from require-dev [yes]? yes ./composer.json has been updated Running composer update squizlabs/php_codesniffer Loading composer repositories with package information Updating dependencies Lock file operations: 0 installs, 0 updates, 1 removal - Removing squizlabs/php_codesniffer (2.2.0) Writing lock file Installing dependencies from lock file (including require-dev) Package operations: 0 installs, 0 updates, 1 removal - Removing squizlabs/php_codesniffer (2.2.0) Generating autoload files
composer require --dev squizlabs/php_codesniffer:^3.13
In InitCommand.php line 904:
Package squizlabs/php_codesniffer at version ^3.13 has requirements incompatible with your PHP version, PHP extensions and Composer vers
ion:
- squizlabs/php_codesniffer 3.13.6 requires ext-simplexml * but it is not present.
- squizlabs/php_codesniffer 3.13.6 requires ext-xmlwriter * but it is not present.
require [--dev] [--dry-run] [--prefer-source] [--prefer-dist] [--prefer-install PREFER-INSTALL] [--fixed] [--no-suggest] [--no-progress] [--no-update] [--no-install] [--update-no-dev] [-w|--update-with-dependencies] [-W|--update-with-all-dependencies] [--with-dependencies] [--with-all-dependencies] [--ignore-platform-req IGNORE-PLATFORM-REQ] [--ignore-platform-reqs] [--prefer-stable] [--prefer-lowest] [--sort-packages] [-o|--optimize-autoloader] [-a|--classmap-authoritative] [--apcu-autoloader] [--apcu-autoloader-prefix APCU-AUTOLOADER-PREFIX] [--] [
php -v
PHP 8.1.2-1ubuntu2.26 (cli) (built: Sep 2 2026 12:42:10) (NTS) Copyright (c) The PHP Group Zend Engine v4.1.2, Copyright (c) Zend Technologies with Zend OPcache v8.1.2-1ubuntu2.26, Copyright (c), by Zend Technologies
sudo apt install php8.2-xml
[sudo] password for andrei: Reading package lists... Done Building dependency tree... Done Reading state information... Done E: Unable to locate package php8.2-xml E: Couldn't find any package by glob 'php8.2-xml'
apt list --installed 2>/dev/null | grep -i php | head
php-cli/jammy,jammy,now 2:8.1+92ubuntu1 all [installed] php-common/jammy,jammy,now 2:92ubuntu1 all [installed,automatic] php-composer-ca-bundle/jammy,jammy,now 1.3.1-1 all [installed,automatic] php-composer-metadata-minifier/jammy,jammy,now 1.0.0-2 all [installed,automatic] php-composer-pcre/jammy,jammy,now 1.0.1-1 all [installed,automatic] php-composer-semver/jammy,jammy,now 3.2.9-1 all [installed,automatic] php-composer-spdx-licenses/jammy,jammy,now 1.5.6-1 all [installed,automatic] php-composer-xdebug-handler/jammy,jammy,now 2.0.4-1build1 all [installed,automatic] php-intl/jammy,jammy,now 2:8.1+92ubuntu1 all [installed,automatic] php-json-schema/jammy,jammy,now 5.2.11-1 all [installed,automatic]
ls /etc/php/
8.1
Now we need either to upgrade PHP to 8.2 or install the missing extensions for 8.1. The latter is easier:
sudo apt install php8.1-xml
Reading package lists... Done Building dependency tree... Done Reading state information... Done The following packages were automatically installed and are no longer required: libfwupd2 libfwupdplugin5 libgcab-1.0-0 libllvm13 libsmbios-c2 libwpe-1.0-1 libwpebackend-fdo-1.0-1 nvidia-firmware-535-server-535.230.02 Use 'sudo apt autoremove' to remove them. The following NEW packages will be installed: php8.1-xml 0 upgraded, 1 newly installed, 0 to remove and 7 not upgraded. Need to get 120 kB of archives. After this operation, 484 kB of additional disk space will be used. Get:1 http://fi.archive.ubuntu.com/ubuntu jammy-updates/main amd64 php8.1-xml amd64 8.1.2-1ubuntu2.26 [120 kB] Fetched 120 kB in 0s (547 kB/s) Selecting previously unselected package php8.1-xml. (Reading database ... 257661 files and directories currently installed.) Preparing to unpack .../php8.1-xml_8.1.2-1ubuntu2.26_amd64.deb ... Unpacking php8.1-xml (8.1.2-1ubuntu2.26) ... Setting up php8.1-xml (8.1.2-1ubuntu2.26) ... Creating config file /etc/php/8.1/mods-available/dom.ini with new version Creating config file /etc/php/8.1/mods-available/simplexml.ini with new version Creating config file /etc/php/8.1/mods-available/xml.ini with new version Creating config file /etc/php/8.1/mods-available/xmlreader.ini with new version Creating config file /etc/php/8.1/mods-available/xmlwriter.ini with new version Creating config file /etc/php/8.1/mods-available/xsl.ini with new version Processing triggers for php8.1-cli (8.1.2-1ubuntu2.26) ...
php -m | grep -i -e simplexml -e xmlwriter
SimpleXML xmlwriter
composer require --dev squizlabs/php_codesniffer:^3.13
./composer.json has been updated Running composer update squizlabs/php_codesniffer Loading composer repositories with package information Updating dependencies Lock file operations: 1 install, 0 updates, 0 removals - Locking squizlabs/php_codesniffer (3.13.6) Writing lock file Installing dependencies from lock file (including require-dev) Package operations: 1 install, 0 updates, 0 removals - Downloading squizlabs/php_codesniffer (3.13.6) - Installing squizlabs/php_codesniffer (3.13.6): Extracting archive Generating autoload files 1 package you are using is looking for funding. Use the `composer fund` command to find out more!
vendor/bin/phpcs --version
PHP_CodeSniffer version 3.13.6 (stable) by Squiz and PHPCSStandards
Now we can run the code fixer on the file with the control signature sniff:
vendor/bin/phpcbf --standard=phpcs.xml --sniffs=Squiz.ControlStructures.ControlSignature -- src/ru/qa/.php/Page.php
PHPCBF RESULT SUMMARY --------------------------------------------------------------------------------- FILE FIXED REMAINING --------------------------------------------------------------------------------- /home/andrei/github/aredel/container/src/ru/qa/.php/Page.php 44 0 --------------------------------------------------------------------------------- A TOTAL OF 44 ERRORS WERE FIXED IN 1 FILE --------------------------------------------------------------------------------- Time: 265ms; Memory: 12MB
And then on the scope indent sniff:
vendor/bin/phpcbf --standard=phpcs.xml --sniffs=Generic.WhiteSpace.ScopeIndent -- src/ru/qa/.php/Page.php
PHPCBF RESULT SUMMARY --------------------------------------------------------------------------------- FILE FIXED REMAINING --------------------------------------------------------------------------------- /home/andrei/github/aredel/container/src/ru/qa/.php/Page.php 9 0 --------------------------------------------------------------------------------- A TOTAL OF 9 ERRORS WERE FIXED IN 1 FILE --------------------------------------------------------------------------------- Time: 246ms; Memory: 12MB
And finally on the closing brace sniffs:
vendor/bin/phpcbf --standard=phpcs.xml --sniffs=Squiz.WhiteSpace.ScopeClosingBrace,PSR2.Methods.FunctionClosingBrace -- src/ru/qa/.php/Page.php
No violations were found Time: 117ms; Memory: 10MB
Install on Linux (alternative)
Or once per user, available in every shell:
composer global require phpcsstandards/php_codesniffer export PATH="$HOME/.composer/vendor/bin:$PATH"
Without Composer, download the PHAR from the GitHub releases page of PHPCSStandards/PHP_CodeSniffer and run it with your PHP interpreter:
php phpcs.phar --version php phpcbf.phar --version
Install on macOS
Same Composer commands as on Linux. PHP itself comes either from the system, from Homebrew (brew install php) or from a Docker image — PHPCS only needs any PHP 7.2+ binary to run:
php -v composer require --dev phpcsstandards/php_codesniffer vendor/bin/phpcs --version
Install on Windows
Install PHP and Composer for Windows, then require the package. Composer creates vendor\bin\phpcs.bat wrappers, so both tools run from cmd or PowerShell:
composer require --dev phpcsstandards/php_codesniffer vendor\bin\phpcs --version vendor\bin\phpcs --standard=PSR12 file.php
The PHAR alternative works too, prefixing every call with php:
php phpcs.phar --standard=PSR12 file.php
Run with Docker
No host PHP at all: mount the project into an official PHP image together with the downloaded PHAR files:
docker run --rm -v "$PWD:/app" -w /app \ -v "$HOME/.cache/aredel-tools:/tools:ro" php:8.2-cli \ php /tools/phpcs.phar --standard=PSR12 --report=full app/
List every installed standard, and show the sniff code behind each message with -s:
phpcs -i phpcs -s --standard=PSR12 file.php
First run
Take a file with several typical style problems:
<?php
use App\Util;
use App\Helper;
$greeting="Hello";
$name="world";
echo $greeting.$name;
$enabled = TRUE;
class Greeter
{
public function say_hello()
{
return "hi";
}
}
phpcs --standard=PSR12 --report=full bad.php
FILE: bad.php
FOUND 12 ERRORS AND 1 WARNING AFFECTING 9 LINES
3 | ERROR | [x] Header blocks must not contain blank lines
7 | ERROR | [x] Expected at least 1 space before "="; 0 found
9 | ERROR | [x] Expected at least 1 space before "."; 0 found
9 | ERROR | [x] Whitespace found at end of line
10 | ERROR | [x] TRUE, FALSE and NULL must be lowercase
14 | ERROR | [ ] Method name "Greeter::say_hello" is not in camel caps format
18 | ERROR | [x] Expected 1 newline at end of file; 0 found
The rest of this article walks through each error type: what it means, how to fix it by hand, and what remains afterwards.
Spaces around operators
PSR-12 wants exactly one space on each side of = and the concatenation operator .. Before:
$greeting="Hello";
echo $greeting.$name;
After — the complaint disappears:
$greeting = "Hello";
echo $greeting . $name;
Lowercase true and false
TRUE, FALSE and NULL must be lowercase. Before:
$enabled = TRUE;
After:
$enabled = true;
Trailing whitespace
Spaces or tabs after the last visible character, and a missing newline at the very end of the file, are both errors. They are invisible in most editors — turn on render whitespace or let phpcbf strip them:
echo $greeting . $name;···
9 | ERROR | [x] Whitespace found at end of line
18 | ERROR | [x] Expected 1 newline at end of file; 0 found
Use block spacing
The header block — <?php, then use statements — must not contain blank lines. Before:
use App\Util;
use App\Helper;
After:
use App\Util;
use App\Helper;
CamelCase method names
Method names must be camelCase. Unlike every error above, this one carries no [x] mark — PHPCS will never fix it for you, because renaming a method means updating every caller:
public function say_hello()
14 | ERROR | [ ] Method name "Greeter::say_hello" is not in camel caps format
Rename by hand, then update each call site:
public function sayHello()
Fix automatically
Run the twin binary on the original file. It fixes all ten [x] violations in place — spacing, lowercase, header block, trailing whitespace, end-of-file newline:
phpcbf --standard=PSR12 bad.php
A TOTAL OF 10 ERRORS WERE FIXED IN 1 FILE
Re-checking shows only the two findings that need a human left: the file mixes a class with top-level code, and the method name from the previous chapter. After putting the code in a namespace and renaming the method, the report is empty:
phpcs --standard=PSR12 good.php
(no output — 0 errors, 0 warnings)
Always re-run phpcs after phpcbf: the fixer reports how much it touched, not what remains. And review the diff — automatic fixes are safe, but they are still changes to your code.
Article author: Andrei Olegovich
| Development with PHP | |
| PHP linters we use | |
| php -l and lint-php.sh | |
| PHPCS: PHP CodeSniffer | |
| phpcs.xml ruleset file | |
| PHPCBF: fix style automatically | |
| wrap-long-lines.sh | |
| PHPStan and Psalm |