Kodokon kodokon.com

Qualité : PHPUnit, PHPStan et les PSR

Outillez votre code comme un professionnel avec les tests PHPUnit, l'analyse statique PHPStan au niveau 9 et les standards PSR.

10 min · 3 questions

Ouvrir cette leçon dans Kodokon

Un code de niveau expert se reconnaît moins à ses astuces qu'à son filet de sécurité. Trois piliers complémentaires : des tests (PHPUnit) qui vérifient le comportement en exécutant le code, une analyse statique (PHPStan) qui prouve des propriétés sans rien exécuter, et des standards partagés (PSR) qui rendent l'écosystème interopérable. Tout s'installe avec Composer, en dépendances de développement.

BASH
composer require --dev phpunit/phpunit
composer require --dev phpstan/phpstan
./vendor/bin/phpunit --testdox tests
./vendor/bin/phpstan analyse src --level=9
L'option --testdox affiche les tests comme une spécification lisible.

Depuis PHPUnit 10, les métadonnées s'écrivent en attributs PHP : #[DataProvider] remplace l'annotation @dataProvider des docblocks. Un fournisseur de données doit être public et statique, car PHPUnit le lit avant même d'instancier la classe de test. Chaque jeu de données porte une clé descriptive, affichée telle quelle quand le cas échoue - un échec accents se localise instantanément.

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'
            ],
        ];
    }
}
Teste une fonction slugify définie ailleurs dans votre projet.

PHPStan gradue son exigence par niveaux, de 0 à 9 (un niveau 10 existe dans les versions récentes) : au niveau 9, toute valeur mixed devient inutilisable sans affinage préalable par des vérifications explicites. Sa vraie puissance vient des types en docblocks que le moteur PHP ignore mais que l'analyseur vérifie : formes de tableaux array{email: string}, listes list<int>, génériques. Ils documentent et prouvent à la fois.

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'];
}
Niveau 9 : la première fonction est rejetée (accès possible sur null, clé et type inconnus), la seconde passe.

Les PSR (PHP Standards Recommendations) sont les conventions du PHP-FIG. PSR-1 et PSR-12 normalisent le style : indentation, accolades, ordre des imports - appliqués automatiquement par php-cs-fixer ou phpcs. PSR-4 définit l'autoloading : un préfixe de namespace correspond à un répertoire, ce qui permet à Composer de localiser toute classe sans require manuel. Enfin, les PSR d'interfaces - PSR-3 pour les loggers, PSR-7 pour les messages HTTP - permettent de remplacer une bibliothèque par une autre sans toucher au code qui la consomme.

Quiz de validation

Vérifiez que vous avez bien retenu les points clés de cette leçon.

  1. Depuis PHPUnit 10, quelle contrainte pèse sur une méthode fournisseuse de données référencée par #[DataProvider] ?
    • Elle doit être publique et statique, car PHPUnit la lit avant d'instancier la classe de test
    • Elle doit retourner un générateur, jamais un tableau
    • Elle doit porter le même nom que le test, suffixé par Provider
  2. Qu'impose le niveau 9 de PHPStan ?
    • L'interdiction des docblocks au profit des seuls types natifs
    • Un traitement strict de mixed : impossible d'utiliser une telle valeur sans l'affiner d'abord
    • Une couverture de tests de 100 % sur le code analysé
  3. Que définit le standard PSR-4 ?
    • Le format des messages de log applicatifs
    • La correspondance entre un préfixe de namespace et un répertoire, utilisée par l'autoloader de Composer
    • Le style d'indentation et le placement des accolades