← Back to blog

Magento 2 Coding Standard with PHPCS: Setup and Usage

Rodrigo Biazzi

Every team agrees that code should follow a standard. Few teams enforce it. In Magento the standard already exists, ships with the project as a dev dependency, and contains checks that go beyond formatting: unescaped output in templates, discouraged PHP functions, raw SQL and more. Running it is cheap. Not running it means those findings show up later in a Marketplace review, a security audit or production.

This article covers how to set up the Magento coding standard with PHP_CodeSniffer, configure it for a project, and run it in a way that people will not start ignoring.

What the Magento coding standard is

The standard is distributed as the Composer package magento/magento-coding-standard. It provides a PHP_CodeSniffer ruleset called Magento2, which builds on PSR-12 and adds Magento specific sniffs. It also pulls in PHPCompatibility, which checks that code runs on the PHP versions you target, and it includes ESLint configuration for JavaScript.

Some of the checks that matter most in practice:

  • Escaping in templates. The XSS template sniff flags output in .phtml files that does not go through an escaper method.
  • Discouraged functions. Calls like file_get_contents, serialize or curl_init, where Magento has framework alternatives such as the filesystem drivers, the serializer and the HTTP client.
  • Raw SQL. SQL written as strings instead of the select builder.
  • Language constructs. exit, die and backticks in application code.
  • Legacy patterns. Old install scripts, deprecated classes and similar leftovers.

These are not style preferences. Several of them map directly to security and maintainability problems.

Installation

Check first whether it is already there:

vendor/bin/phpcs -i

If the output lists Magento2, you are done. If not, require the package and register its path with PHPCS:

composer require --dev magento/magento-coding-standard
vendor/bin/phpcs --config-set installed_paths ../../magento/magento-coding-standard/,../../phpcompatibility/php-compatibility

The package's own composer.json contains scripts that set this path after install. If they did not run, for example because scripts were disabled, the manual command above does the same. The relative paths are resolved from the PHPCS install folder inside vendor.

A project ruleset

Passing --standard=Magento2 on every command works, but a phpcs.xml.dist in the project root is easier to share and lets you set paths, file extensions and exclusions once:

<?xml version="1.0"?>
<ruleset name="Project">
    <description>Coding standard for custom code</description>

    <file>app/code</file>
    <file>app/design</file>

    <arg name="extensions" value="php,phtml"/>
    <arg name="colors"/>
    <arg value="sp"/>

    <exclude-pattern>*/node_modules/*</exclude-pattern>
    <exclude-pattern>*/web/css/source/_module.less</exclude-pattern>

    <rule ref="Magento2"/>

    <config name="testVersion" value="8.3-"/>
</ruleset>

With that file in place, running vendor/bin/phpcs without arguments checks your code with your settings. Developers can keep a local phpcs.xml, which PHPCS prefers over the dist file, without changing the shared one.

The testVersion value tells PHPCompatibility to check against PHP 8.3 and newer. That is useful during a PHP upgrade, and it is covered in more depth in PHP 8 upgrade compatibility.

Do not point the ruleset at vendor or generated. Core and third party code is not yours to fix through PHPCS, and the output would bury your own findings.

Severity levels

Every finding in the Magento standard has a severity from 1 to 10. The highest levels are reserved for security and critical problems, the middle ones for errors that should be fixed, and lower ones for warnings and style. PHPCS lets you filter by severity:

vendor/bin/phpcs --severity=10
vendor/bin/phpcs --error-severity=1 --warning-severity=8

A practical approach is to fail CI on errors, show warnings at a high severity, and ignore the low severity warnings until the team is ready for them. A standard that produces hundreds of minor warnings on every run trains people to stop reading the output.

Fixing automatically with phpcbf

PHPCS comes with phpcbf, which fixes the findings that have an automatic fix:

vendor/bin/phpcbf app/code/Vendor/Shipping

It handles indentation, spacing, line endings, some docblock formatting and similar. It does not fix findings that need judgment, like escaping or discouraged functions. Run it on a clean working tree and review the diff before committing, since a large automatic change mixed with real work makes a pull request hard to review. Better yet, commit the formatting fix on its own.

Understanding a finding

A typical finding in a template:

<div class="label"><?= $block->getLabel() ?></div>

The XSS sniff reports this as unescaped output. The fix depends on the context:

<div class="label"><?= $escaper->escapeHtml($block->getLabel()) ?></div>
<a href="<?= $escaper->escapeUrl($block->getLink()) ?>" title="<?= $escaper->escapeHtmlAttr($block->getTitle()) ?>">

The $escaper variable is available in templates rendered by Magento 2.4 blocks. For output that is intentionally HTML, such as content from a WYSIWYG field already processed by a filter, the sniff accepts a @noEscape annotation in the output tag. Use it only when you can explain why the value is safe.

When a finding is wrong or not applicable, suppress it at the line with the specific sniff code instead of disabling the sniff for the whole file:

// phpcs:ignore Magento2.Functions.DiscouragedFunction
$content = file_get_contents($path);

Ask for a reason in code review every time someone adds an ignore. Often the framework alternative is the real fix, in this case the filesystem driver.

Checking only changed files

On a codebase with years of history, running the standard on everything for the first time returns thousands of findings. Fixing all of them in one go is risky and nobody wants to review it. The practical answer is to check only the files changed in a pull request:

git diff --name-only --diff-filter=ACMR origin/main...HEAD -- '*.php' '*.phtml' \
  | xargs -r vendor/bin/phpcs

New code must be clean. Old files get cleaned when someone touches them for another reason. After a few months the most active parts of the codebase are compliant, and the parts that never change can stay as they are.

In the IDE and in CI

PhpStorm has built in PHP_CodeSniffer integration. Point it at vendor/bin/phpcs inside your container or use a remote interpreter, enable the inspection and select the project ruleset. Findings then show up while typing, which is much cheaper than finding them in CI.

In CI, run the changed files check on pull requests and fail the job on errors. The pipeline in the GitHub Actions article has a PHPCS step you can adapt. Pair it with static analysis, since PHPCS checks how code is written and PHPStan checks whether it is correct. The setup for that is in the PHPStan article.

JavaScript and LESS

The package includes ESLint rules for Magento JavaScript. If your project has a lot of custom JS, run them through ESLint with the configuration from the package. For LESS, Magento core uses its own conventions documented in the frontend developer guide. There is no single official linter run in core projects, so agree on one as a team if you need it. For Hyva themes, the Tailwind and Alpine code follows the theme's own tooling instead.

What the standard does not catch

A clean PHPCS report does not mean good code. It will not notice a collection loaded in a loop, a plugin on the wrong method, or cacheable="false" on a common layout handle. Those are review topics. Use the standard to remove noise from code review so reviewers have time for the problems that matter.

FAQ

Is the Magento coding standard already installed in my project?

Usually yes. Projects created from the Magento project templates include magento/magento-coding-standard as a dev dependency. Run vendor/bin/phpcs -i to see whether Magento2 is in the list of installed standards.

What severity does Magento Marketplace use?

Marketplace technical review runs the same standard and focuses on the higher severity findings. Check the current Marketplace documentation for the exact threshold before submitting an extension.

Can phpcbf fix everything automatically?

No. It fixes formatting such as spacing, indentation and some docblock issues. Findings about escaping, discouraged functions or architecture need a developer to change the code.

How do I start using PHPCS on a large legacy codebase?

Check only changed files in CI. New code has to be clean and old code improves as it is touched, without a huge one-time cleanup pull request.

I am available for freelance Magento and Adobe Commerce work, including cleaning up legacy modules and setting up quality checks. If you need help with this, get in touch.

Related articles