В опенсорс-проектах часто можно увидеть использование инструментов для проверки кода: проверяется кодстайл, выполняется статический анализ. Эти инструменты широко распространены, но в проектах на Битриксе они встречаются редко. В этой статье я покажу, как начать использовать такие инструменты в своих проектах на Битриксе.
Предупреждение
Прежде чем внедрять инструменты из статьи, стоит подумать, какую проблему вы хотите решить.
Допустим, вы заметили, что слишком часто возвращаете мерж-реквесты на доработку с просьбой добавить пробел между (int) и переменной, чтобы соответствовать PER Coding Style. Если вы в своем проекте придерживаетесь какого-то кодстайла, то проверку соответствия этому кодстайлу вполне можно поручить компьютеру, а не делать ее вручную.
Либо бывают ситуации, когда в одной ветке разработчик переименовал метод, а в другой ветке в то же время другой разработчик добавил новый вызов этого метода. В таком случае после слияния на площадке у нас возникнет ошибка обращения к несуществующему методу. Чтобы не ловить такие ошибки в рантайме, проверку существования вызываемого метода также можно доверить компьютеру!
Когда вы поняли проблему, нужно определиться с необходимостью ее решения. Если у вас на проекте 1 бэкендер и 1 фронтендер, то добавление проверок может и не пригодиться. Каждый разработчик сам будет следить за своим кодстайлом и не будет переименовывать метод одновременно с добавлением нового его вызова. Не стоит бросаться добавлять новые штуки просто потому, что кто-то другой так сделал — сначала определите, какую проблему вы решаете.
С чего начать
Допустим, вы определились с проблемой и какой-то из инструментов может ее решить. С чего начать? Рекомендую начать с минимального CI. Чтобы добавленными инструментами пользовались, вам нужно сделать использование этих инструментов удобным. Если проверки запускаются в CI, то вся команда видит статус каждого коммита. И если разработчик у себя проверки не запустил, они все равно запустятся в CI. Таким образом, вам становится неважно, в какой среде разработки или операционной системе запускаются ваши проверки — источник истины в любом случае будет в CI.
Показывать буду на примере гитлаба, потому что по моим наблюдениям в студиях обычно разворачивают именно его. Создаем файл .gitlab-ci.yml. Для начала он будет содержать всего одну стадию: check.
check: image: quay.io/bitrix24/php:8.3.22-fpm-v1-alpine script: - composer validate --strict - composer install
Для запуска проверок используем официальный образ php от Битрикса. Для начала в теле скрипта укажем всего две команды — composer validate и composer install. После пуша видим в нашем репозитории зеленую лампочку. Теперь мы в любой момент времени можем быть уверены, что композер сконфигурирован корректно и успешно устанавливает все пакеты.
Добавляем PHP CS Fixer
Далее добавим PHP CS Fixer:
composer require --dev friendsofphp/php-cs-fixer
Также добавим правила кодстайла PHPyh:
composer require --dev phpyh/coding-standard
Создадим файл .php-cs-fixer.dist.php со следующим содержимым:
<?php use PhpCsFixer\Config; use PhpCsFixer\Finder; use PHPyh\CodingStandard\PhpCsFixerCodingStandard; $config = (new Config()) ->setFinder( (new Finder()) ->in([ __DIR__ . '/src/', __DIR__ . '/public/local/', __FILE__, ]) ); (new PhpCsFixerCodingStandard())->applyTo($config); return $config;
Для удобства добавим элемент scripts в composer.json:
"scripts": { "cs-check": "php-cs-fixer check", "cs-fix": "php-cs-fixer fix" }
В целом можно этого не делать и запускать всегда команду php-cs-fixer check, но так удобнее будет смотреть какие команды используются на проекте.
Также добавим запуск cs-check в CI:
check: image: quay.io/bitrix24/php:8.3.22-fpm-v1-alpine script: - composer validate --strict - composer install - composer cs-check
Готово! Теперь у нас есть проверки кодстайла и вам больше не нужно проверять вручную глазами форматирование кода, за этим будет следить компьютер.
Добавляем Rector
Далее добавим Rector:
composer require --dev rector/rector
В конфигурацию пропишем те же пути, что и для PHP CS Fixer. Rector нужно обязательно использовать вместе с каким-нибудь кодстайл-фиксером, потому что форматированием кода Rector не занимается.
<?php declare(strict_types=1); use Rector\Config\RectorConfig; return RectorConfig::configure() ->withCache(__DIR__ . '/cache/rector') ->withPaths([ __DIR__ . '/src', __DIR__ . '/public/local', ]) ->withParallel() ->withPhpSets();
Явно укажем путь кэша в rector.php.
И в .php-cs-fixer.dist.php тоже:
<?php declare(strict_types=1); use PhpCsFixer\Config; use PhpCsFixer\Finder; use PHPyh\CodingStandard\PhpCsFixerCodingStandard; $config = (new Config()) ->setCacheFile(__DIR__ . '/cache/.php-cs-fixer.cache') ->setFinder( (new Finder()) ->in([ __DIR__ . '/src/', __DIR__ . '/public/local/', ]) ->append([ __FILE__, __DIR__ . '/rector.php', ]), ); (new PhpCsFixerCodingStandard())->applyTo($config); return $config;
В файле .gitlab-ci.yml добавим кэширование, чтобы проверки выполнялись быстрее:
check: image: quay.io/bitrix24/php:8.3.22-fpm-v1-alpine script: - composer validate --strict - composer install - composer cs-check - composer rector-check cache: paths: - vendor - cache
Также добавим команду для запуска Rector в composer.json:
"scripts": { "cs-check": "php-cs-fixer check", "cs-fix": "php-cs-fixer fix", "rector-check": "rector process --dry-run", "rector-fix": "rector process" }, "scripts-descriptions": { "cs-check": "Check coding standards", "cs-fix": "Apply coding standards", "rector-check": "Check rector rules", "rector-fix": "Apply rector rules" }
Добавляем PHPStan
Далее добавим PHPStan:
composer require --dev phpstan/phpstan
Чтобы PHPStan не выдавал ошибки class.notFound на все классы Битрикса, в конфигурацию добавляем пути до используемых модулей Битрикса в scanDirectories:
parameters: level: 10 tmpDir: cache/phpstan paths: - src - public/local scanFiles: - stubs/orm.stub - public/bitrix/modules/orm_annotations.php - stubs/bitrix.stub scanDirectories: - public/bitrix/modules/main - public/bitrix/modules/iblock - public/bitrix/modules/highloadblock
level подбирайте под проект, скорее всего максимальный уровень в начале вам не подойдет. На легаси-проект у меня удалось затащить только первый уровень (есть еще нулевой), на проект поновее и поменьше подошел четвертый. Даже на низком уровне вы все равно получите бонусы статического анализа, как минимум не поймаете на проде ошибку Call to undefined method... В примере я поставлю максимальный 10 уровень, чтобы поймать все возможные предупреждения анализатора.
Также укажем в tmpDir путь до директории с кэшем, чтобы результат анализа кэшировался между запусками CI.
Далее — какие у вас могут возникнуть проблемы?
Например, вы где-то в коде используете функцию Битрикса LocalRedirect чтобы средиректить пользователя. Возвращаемый тип у нее void, хотя на самом деле там never! Из-за этого PHPStan будет неправильно анализировать ваш код, думая что после LocalRedirect исполнение продолжается, хотя в рантайме это, конечно, будет не так. Чтобы исправить эту проблему, создадим файл bitrix.stub со следующим содержимым:
<?php /** * @param string $url * @param bool $skip_security_check * @param string $status * @return never */ function LocalRedirect($url, $skip_security_check = false, $status = "302 Found") {}
И укажем путь до файла в scanFiles. PHPStan будет читать этот стаб и понимать, что после вызова LocalRedirect исполнение завершается.
Если вы в проекте используете ORM Битрикса, то вы скорее всего уже генерируете подсказки для IDE (orm_annotations.php). Этот файл с подсказками также нужно указать в scanFiles, PHPStan будет его понимать.
При этом, в сгенерированном файле также могут быть ошибки. Например, по какой-то причине PHPStan считает, что string и \string, это два разных типа, и пишет в ошибке Parameter of class constructor expects string, string given. Это явно ошибка, не понятно только ошибка PHPStan или проблема Битрикса, что он для некоторых типов добавляет в аннотации символ \. Но в любом случае проблему надо решать, и поэтому также добавим файл orm.stub:
<?php namespace Bitrix\Iblock\Elements { /** * @method string getName() * @method string getPreviewText() * @method string getDetailText() */ class EO_ElementNews {} }
Укажем в нем используемые нами методы с типом без \. Также добавим путь к файлу в scanFiles. Важно — orm.stub нужно добавить до orm_annotations.php. Теперь PHPStan понимает, что же вернет Битрикс в getPreviewText, например вот в таком вызове ORM:
/** @var EO_ElementNews_Collection $collection */ $collection = ElementNewsTable::query() ->addSelect('NAME') ->addSelect('PREVIEW_TEXT') ->addSelect('DETAIL_TEXT') ->fetchCollection(); $news = []; foreach ($collection as $element) { $news[] = new NewsDTO( $element->getName(), $element->getPreviewText(), $element->getDetailText(), ); }
и не будет ругаться так:
Parameter #1 $name of class Example\News\NewsDTO constructor expects string, string given.
Далее самое интересное — как все это запустить в CI? Ведь в CI нет файлов ядра Битрикса и сгенерированного orm_annotations.php. Добавлять ядро в систему контроля версий?
Можно конечно и так поступить. Видел проект, где ядро было закоммичено в отдельный репозиторий, там вероятно можно было бы его и подтягивать. Но в целом иметь ядро под контролем версий обычно не нужно.
Мне больше нравится добавлять ядро в образ для запуска проверок в CI.
Через админку создаем бэкап ядра без базы и без файлов публичной части.
Далее создаем Dockerfile с таким содержимым:
FROM quay.io/bitrix24/php:8.3.22-fpm-v1-alpine RUN curl -SL https://ilimurzin.ru/bitrix/backup/20250625_215733_core_c3qfdf0u7s7z98q5.tar.gz | tar -xzC /tmp
quay.io/bitrix24/php:8.3.22-fpm-v1-alpine это тот же образ PHP от Битрикса, который мы использовали для запуска проверок.
Курлом качаем и распаковываем созданный бэкап.
В полученном образе ядро Битрикса будет лежать по пути /tmp/bitrix.
Сборку и деплой образа в гитлабовский Container registry можно автоматизировать, пример вот тут: https://gitlab.com/ilimurzin/php-image-example
check: image: registry.gitlab.com/ilimurzin/php-image-example:1 script: - composer validate --strict - composer install - composer cs-check - composer rector-check - ln -s /tmp/bitrix public/bitrix - composer phpstan cache: paths: - vendor - cache
Полученный образ указываем в image. Перед запуском PHPStan линкуем ядро Битрикса в public/bitrix. Готово! Лампочка зеленая — PHPStan в CI видит файлы Битрикса.
Полный пример можно посмотреть тут
Вывод
Технически вполне возможно использовать все современные инструменты вместе с Битриксом, но важно подумать, прежде чем что-то применять. У вас должна быть причина для изменений — иначе это просто бесполезно проделанная работа.
Инструменты могут отличаться от проекта к проекту: вы можете использовать их в разных сочетаниях. При этом на одном проекте инструмент может принести заметную пользу, а на другом — никогда не окупить время, потраченное на его внедрение и поддержку.
Об этом не стоит забывать, и всегда думать прежде чем что-то делать :-)
