Using Coder and PHP_CodeSniffer in GitLab CI

Last updated on
7 February 2024

This page has not yet been reviewed by Using GitLab to contribute to Drupal maintainer(s) and added to the menu.

This documentation needs review. See "Help improve this page" in the sidebar.

This documentation covers adding a phpcs.xml file to your contributed project.

What the GitLab CI test runner does

The default .gitlab-ci.yml runs PHP_CodeSniffer using the Drupal coding standard from the Coder project.

If you want to disable PHP_CodeSniffer, set the SKIP_PHPCS: '1' variable in your .gitlab-ci.yml file.

If your project requires a specific version of the Coder project, specify it in your composer.json file.

Customizing the Drupal coding standard

To customize the coding standard used in your project, include a phpcs.xml or phpcs.xml.dist file, and configure the sniffs you want to include or exclude. Like this fragment from the Examples for Developers project:

<ruleset name="drupal_examples">
  <description>Default PHP CodeSniffer configuration for Examples for Developers.</description>
  <file>.</file>
  <arg name="extensions" value="css,inc,install,module,php,profile,test,theme"/>
  <rule ref="Drupal.Array"/>
  <rule ref="Drupal.CSS"/>
  <rule ref="Drupal.Classes"/>
  <rule ref="Drupal.Commenting">
      <exclude name="Drupal.Commenting.DocComment.MissingShort"/>
      <!-- TagsNotGrouped has false positives for @code/@endcode. See
        https://www.drupal.org/node/2502837 -->
      <exclude name="Drupal.Commenting.DocComment.TagsNotGrouped"/>
      <!-- We have to document hooks in a non-standard way in order to be
        informative -->
      <exclude name="Drupal.Commenting.HookComment.HookParamDoc"/>
      <exclude name="Drupal.Commenting.HookComment.HookReturnDoc"/>
  </rule>

Rules are specified by sniff name. This is in contrast to Drupal core, which specifies rules by file location. Don't do this. Use the sniff name.

If you still want to be able to run PHPCS locally, you can either add the dealerdirect/phpcodesniffer-composer-installer package to the composer require-dev section, or add the following to your phpcs.xml:

<config name="installed_paths" value="../../drupal/coder/coder_sniffer/Drupal,../../drupal/coder/coder_sniffer/DrupalPractice" />

Adding third-party sniffs

GitLab CI supports the following rulesets out of the box: Zend, SquizPSR1, PSR2, PSR12, PEAR, MySource, DrupalPractice and Drupal. Additional sniffs can be added by including the ruleset in composer.json, and defining the rules in phpcs.xml. Here is an example that adds a number of rules from the slevomat/coding-standard ruleset:

First, add the ruleset to composer.json:

$ composer require --dev slevomat/coding-standard

Then, customize your phpcs.xml file with the rules you'd like to include. Here is an example of a fully featured phpcs.xml file:

<?xml version="1.0" encoding="UTF-8"?>
<!-- https://www.drupal.org/docs/develop/git/using-gitlab-to-contribute-to-drupal/using-coder-and-php_codesniffer-in-gitlab-ci -->
<ruleset name="my_drupal_module">
    <description>PHP CodeSniffer configuration for a Drupal module.</description>

    <arg name="extensions" value="php,inc,module,install,info,test,profile,theme,css,js"/>
    <arg name="report" value="full"/>
    <arg value="p"/>

    <!-- Exclude third party libraries. -->
    <exclude-pattern>./vendor</exclude-pattern>

    <!-- Exclude unsupported file types. -->
    <exclude-pattern>*.gif</exclude-pattern>
    <exclude-pattern>*.less</exclude-pattern>
    <exclude-pattern>*.png</exclude-pattern>

    <!-- Minified files don't have to comply with coding standards. -->
    <exclude-pattern>*.min.css</exclude-pattern>
    <exclude-pattern>*.min.js</exclude-pattern>

    <!-- Include the standard Drupal coding standard. -->
    <rule ref="Drupal" />

    <!-- Include the best practices for Drupal development. -->
    <rule ref="DrupalPractice" />

    <!-- Require the strict types declaration in every PHP file. -->
    <rule ref="SlevomatCodingStandard.TypeHints.DeclareStrictTypes">
        <properties>
            <property name="linesCountBeforeDeclare" value="1"/>
            <property name="linesCountAfterDeclare" value="1"/>
            <property name="spacesCountAroundEqualsSign" value="0"/>
        </properties>
    </rule>

    <!-- Require nullable types to be declared as such. -->
    <rule ref="SlevomatCodingStandard.TypeHints.NullableTypeForNullDefaultValue">
    </rule>

    <!-- Forbid annotations which are not recognized by the Drupal API documentation standard but are often added by IDEs. -->
    <rule ref="SlevomatCodingStandard.Commenting.ForbiddenAnnotations">
        <properties>
            <property name="forbiddenAnnotations" type="array">
                <element value="@author"/>
                <element value="@created"/>
                <element value="@copyright"/>
                <element value="@license"/>
                <element value="@package"/>
                <element value="@version"/>
            </property>
        </properties>
    </rule>

    <!-- Forbid documentation auto-generated by IDEs which does not align with the Drupal documentation standards. -->
    <rule ref="SlevomatCodingStandard.Commenting.ForbiddenComments">
        <properties>
            <property name="forbiddenCommentPatterns" type="array">
                <element value="/@inheritDoc/"/>
                <element value="/^Class [a-zA-z]*\.$/"/>
                <element value="/^Interface [a-zA-z]*\.$/"/>
                <element value="/^[a-zA-z]* constructor\.$/"/>
            </property>
        </properties>
    </rule>

    <!-- Enforce correct formatting of return types hints. -->
    <rule ref="SlevomatCodingStandard.TypeHints.ReturnTypeHintSpacing"/>

    <!-- Use statements should be ordered alphabetically. -->
    <rule ref="SlevomatCodingStandard.Namespaces.AlphabeticallySortedUses">
        <properties>
            <property name="caseSensitive" value="true"/>
        </properties>
    </rule>
</ruleset>

Help improve this page

Page status: Needs review

You can: