Что проверяет CI в PHP-проекте
CI, continuous integration, — это автоматический прогон проверок на чистой копии проекта: после push, перед merge, иногда перед deploy. В PHP он обычно отвечает на простой вопрос: «Если взять этот commit, поставить зависимости из composer.lock и выполнить стандартные проверки, проект всё ещё собирается?»
CI не заменяет code review. Он убирает из review механические вещи: битый composer.json, расхождение lock-файла, синтаксические ошибки, нарушения style guide, падение тестов, известные уязвимости в зависимостях. Поэтому эта статья находится рядом с composer.lock, install/update и audit, PHPUnit, моки и стабы, PHPDoc, generics и статический анализ и PSR-1, PSR-12 и стиль кода: все эти инструменты сходятся в одном pipeline.
flowchart TD
A[Push или pull request] --> B[Checkout чистой копии]
B --> C[composer install из composer.lock]
C --> D[composer validate]
D --> E[composer audit]
E --> F[Style check: PHP-CS-Fixer или PHPCS]
F --> G[Static analysis: PHPStan или Psalm]
G --> H[PHPUnit tests]
H --> I{Все checks зелёные?}
I -->|да| J[Merge разрешён]
I -->|нет| K[Merge блокируется до исправления]Типичный порядок проверок
Обычный quality pipeline начинается с установки зависимостей, а не с тестов. Если зависимости не ставятся воспроизводимо, остальные проверки уже не имеют смысла.
composer install --no-interaction --prefer-dist
composer validate --strict
composer audit
vendor/bin/php-cs-fixer check --diff
vendor/bin/phpstan analyse
vendor/bin/phpunitВ реальном проекте команды часто прячут в Composer scripts, чтобы локальный запуск и CI использовали один контракт:
{
"scripts": {
"validate": "composer validate --strict",
"cs": "php-cs-fixer check --diff",
"stan": "phpstan analyse",
"test": "phpunit",
"audit": "composer audit",
"check": [
"@validate",
"@audit",
"@cs",
"@stan",
"@test"
]
}
}После этого разработчик запускает composer check локально, а CI делает то же самое на сервере. Это снижает расхождение «у меня зелёное, а в CI красное». Если проект использует Psalm вместо PHPStan, команда меняется, но идея остаётся той же: один script для полного набора проверок.
Install, update и production-режим
В CI почти всегда нужен composer install, а не composer update. install берёт точные версии из lock-файла, если он есть. update пересчитывает зависимости и меняет composer.lock; это отдельная операция сопровождения, а не обычный шаг перед merge. Подробнее это разобрано в composer.lock, install/update и audit.
Есть важный нюанс с --no-dev. На quality-стадии CI обычно нужны dev-зависимости: PHPUnit, PHPStan, Psalm, PHP-CS-Fixer, PHP_CodeSniffer. Поэтому composer install --no-dev там сломает проверки или сделает их неполными. --no-dev уместен на production build/deploy стадии, где нужны только runtime-пакеты. Там же часто добавляют оптимизацию autoload:
composer install --no-dev --prefer-dist --no-interaction --optimize-autoloaderЕсли проект зависит от расширений PHP, полезна отдельная проверка platform requirements. Она ловит ситуацию, когда lock-файл собран под один набор ext-*, а CI или production запускается с другим.
Линтер, форматер и coding standard
В PHP словом «линтер» часто называют разные инструменты, но они делают не одно и то же.
php -l проверяет синтаксис одного файла. Он не понимает архитектуру проекта, не строит типовую модель и не проверяет стиль. Это дешёвая базовая проверка, но не полноценный quality gate.
PHP-CS-Fixer — форматер и fixer: он умеет привести код к набору правил, например близкому к PSR-12, и в режиме check показать, какие файлы надо исправить, не меняя их в CI. Локально обычно запускают fix, в CI — check.
vendor/bin/php-cs-fixer fix
vendor/bin/php-cs-fixer check --diffPHP_CodeSniffer (phpcs) работает через standards и sniffs: он проверяет код на соответствие выбранному стандарту. Часто рядом используют phpcbf, который автоматически исправляет часть нарушений.
vendor/bin/phpcs src tests --standard=PSR12
vendor/bin/phpcbf src tests --standard=PSR12Выбор между PHP-CS-Fixer и PHP_CodeSniffer зависит от команды и проекта. Не обязательно ставить оба. Важно, чтобы правило было одно: style не обсуждается вручную в каждом PR/MR, а воспроизводимо проверяется машиной. Это практическое продолжение статьи PSR-1, PSR-12 и стиль кода.
Статический анализ: PHPStan или Psalm
Статический анализ проверяет код без запуска приложения. Он видит несовместимые типы, недостижимые ветки, подозрительные вызовы, неправильные PHPDoc-аннотации, проблемы с generics и array shapes. Это не то же самое, что PHPUnit: тест исполняет выбранные сценарии, а анализатор пытается доказать свойства кода по сигнатурам и потоку значений.
Типичный запуск PHPStan:
vendor/bin/phpstan analyse src testsТипичный запуск Psalm:
vendor/bin/psalmНа практике уровень строгости повышают постепенно. Легаси-проект редко можно сразу включить на максимум без сотен ошибок. Для этого используют baseline: текущие проблемы фиксируются как известный долг, а новые ошибки начинают блокировать merge. Но baseline не должен становиться мусорной корзиной. Если он только растёт, CI формально зелёный, а качество фактически падает.
Статический анализ особенно полезен рядом с Типы и strict_types, PHPDoc, generics и статический анализ и Неймспейсы и use: чем точнее типы и автозагрузка, тем меньше анализатору приходится угадывать.
PHPUnit в pipeline
PHPUnit обычно запускают после быстрых проверок: если composer validate или style check уже упали, нет смысла тратить время на весь test suite. Но порядок не должен скрывать ошибки: в больших проектах проверки часто разбивают на параллельные jobs — style, static analysis, tests, security.
Минимальный GitHub Actions workflow может выглядеть так:
name: php-checks
on:
pull_request:
push:
branches: [main]
jobs:
checks:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.4'
coverage: none
- name: Install dependencies
run: composer install --no-interaction --prefer-dist
- name: Run project checks
run: composer checkЭто не универсальный шаблон. В Laravel-проекте могут понадобиться .env.testing, SQLite, Redis или миграции. В библиотеке, наоборот, может быть matrix по нескольким версиям PHP, чтобы заранее поймать проблемы из статьи Миграции между версиями PHP.
Security audit и границы автоматизации
composer audit проверяет установленные пакеты по advisories и политикам зависимостей. Его стоит запускать в CI, но он не заменяет security review. Он не найдёт XSS в шаблоне, небезопасный redirect, ручную склейку SQL или неправильную настройку cookies. Для этого нужны проверки уровня кода и знания из статей Prepared statements и SQL injection, XSS, экранирование вывода и шаблоны, CSRF и state-changing запросы и Конфигурация безопасности PHP.
Хорошее правило: CI должен блокировать merge на проблемах, которые команда считает недопустимыми. Для security audit это обычно known vulnerabilities в production-зависимостях. Для abandoned packages политика может быть мягче или жёстче в зависимости от риска, но она должна быть явной, а не случайной.
Что должно блокировать merge
Минимальный набор для современного PHP-проекта:
composer validate --strict— структураcomposer.jsonи согласованность с lock-файлом.composer installиз lock-файла — воспроизводимая установка.- Style check — PHP-CS-Fixer или PHP_CodeSniffer.
- Static analysis — PHPStan или Psalm на согласованном уровне строгости.
- PHPUnit — unit/integration tests, которые фиксируют поведение.
composer audit— известные проблемы зависимостей.
Для production-сервисов добавляют проверки миграций, сборку assets, smoke tests, Docker image scan, деплой в staging. Но ядро остаётся тем же: каждый commit должен проходить один и тот же набор автоматических gate’ов. Тогда review можно тратить на смысл изменений: доменную модель, API, безопасность, читаемость, а не на пробелы, импорты и забытый lock-файл.
См. также
- composer.lock, install/update и audit — почему CI должен ставить зависимости из lock-файла.
- Composer, Packagist и composer.json — где задаются scripts и dev-зависимости.
- PHPUnit, моки и стабы — как тесты становятся частью автоматического pipeline.
- PHPDoc, generics и статический анализ — как PHPStan/Psalm используют типы и аннотации.
- PSR-1, PSR-12 и стиль кода — откуда берутся правила style checks.
- Миграции между версиями PHP — зачем гонять проверки на нескольких версиях PHP перед обновлением.