Используем PHPCS в WordPress с WPCS стандартом

Используем PHPCS в WordPress с WPCS стандартом

В этой статье мы поговорим о том, что такое PHPCS линтер, а так-же рассмотрим: важность использования на проекте в команде, его настройку, интеграцию PHPCS с WPCS стандартом кодирования, и применение в Github Actions.

Что такое Линтер

Linter — это инструмент статического анализа кода, проверяющий наличие ошибок, багов, уязвимостей.
Как и что проверять статическому анализатору — настраивается в конфигурационном файле.
Подробнее о linter читайте в Wiki.

Есть разные виды статических анализаторов: Phan, PHPStan, PSalm, PHPCS, etc.
Нужно иметь ввиду что не все они на самом деле являются полностью статическими. Например PHPStan анализатор использует под капотом и гибридную рефлексию (Runtime Reflection, и Static Reflection) для оптимизации CPU time и RAM allocation.
Использование Runtime Reflection может привести к неприятным сайд эффектам, как например: «Выполнение кода класса или функции которая вызывается в анализируемом файле». Подробнее по ссылке.

Хоть PHPCS, PHPStan, Phan, PSalm все являются статическими анализаторами, используются они по разному. Последняя троица используются для сложного синтаксического анализа кода с построением глубокого AST (abstract syntax tree) что позволяет выявлять неиспользуемый код, обнаружить несоответствие типов, предотвратить runtime errors, что в свою очередь позволяет их применять при рефакторинге и поддержке легаси кода.

Но далее хотелось бы подробнее поговорить о PHPCS.

PHPCS (PHP_CodeSniffer)

PHPCS — это инструмент (пакет composer) для упрощенного статического анализа кода на базе построения AST, проверяющий код на соотстветствие стандартов кодирования таких как PSR2, PSR12 или WPCS (WordPress Coding Standards).

PHP_CodeSniffer помогает нам:

  • Контролировать соблюдение единых стандартов кода (это особенно важно, когда над кодом работают разные люди).
  • Писать последовательный, легко читаемый и поддерживаемый код.
  • Уменьшить количество багов и уязвимостей.

Из коробки линтер поддерживает: MySource, PEAR, PSR 1, PSR 2, PSR 12, Squiz, Zend стандарты кодирования.
В случае необходимости, вы можете дополнительно установить стандарт который вам требуется, например WPCS или даже создать свой собственный.
Он так же поддерживает кастомизацию стандартов кодирования под конкретные нужны проекта, что мы и рассмотрим в этой статье.

PHPCS можно использовать из командной строки или интегрировать в IDE (e.g. PhpStrom), а так же его можно встроить в CI/CD процессы.

В Composer пакете PHP_CodeSniffer имеется 2 бинарника:

  • phpcs — скрипт анализа кода на наличие нарушений стандартов кодирования.
  • phpcbf — скрипт автоматического исправления этих нарушений.

Как установить

Добавим PHPCS как composer зависимость:

composer require "squizlabs/php_codesniffer" --dev

Флаг --dev обязателен, т.к. при использовании его, PHPCS установится в dev dependencies, которые в свою очередь не будут инсталированы для production среды при грамотной установке composer пакетов (используем на проде команду composer install --no-dev).

Добавьте директиву scripts с командами в composer.json, чтобы вы могли запускать скрипты phpcs и phpcbf через композер.
Это поможет избежать ошибок связанных с запуском неверной версии бинарника, т.к их запуск менеджит сам Composer.

//..... 
  "require-dev": {
    "squizlabs/php_codesniffer": "^3.7"
  },
  "scripts": {
    "phpcs": "phpcs",
    "phpcbf": "phpcbf"
  }
}

Далее создадим файл в проете PHPCSWorkExample.php с намеренно нарушенным кодстайлом.
Я бы рекомендовал скопировать файл целиком из репозитория (see commit). Т.к. при вставке ваша IDE может поменять форматирование.

<?php
 
class PHPCSWorkExample {
    private $name = 'test';
	function get_test_name() {
      return $this->name;
	}
}

Следующим шагом попробуем запустить линтер.
Флаг --standard=PSR2 означает что мы будем проверять файл index.php на соответствие PSR-2: Coding Style Standard.

composer run phpcs -- --standard=PSR2 ./PHPCSWorkExample.php

И что же мы видим?
— Список ошибок несоотстветствия PSR2 стандарту файла PHPCSWorkExample.php и вконце наша команда завершается с error code.

> phpcs '--standard=PSR2' './PHPCSWorkExample.php'
FILE: /usr/src/myapp/PHPCSWorkExample.php
---------------------------------------------------------------------------------------------------
FOUND 8 ERRORS AFFECTING 4 LINES
---------------------------------------------------------------------------------------------------
 3 | ERROR | [ ] Each class must be in a namespace of at least one level (a top-level vendor name)
 3 | ERROR | [x] Opening brace of a class must be on the line after the definition
 6 | ERROR | [x] Spaces must be used to indent lines; tabs are not allowed
 6 | ERROR | [ ] Method name "PHPCSWorkExample::get_test_name" is not in camel caps format
 6 | ERROR | [ ] Visibility must be declared on method "get_test_name"
 6 | ERROR | [x] Opening brace should be on a new line
 7 | ERROR | [x] Line indented incorrectly; expected at least 8 spaces, found 6
 8 | ERROR | [x] Spaces must be used to indent lines; tabs are not allowed
---------------------------------------------------------------------------------------------------
PHPCBF CAN FIX THE 5 MARKED SNIFF VIOLATIONS AUTOMATICALLY
---------------------------------------------------------------------------------------------------
Time: 61ms; Memory: 6MB
Script phpcs handling the phpcs event returned with error code 2

Обратите внимание на строчку в output console:

//..
PHPCBF CAN FIX THE 5 MARKED SNIFF VIOLATIONS AUTOMATICALLY
//...

PHPCS нам предлагает автоматически исправить найденные нарушения посредством использования PHPCBF команды.

Как использовать PHPCBF (PHP Code Beautifier and Fixer)

Послушаем совет PHPCS, и попробуем профиксить ошибки использованием PHPCBF команды:

composer run phpcbf -- --standard=PSR2 ./PHPCSWorkExample.php

Мы видим, что PHPCBF исправил 5 ошибок в файле, но осталась 3 нерешенные.

/usr/src/myapp # composer run phpcbf -- --standard=PSR2 ./PHPCSWorkExample.php
> phpcbf --standard=./phpcs.xml '--standard=PSR2' './PHPCSWorkExample.php'
PHPCBF RESULT SUMMARY
----------------------------------------------------------------------
FILE                                                  FIXED  REMAINING
----------------------------------------------------------------------
/usr/src/myapp/PHPCSWorkExample.php                   5      3
----------------------------------------------------------------------
A TOTAL OF 5 ERRORS WERE FIXED IN 1 FILE
----------------------------------------------------------------------
Time: 50ms; Memory: 6MB
Script phpcbf --standard=./phpcs.xml handling the phpcbf event returned with error code 1

Давайте это проверим и выполним еще раз команду проверки кодстайлов

composer run phpcbf -- --standard=PSR2 ./PHPCSWorkExample.php

И действительно, мы видим что у нас теперь всего 3 ошибки, которые автоматически не получилось профиксить:

-----------------------------------------------------------------------------------------------
FOUND 3 ERRORS AFFECTING 2 LINES
-----------------------------------------------------------------------------------------------
 3 | ERROR | Each class must be in a namespace of at least one level (a top-level vendor name)
 7 | ERROR | Method name "PHPCSWorkExample::get_test_name" is not in camel caps format
 7 | ERROR | Visibility must be declared on method "get_test_name"
-----------------------------------------------------------------------------------------------

Так произошло, потому что PHPCBF не всегда может исправить все ошибки в коде.

Это может происходить по нескольким причинам:

  • PHPCBF не может исправить ошибки в случае, если код является синтаксически неправильным, содержит логические ошибки или требует дополнительного программирования. В таких случаях, необходимо производить ручную правку кода.
  • PHPCBF может быть не настроен на использование всех правил кодирования, которые используются на проекте. Если вы используете нестандартные правила, которые не поддерживаются PHPCBF, то он не сможет исправить соответствующие ошибки.
  • Некоторые правила PHPCS необходимо игнорировать вручную, когда того требует ситуация. (Об этом ниже).

Исправить такие ошибки вы можете только вручную, полагаясь на используемый стайлгайд и сообщения от PHPCS в консоли.

После всех мануальных исправлений наш файл будет выглядеть так:

<?php
namespace APP;
 
class PHPCSWorkExample
{
    private $name = 'test';
    public function getTestName()
    {
        return $this->name;
    }
}

Помните, что автоматические инструменты коррекции кода, могут приводить к нежелательным изменениям в вашем коде, поэтому всегда следует быть внимательным и проверять внесенные изменения вручную.

При нахождении большого количества ошибок, я рекомендую вначале закоммитить код, а потом запустить команду PHPCBF и сравнить diff файлов. Тогда вы будите уверены что автофикс прошел успешно.

WPCS (WordPress Coding Standards)

WPCS (WordPress Coding Standards) — это набор правил к написанию кода под WordPress.

WordPress Coding Standards для PHPCS — это коллекция PHP_CodeSniffer правил (sniffs) для проверки кода написанного для WordPress.

Как установить WPCS стандарт

Вы можете установить WPCS набор правил используя Composer:

composer require wp-coding-standards/wpcs --dev

На момент написания этой статьи 13.04.2023, пакет с WPCS снифами не работает с PHP выше 7.4

Чтобы установить пакет с фиксами, используйте dev-develop branch:
composer require wp-coding-standards/wpcs:"dev-develop" --dev .
Подробнее по ссылке issues/2087

После установки WPCS стандарта выполним команду чтобы посмотреть какие стандарты сейчас подключены к PHPCS:

composer run phpcs -- -i
The installed coding standards are MySource, PEAR, PSR1, PSR2, PSR12, Squiz and Zend

Но где же наши WordPress Coding Standards?

Для того чтобы они подтянулись, нам нужно добавить пакет позволяющий легко использовать новые PHP_CodeSniffer стандарты, без дополнительной мануальной конфигурации их подключения:

composer require --dev dealerdirect/phpcodesniffer-composer-installer

Теперь если мы выполним команду composer run phpcs -- -i , мы увидим, что все WPCS стандарты нам доступны:

The installed coding standards are MySource, PEAR, PSR1, PSR2, PSR12, Squiz, Zend, WordPress, WordPress-Core, WordPress-Docs and WordPress-Extra

Предлагаю проверить насколько наш файл PHPCSWorkExample.php соответствует WPCS стандарту:

composer run phpcs -- --standard=WordPress ./PHPCSWorkExample.php

И получаем результат

> phpcs '--standard=WordPress' './PHPCSWorkExample.php'
FILE: /usr/src/myapp/PHPCSWorkExample.php
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
FOUND 16 ERRORS AND 1 WARNING AFFECTING 9 LINES
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
  1 | ERROR   | [ ] Filenames should be all lowercase with hyphens as word separators. Expected phpcsworkexample.php, but found PHPCSWorkExample.php.
  1 | ERROR   | [ ] Class file names should be based on the class name with "class-" prepended. Expected class-phpcsworkexample.php, but found PHPCSWorkExample.php.
  1 | ERROR   | [ ] Missing file doc comment
  4 | WARNING | [ ] Found precision alignment of 1 spaces.
  4 | ERROR   | [x] Whitespace found at end of line
  5 | ERROR   | [ ] Missing doc comment for class PHPCSWorkExample
  6 | ERROR   | [x] Opening brace should be on the same line as the declaration for class PHPCSWorkExample
  7 | ERROR   | [x] Tabs must be used to indent lines; spaces are not allowed
  7 | ERROR   | [ ] Missing member variable doc comment
  9 | ERROR   | [x] Tabs must be used to indent lines; spaces are not allowed
  9 | ERROR   | [x] Expected exactly one space between closing parenthesis and opening control structure; "
    |         |     " found.
  9 | ERROR   | [ ] Method name "getTestName" in class PHPCSWorkExample is not in snake case format, try "get_test_name"
  9 | ERROR   | [ ] Missing doc comment for function getTestName()
 10 | ERROR   | [x] Tabs must be used to indent lines; spaces are not allowed
 10 | ERROR   | [x] Opening brace should be on the same line as the declaration
 11 | ERROR   | [x] Tabs must be used to indent lines; spaces are not allowed
 12 | ERROR   | [x] Tabs must be used to indent lines; spaces are not allowed
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
PHPCBF CAN FIX THE 9 MARKED SNIFF VIOLATIONS AUTOMATICALLY
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
Time: 750ms; Memory: 6MB
Script phpcs handling the phpcs event returned with error code 2

Запускаем автофиксер PHPCBF снова, и у нас остается 7 ошибок в 4 линиях.

--------------------------------------------------------------------------------------------------------------------------------------------------------------
 1 | ERROR | Filenames should be all lowercase with hyphens as word separators. Expected phpcsworkexample.php, but found PHPCSWorkExample.php.
 1 | ERROR | Class file names should be based on the class name with "class-" prepended. Expected class-phpcsworkexample.php, but found PHPCSWorkExample.php.
 1 | ERROR | Missing file doc comment
 5 | ERROR | Missing doc comment for class PHPCSWorkExample
 7 | ERROR | Missing member variable doc comment
 9 | ERROR | Method name "getTestName" in class PHPCSWorkExample is not in snake case format, try "get_test_name"
 9 | ERROR | Missing doc comment for function getTestName()
--------------------------------------------------------------------------------------------------------------------------------------------------------------

Когда вы выполните все PHPCS требования к кодстайлам, ваш файл будет переименован в class-phpcsworkexample.php и будет выглядеть следующим образом:

<?php
/**
 * PHPCSWorkExample
 *
 * @package APP
 */
namespace APP;
/**
 * PHPCSWorkExample
 */
class PHPCSWorkExample {
	/**
	 * Test name.
	 *
	 * @var string $name
	 */
	private $name = 'test';
	/**
	 * Get test name.
	 *
	 * @return string
	 */
	public function get_test_name() {
		return $this->name;
	}
}

Некоторые ошибки которые выбросил PHPCS могут показаться избыточными, и могут только ухудшать UserExpirience при работе с таким кодом как например «обязательное строгое именование файлов классов».

— И да, вы будите правы, как раз в следующей главе мы разберем как настраивать кастомные WPCS правила, под наш проект.

Настройка WPCS правил (Best Practices)

PHP_CodeSniffer дает возможность разработчикам сконфигурировать свой собстенный coding standards посредством создания phpcs.xml файла с набором собственных правил. В файле мы можем использовать уже существующие стандарты кодирования и модифицировать их упрощая или делая их более строгими под нужды нашего проекта.

Базовые настройки

Для начала необходимо создать файл в корне phpcs.xml со следующим содержимым

<?xml version="1.0"?>
<ruleset name="WordPress Coding Standards">
    <description>A custom set of code standard rules to check for WordPress code.</description>
    <!-- How to scan -->
    <arg value="sp"/><!-- Show sniff and progress -->
    <arg name="basepath" value="./"/><!-- Strip the file paths down to the relevant bit -->
    <arg name="extensions" value="php"/>
    <arg name="parallel" value="10"/><!-- Enables parallel processing when available for faster results. -->
    <arg name="cache" value=".phpcs.cache"/>
    <!-- What to scan -->
    <file>./</file>
    <!-- Exclude basic project directories -->
    <exclude-pattern>*/\.idea/*</exclude-pattern>
    <exclude-pattern>*/\.github/*</exclude-pattern>
    <exclude-pattern>*/vendor/*</exclude-pattern>
    <exclude-pattern>*/node_modules/*</exclude-pattern>
    <exclude-pattern>*/assets/*</exclude-pattern>
    <exclude-pattern>wp-content/uploads/</exclude-pattern>
    <rule ref="WordPress"/>
</ruleset>

В конфиге мы указали что используем WordPress ruleset, сканируем только php файлы, отображаем процесс сканирования, запускаем в 10 потоков для ускорения анализа и кешируем результат в .phpcs.cache файл.

Теперь, когда наш конфиг готов, остается только указать откуда PHPCS будет его подхватывать.
Идем в файл composer.json и правим scripts блок:

  "scripts": {
    "phpcs": "phpcs --standard=./phpcs.xml",
    "phpcbf": "phpcbf --standard=./phpcs.xml"
  },

Теперь можно выполнять команды с учетом этого конфига

composer run phpcs
composer run phpcbf

Добавить кеш

Обазательно добавьте директиву с кешированием результатов PHPCS, т.к. это поможет сэкономить огромное количество времени при повторном запуске линтера:

<arg name="cache" value=".phpcs.cache"/>

Что сканировать

Я рекомендую использовать подход «Сканировать все, кроме того что запрещено».
Т.е. нам необходимо разрешить сканировать все в директориях /mu-plugins/, /plugins, /themes кроме того что запрещено. А в запрещенных будут third party plugins и theme, которые вы будите прописывать в PHPCS конфиг в виде исключений вручную.

<!-- .... --> 
   <!-- What to scan -->
    <file>wp-content/mu-plugins/</file>
    <file>wp-content/plugins/</file>
    <file>wp-content/themes/</file>
    <!-- Exclude "Third-party Themes" -->
    <exclude-pattern>wp-content/themes/twentytwenty/</exclude-pattern>
    <!-- Exclude "Third-party Plugins" -->
    <exclude-pattern>wp-content/plugins/advanced-custom-fields-pro/</exclude-pattern>
<!-- .... -->

Почему так нужно делать?
Ответ прост: если девелопер создаст новую тему или плагин, то ваш код по умолчанию попадет в область сканирования PHPCS и ваш CI никогда не пропустит нарушения стандартов, что не скажешь о подходе «Сканировать только то что разрешено». При использовании второго, будут регулярно случаться случаи мержа кода с нарушением стандартов.

Не используйте YodaConditions

Yoda Conditionals одно из самых разражающий правил WPCS стандарта, которое приносит больше проблем чем пользы.
Приведу цитату из HumanMade CodeStyle Convention которая подробно раскрывает всю боль:

<!—
Ладно, сейчас реальный разговор. Yoda Conditions — это нелепость.
Фундаментальная проблема, которую пытаются решить Yoda conditions, заключается в следующем:
оператор равенства находится очень близко к оператору присваивания.
Легко ошибиться и случайно присвоить переменную, которую вы пытаетесь проверить.
Но вот в чем дело. Yoda Conditions просто не читаются корректно
в потоке чтения кода. Они требуют изменить наш образ мышления.
Вместо того чтобы заставлять каждое условие быть обратным, почему бы не запретить
присваивание из условий? На самом деле вам никогда не нужно присваивать в условных выражениях.
Итак, вот моя позиция: нет Yoda conditions. Да — запрету
на использование оператора присвоения значения в условных выражениях.
—>

Источник: https://github.com/humanmade/coding-standards/blob/master/HM/ruleset.xml

Запретим использование YodaConditionals в phpcs.xml

<!-- .... -->
<rule ref="WordPress">
    <!-- .... -->
    <exclude name="WordPress.PHP.YodaConditions" />
    <!-- .... -->
</rule>
<!-- .... -->
<!-- Prohibit Yoda Conditions expressions -->
<rule ref="Generic.ControlStructures.DisallowYodaConditions"/>
<!-- .... -->

Отключение PHPDocBlock

Правила WPCS относящиеся к комментариям, довольно строгие и избыточные.
При грамотном написании кода, вам не должно быть необходимо писать комментарий к кадому свойству/методу/классу/файлу.
Названия ваших функций, классов, файлов, свойств — должны описавать что происходит внутри или к какой сущности они относятся.

Пишите комментарии там где это только необходимо.

Ослабим правила комментирования следующими настройками:

<!-- .... -->
<rule ref="WordPress">
        <!-- .... -->   
        <exclude name="Generic.Commenting.DocComment.MissingShort" />
        <!-- Disable dot of end string docblock -->
        <exclude name="Squiz.Commenting.InlineComment.InvalidEndChar" />
        <!-- .... -->
</rule>
<!-- .... -->
 <!-- Disable comments blocks -->
    <rule ref="WordPress-Docs">
        <exclude name="Squiz.Commenting.FileComment.Missing" />
        <exclude name="Squiz.Commenting.FileComment.MissingPackageTag" />
        <exclude name="Squiz.Commenting.ClassComment.Missing" />
        <exclude name="Squiz.Commenting.FunctionComment.Missing" />
        <exclude name="Squiz.Commenting.FunctionComment.SpacingAfterParamType" />
        <exclude name="Squiz.Commenting.VariableComment.Missing" />
        <exclude name="Squiz.Commenting.FunctionComment.MissingParamComment" />
        <exclude name="Squiz.Commenting.FunctionComment.MissingParamTag" />
    </rule>
<!-- .... -->

Файлы классов в CamelCase или Snake_Case

В WPCS rules, строгие правила к названиям файлов классов, и чтобы наш файл соответсовал им, нам пришлось его переименовать в class-phpcsworkexample.php , что в свою очередь не добавляет читаемости именам файлов, чтобы вычленить оттуда название сущностей нужно обладать сноровкой.
Такие имена классов совсем не соответствуют принятым стандартам в PHP, и более того не поддаются здравой логике.
Предлагаю отключить эти правила, что нам позволит называть файлы классов в CamelCase (по PSR) или в Snake_Case формате (советую придерживаться CamelCase, т.к. это PSR формат).

<!-- .... --> 
   <rule ref="WordPress">
        <!-- .... -->
        <exclude name="WordPress.Files.FileName.InvalidClassFileName" />
        <exclude name="WordPress.Files.FileName.NotHyphenatedLowercase" />
        <!-- .... -->
    </rule>
<!-- .... -->

Запрет на использование функций

Некоторые из функций должны быть запрещены по соображениям безопасности. Другие являются функциями дебага и не должны оказаться в продакшене. Третьи же могут породить потенциальные ошибки при рефакторинге. Для этих целей в PHPCS вы можете добавить блок правил приведенный ниже.
Так же вы можете его расширить своими функциями.
Например: «deprecated» функциями вашего проекта, которые нуждаются в рефакторинге или замене.

<!-- .... -->
    <!-- Forbidden functions -->
    <rule ref="Generic.PHP.ForbiddenFunctions">
        <properties>
            <property name="forbiddenFunctions" type="array">
                <element key="delete" value="unset"/>
                <element key="print" value="echo"/>
                <element key="create_function" value="null"/>
                <element key="sizeof" value="count"/>
                <!-- <element key="var_dump" value="null"/> duplicate of WP rule-->
                <element key="print_r" value="null"/>
                <element key="eval" value="null"/>
                <element key="compact" value="null"/>
            </property>
        </properties>
    </rule>
<!-- .... -->

AutoEscaped функции

Некоторые из ваших функций могут уже содержать escaping внутри и к сожалению WPCS не поймет этого.
У вас в этом случае есть 2 варианта как это обойти:

  • Использовать phpcs:ignore в коде где вы используете эту функцию
    //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
  • Добавить в блок auto escaped functions в phpcs.xml файл
<!-- .... -->
  
  <!-- AutoEscaped functions -->
    <rule ref="WordPress.Security.EscapeOutput">
        <properties>
            <property name="customAutoEscapedFunctions" type="array">
                <element value="rd_render_attributes" />
                <element value="rd_get_picture_html" />
            </property>
        </properties>
    </rule>
<!-- .... -->

Я рекомендую использовать 2й вариант, т.к. ваш код будет чище.

Разрешить PHP short syntax

<!-- .... -->
    <rule ref="WordPress">
        <!-- Allow short ternary syntax and short arrays and short open tag -->
        <exclude name="Generic.Arrays.DisallowShortArraySyntax" />
        <exclude name="WordPress.PHP.DisallowShortTernary.Found" />
        <exclude name="Generic.PHP.DisallowShortOpenTag.EchoFound" />
    </rule>
<!-- .... -->
    <!-- Disallow long array syntax. -->
    <rule ref="Generic.Arrays.DisallowLongArraySyntax.Found"/>
<!-- .... -->

В описанных выше правилах мы разрешим Short Array Syntax и запретим Long Array Syntax для соблюдения единой конвенции стилей

<?php
//available
$data = [
	'one' => 'one-data',
	'two' => 'two-data',
	'three' => 'three-data',
];
//unvailable
$data = array(
	'one' => 'one-data',
	'two' => 'two-data',
	'three' => 'three-data',
);

Так же разрешим Ternary Syntax

<?php
//available
$data = $request ? $request->data : null;

И разрешим использовать Short Open Tag with Echo <?= :

<!--available -->
<p><?= $a ?><p>

Не стоит путать его с ASP style tag <? , т.к. второй был задепрекейчен в PHP и не поддерживается более, в то время как <?= позволяет писать нам более лаконичный код.

Использование «/» в именах хуков

Разрешить использовать / в именах хуков, позволит нам писать выразительные хуки напоминающие концепцию неймспейсов:

<?php 
//available
$path_to_font = apply_filters( 'rd/plugin-name/class-name/component-name', $data );
<!-- .... -->   
    <!-- Allow symbol `/` in hook names. -->
    <rule ref="WordPress.NamingConventions.ValidHookName">
        <properties>
            <property name="additionalWordDelimiters" value="/" />
        </properties>
    </rule>
<!-- .... -->

У нас на проекте этот код-стайл команда разработчиков полюбила довольно быстро, т.к. у всех возникла прямая аналогия с namespaces концепцией.

Уровень вложненности инструкций

Это правило определяет уровень вложенности инструкций if else, while, forach, for etc, и выводит ошибку в случае превышения:

<!-- .... -->    
   <rule ref="Generic.Metrics.NestingLevel">
        <properties>
            <property name="absoluteNestingLevel" value="3"/>
        </properties>
    </rule>
<!-- .... -->   

Это правило неприменно влияет на качество кода и его поддержку, при высоком уровне вложенности читать код становится сложно.
Приведу наглядный пример, при уровне вложенности 2 мы сможем писать следующий код:

<?php
if ( $data['request'] ) {
	if ($data['request']['method'] === 'GET' ) {
		echo "Hello World!";
	}
}

Но если выставим значение absoluteNestingLevel на 1, то у нас появится ошибка.

Я рекомендую вам ставить его на 3, но иногда бывают сложные легаси проекты, где приходится выставлять на 4. В целом никто не мешает добавить 2 правила, для легаси кода с absoluteNestingLevel=4 и для нового кода с absoluteNestingLevel=3, лишь нужно будет указать директории для каждого блока правил.

Провека кода на совместимость WordPress версий

В WPCS есть сниф для проверки кода на минимальную совместимую версию WP. Этот сниф проверяет deprecated functions, classes, параметры functions.
Для удаленных функций до  minimum_wp_version — будет выбрашена ошибка. При определении устаревших функций (depecated) между minimum_wp_version и текущим релизом, вы получите warning.

Например: функция add_object_page() была признана устаревшей в WP 4.5. Если обнаружено использование этой функции, линтер выдаст ошибку, если minimum_wp_version равно 4.6 или выше. Линтер выдаст warning, если значение minimum_wp_version равно 4.5 или ниже.

Добавим это правило в наш phpcs.xml:

    <!-- Minimum WP version to check for usage of deprecated functions, classes and function parameters -->
    <config name="minimum_wp_version" value="5.4.1"/>

До версии WordPressCS 3.0.0 эта директива конфигурации называлась minimum_supported_wp_version.

Указывайте ту версию WP, которую сейчас используете или ту на которую хотите перейти, чтобы проверить ваш код на совместимость.

Почитать подробнее про minimum_wp_version можно по ссылке.

Проверка кода на совместимость PHP версий

Используя PHPCompatibilityWP, вы можете проанализировать кодовую базу вашего WordPress проекта на совместимость с разными версиями PHP.

composer require --dev phpcompatibility/phpcompatibility-wp:"*"

Если все прошло успешно выполнив команду ниже, вы увидите PHPCompatibility и PHPCompatibilityWP в доступных кодстайлах

vendor/bin/phpcs -i

Чтобы наш анализатор проверял совместимость с PHP версиями, добавим следующие правила в phpcs.xml :

<!-- Check PHP version compatibility -->
<rule ref="PHPCompatibilityWP"/>
<!-- Check for cross-version support for PHP 7.2 and higher. -->
<config name="testVersion" value="7.2-"/>

Теперь наш анализируемый код будет проверяться на совместимость минимальной версии PHP 7.2. И это позволит нам проверять код на deprecated/removed PHP функции, а так же определять использование новых неподдерживаемых фич PHP.

Прочие правила

С другими кастомными правилами, вы можете познакомиться по ссылке.

Игнорирование правил в коде

PHPCS помимо настройки конфига, имеет возможность точечно в коде игнорировать правила.

Нужно лишь добавить к игнорируемой строке // phpcs:ignore .

<?php
$test = "Hello World";
echo $test; //phpcs:ignore

Рекомендую указывать правило которое вы игнорируете, т.к. игнорируя все правила // phpcs:ignore вы можете пропустить уязвимость в вашем коде, которая будет добавлена после.

Следующий пример депонстрирует игнорирование только правила WordPress.DB.SlowDBQuery.slow_db_query_meta_query

<?php
//......
$duplicated_posts = new \WP_Query(
	[
		'post_type'  => 'any',
		'meta_query' => [ // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
			[
				'key'   => META_DP_ORIGINAL,
				'value' => $post_id,
			],
		],
	]
);

Сами правила на которые ругается PHPCS, вы можете взять из лога вывода ошибок линтера.

Как использовать PHPCS с Github Actions

Использование PHPCS совместно в Github Actions CI позволит выполнять автоматизированную проверку кодстайла перед мержем MR/PR в main ветку. В случае если найдена ошибка, Github Action Job завершится с неудачей и сообщит об этом при кодревью. Внутри самой джобы вы сможете ознакомиться с логами ошибок.

Приведу ниже код рабочего экшена:

name: Workflow with PHPCS linter
env:
  PHP_VER: "8.2"
  COMPOSER_VER: "2.5.1"
on:
  push:
    branches: [ "main", "dev" ]
  pull_request:
    types: [synchronize, opened, reopened]
jobs:
  php-ci:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Cache Composer dependencies
        uses: actions/cache@v3
        with:
          path: /tmp/composer-cache
          key: ${{ runner.os }}-${{ hashFiles('**/composer.lock') }}
      - uses: php-actions/composer@v6
        with:
          php_version: ${{ env.PHP_VER }}
          version: ${{ env.COMPOSER_VER }}
      - name: Get PHPCS Cache
        id: phpcs-cache
        run: |
          echo "file=.phpcs.cache" >> $GITHUB_OUTPUT
      - uses: actions/cache@v3
        with:
          path: ${{ steps.phpcs-cache.outputs.file }}
          key: ${{ runner.os }}-phpcs-${{ hashFiles('**/.phpcs.cache') }}
          restore-keys: |
            ${{ runner.os }}-phpcs-
      - name: Run PHPCS
        run: composer run phpcs

Что же здесь происходит?

  • Как и для локального env, вначале нам нужно получить код из git, для этого Github action использует готовый экшен actions/checkout@v3 .
  • Потом нам необходимо установить composer зависимости, для этого в Github actions мы используем php-actions/composer@v6 .
  • Когда пакеты установлены, мы приступаем к джобе Get PHPCS Cache в которой вытаскиваем кеш PHPCS.
  • После того как кеш файл получен, мы можем выполнять Run PHPCS .
WPCS в WordPress с Github Actions

На картинке вы можете видеть резльтат выполнения Github Action jobs. В раскрытой джобе представлен output PHPCS.
С реальным примером работы PHPCS и Github Action вы можете ознакомиться по ссылке https://github.com/renakdup/using-phpcs-for-wordpress/actions/runs/4715246123

Полный код

С полным кодом статьи вы можете ознакомиться в Github репозитории.

Итог

В этой статей мы разобрали что такое PHPCS, какие есть отличия между PHPCS и другими статическими анализаторами. Рассмотрели WPCS стандарт и как можно настроить свой собственный кодстайл. Написали простой GitHub Action CI для запуска PHPCS.

Этой не совсем базовой информации, вам дожно хватить, чтобы вы могли использовать PHPCS с WPCS на WordPress проекте и повысить качество кода при разработке в команде.
Не стейсняйтесь экспереминтировать и предлагать что-то новое на своем проекте.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *