Kodokon kodokon.com

Качество: PHPUnit, PHPStan и стандарты PSR

Оснасти свой код по-профессиональному: тесты PHPUnit, статический анализ PHPStan на уровне 9 и стандарты PSR.

10 мин · 3 вопросов

Открыть этот урок в Kodokon

Экспертный код узнаётся не столько по хитрым приёмам, сколько по страховочной сетке. Три взаимодополняющие опоры: тесты (PHPUnit), которые проверяют поведение, запуская код, статический анализ (PHPStan), который доказывает свойства кода, ничего не запуская, и общие стандарты (PSR), которые делают экосистему совместимой. Всё ставится через Composer как зависимости для разработки.

BASH
composer require --dev phpunit/phpunit
composer require --dev phpstan/phpstan
./vendor/bin/phpunit --testdox tests
./vendor/bin/phpstan analyse src --level=9
Опция --testdox показывает тесты как читаемую спецификацию.

Начиная с PHPUnit 10 метаданные пишутся как атрибуты PHP: #[DataProvider] заменяет аннотацию @dataProvider в докблоке. Поставщик данных должен быть публичным и статическим, потому что PHPUnit читает его ещё до создания экземпляра тестового класса. Каждый набор данных несёт описательный ключ, который выводится как есть при падении случая - сбой accents находится мгновенно.

PHP
<?php
declare(strict_types=1);

use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;

final class SlugifierTest extends TestCase
{
    #[DataProvider('provideTitles')]
    public function testSlugify(
        string $input,
        string $expected
    ): void {
        $this->assertSame($expected, slugify($input));
    }

    public static function provideTitles(): array
    {
        return [
            'simple' => [
                'Hello World', 'hello-world'
            ],
            'accents' => [
                'Déjà vu', 'deja-vu'
            ],
        ];
    }
}
Тестирует функцию slugify, определённую где-то ещё в твоём проекте.

PHPStan градуирует свою строгость по уровням, от 0 до 9 (в свежих версиях есть и уровень 10): на уровне 9 любое значение mixed становится непригодным к использованию, пока его не сузили явными проверками. Настоящая сила инструмента - в типах из докблоков, которые движок PHP игнорирует, а анализатор проверяет: формы массивов array{email: string}, списки list<int>, обобщения. Они одновременно документируют и доказывают.

PHP
<?php
declare(strict_types=1);

function findEmail(?array $user): string
{
    return $user['email'];
}

/**
 * @param array{email: string}|null $user
 */
function findEmailSafe(?array $user): string
{
    return $user === null
        ? 'n/a'
        : $user['email'];
}
Уровень 9: первая функция отклоняется (возможное обращение к null, неизвестные ключ и тип), вторая проходит.

PSR (PHP Standards Recommendations) - это соглашения группы PHP-FIG. PSR-1 и PSR-12 стандартизируют стиль: отступы, фигурные скобки, порядок импортов - всё это автоматически применяют php-cs-fixer или phpcs. PSR-4 определяет автозагрузку: префикс пространства имён сопоставляется с каталогом, что позволяет Composer найти любой класс без ручного require. Наконец, PSR с интерфейсами - PSR-3 для логгеров, PSR-7 для HTTP-сообщений - позволяют заменить одну библиотеку другой, не трогая код, который её использует.

Проверка знаний

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

  1. Какое ограничение действует начиная с PHPUnit 10 на метод-поставщик данных, на который ссылается #[DataProvider]?
    • Он должен быть публичным и статическим, потому что PHPUnit читает его до создания экземпляра тестового класса
    • Он должен возвращать генератор, но никогда не массив
    • Он должен называться так же, как тест, с суффиксом Provider
  2. Что навязывает уровень 9 в PHPStan?
    • Запрет на докблоки в пользу только нативных типов
    • Строгую работу с mixed: такое значение нельзя использовать, не сузив его предварительно
    • 100% покрытие тестами анализируемого кода
  3. Что определяет стандарт PSR-4?
    • Формат сообщений в логах приложения
    • Соответствие между префиксом пространства имён и каталогом, используемое автозагрузчиком Composer
    • Стиль отступов и расстановку фигурных скобок