Développement

Qualité Code & Tests Automatisés : Retour d'expérience sur mon Portfolio Symfony

04/03/2026 12 min
PHP Symfony PHPStan Qualité Code CI/CD GitHub Actions DevOps
Retour au blog
Qualité Code & Tests Automatisés : Retour d'expérience sur mon Portfolio Symfony

Développer un portfolio professionnel, c'est bien plus qu'une vitrine : c'est une opportunité de démontrer sa maîtrise des bonnes pratiques de développement. Aujourd'hui, je partage mon retour d'expérience sur l'intégration d'outils de qualité code dans mon portfolio Symfony 7.4.

Contexte : Un portfolio Symfony avec des exigences professionnelles

Mon portfolio est construit avec Symfony 7.4, PHP 8.2, et Doctrine ORM. L'objectif initial était de présenter mes compétences en développement Full Stack et en tests logiciels. Mais pour qu'il soit crédible auprès de recruteurs techniques, il devait respecter les standards professionnels : 0 erreur d'analyse statique, code style uniforme, et CI/CD automatisé.

État initial : 138 erreurs PHPStan détectées

En lançant PHPStan niveau 8 (le niveau maximum de strictness) sur mon code, j'ai découvert 138 erreurs réparties en plusieurs catégories :

  • 60% Array typing : PHPStan exige @var array<string> au lieu de array générique
  • 15% Nullable handling : Les fonctions natives PHP comme trim(), iconv(), preg_replace() n'acceptent pas string|null
  • 15% Type narrowing : Les paramètres Request::get() retournent mixed, nécessitant des vérifications is_string()
  • 10% Unused code : Méthodes privées jamais appelées, propriétés non utilisées

PHPStan, développé par Ondřej Mirtes et maintenu activement, est l'outil de référence pour l'analyse statique en PHP. Il simule l'exécution du code sans l'exécuter réellement, détectant les bugs potentiels à la compilation.

Stratégie de correction : Priorisation par impact

Plutôt que de corriger aléatoirement, j'ai appliqué une approche méthodique :

1. Entités Doctrine : Array typing et propriétés nullable

Les entités (Article, Project, Experience) contenaient des tableaux JSON non typés. J'ai ajouté des annotations PHPDoc strictes :

/**
 * @var array<string>
 */
#[ORM\Column(type: Types::JSON)]
private array $technologies = [];

Résultat : 24 erreurs corrigées.

2. Contrôleurs : Helper methods pour type-safety

Le contrôleur AdminController récupérait des paramètres via $request->request->get(), qui retourne mixed. J'ai créé deux helper methods pour garantir le type :

private function getStringParam(Request $request, string $key, string $default = ''): string
{
    $value = $request->request->get($key, $default);
    return is_string($value) ? $value : $default;
}

private function getNullableStringParam(Request $request, string $key): ?string
{
    $value = $request->request->get($key);
    if ($value === null || $value === '') {
        return null;
    }
    return is_string($value) ? $value : (string) $value;
}

Résultat : 50+ erreurs corrigées dans AdminController et ContactController.

3. Services : Null-safety avec pattern intermédiaire

Les services FileUploadService et MediaUploadService utilisaient iconv() et preg_replace(), qui peuvent retourner false ou null. J'ai systématisé ce pattern :

// Avant (erreur PHPStan)
$text = preg_replace('~[^\\pL\\d]+~u', '-', $text);
$text = iconv('utf-8', 'us-ascii//TRANSLIT', $text);

// Après (type-safe)
$text = preg_replace('~[^\\pL\\d]+~u', '-', $text) ?? $text;
$iconvResult = iconv('utf-8', 'us-ascii//TRANSLIT', $text);
$text = $iconvResult !== false ? $iconvResult : $text;

Résultat : 10 erreurs corrigées.

4. Configuration PHPStan : Ignorer les faux positifs Doctrine

PHPStan signalait que les propriétés $id des entités Doctrine n'étaient jamais assignées, car il ne comprend pas #[ORM\GeneratedValue]. J'ai ajouté une règle globale dans phpstan.dist.neon :

parameters:
    ignoreErrors:
        -
            message: '#Property App\\Entity\\.*::\$id \(int\|null\) is never assigned int#'
            identifier: property.unusedType

Résultat : 6 erreurs ignorées légitimement.

PHP CS Fixer : Uniformisation du style de code

En parallèle de PHPStan, j'ai intégré PHP CS Fixer pour garantir un code style conforme aux standards Symfony et PSR-12. Après configuration dans .php-cs-fixer.dist.php :

return (new PhpCsFixer\Config())
    ->setRiskyAllowed(true)
    ->setRules([
        '@Symfony' => true,
        '@PSR12' => true,
        'array_syntax' => ['syntax' => 'short'],
        'ordered_imports' => ['sort_algorithm' => 'alpha'],
        'no_unused_imports' => true,
        'global_namespace_import' => ['import_classes' => true],
    ]);

Un simple composer cs-fix a corrigé automatiquement 32 fichiers : indentation, espaces, ordre des imports, lignes vides en fin de fichier.

GitHub Actions : CI/CD pour maintenir la qualité

Pour éviter les régressions, j'ai créé un workflow GitHub Actions (.github/workflows/quality.yml) qui exécute automatiquement sur chaque push :

  • PHPStan : Analyse statique niveau 8
  • PHP CS Fixer : Vérification du style (dry-run)
  • Composer Validate : Validation de composer.json et composer.lock
  • Symfony Security Checker : Détection des vulnérabilités dans les dépendances

Configuration du job PHPStan :

phpstan:
  name: PHPStan Static Analysis
  runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v4
    - uses: shivammathur/setup-php@v2
      with:
        php-version: '8.2'
        extensions: ctype, iconv, pdo, sqlite3
    - run: composer install --prefer-dist
    - run: php bin/console cache:warmup --env=dev
    - run: composer phpstan

Chaque commit déclenche maintenant ces vérifications automatiquement, avec un statut visible directement dans GitHub.

Baseline PHPStan : Gérer les erreurs externes

Certaines erreurs provenaient de bibliothèques externes (ex : lcobucci/jwt exigeant non-empty-string au lieu de string). Plutôt que de polluer mon code, j'ai créé un baseline :

composer phpstan:baseline

Le fichier phpstan-baseline.neon documente ces 7 erreurs externes, permettant à PHPStan de se concentrer uniquement sur mon code.

Résultats finaux : 138 → 0 erreurs (-100%)

Après 13 commits méthodiques :

  • 0 erreur PHPStan (niveau 8 maximum)
  • 0 erreur PHP CS Fixer (100% conforme Symfony/PSR12)
  • CI/CD automatisé sur GitHub Actions
  • Documentation complète (3 fichiers Markdown détaillés)

Techniques clés à retenir

Si vous souhaitez reproduire cette démarche sur vos projets :

  • Helper methods : Encapsulez les opérations répétitives de type casting pour éviter la duplication
  • Type narrowing : Utilisez is_string(), is_array(), is_int() systématiquement avant les opérations natives
  • Opérateur ?? judicieux : Pour les fonctions retournant false ou null, utilisez l'opérateur de coalescence : $result = func() ?? $default
  • Configuration PHPStan progressive : Commencez au niveau 5, puis montez progressivement vers 8
  • Baseline pour l'existant : Sur un projet legacy, créez un baseline initial puis corrigez incrémentalement

Impact professionnel

Cette démarche qualité a plusieurs avantages concrets :

  • Crédibilité technique : Montrer aux recruteurs que je suis capable de produire du code maintenable et industriel
  • Prévention des bugs : PHPStan détecte des erreurs que les tests unitaires ne captureraient pas toujours
  • Onboarding facilité : Un nouveau développeur peut contribuer sereinement grâce au CI/CD qui valide automatiquement
  • Compétences DevOps : Maîtrise de GitHub Actions, Docker (pour les tests en isolation), et outils de qualité modernes

Développement du portfolio : Architecture technique

Au-delà de la qualité code, voici les choix techniques de mon portfolio :

Stack technique

  • Backend : Symfony 7.4 (dernière version LTS prévue pour mi-2026)
  • Base de données : SQLite en développement, PostgreSQL recommandé pour la production
  • Templates : Twig avec composants réutilisables
  • Authentification : JWT (LexikJWTAuthenticationBundle) pour l'admin
  • Déploiement : GitHub Actions vers OVH mutualisé via FTP (script automatisé)

Fonctionnalités clés

  • Back-office complet : Gestion des articles, projets, expériences professionnelles
  • Upload d'images : Service dédié avec validation MIME type et génération de slug SEO-friendly
  • Système de contact : Envoi d'emails via Symfony Mailer avec validation anti-spam simplifiée
  • SEO optimisé : Génération automatique du sitemap.xml et robots.txt
  • Responsive design : CSS moderne avec Flexbox et Grid, sans framework CSS lourd

Choix architecturaux

J'ai privilégié la simplicité tout en respectant les principes SOLID :

  • Services typés : FileUploadService, MediaUploadService, JwtService avec injection de dépendances
  • Repositories Doctrine : Requêtes optimisées avec Query Builder et méthodes métier (findPublishedArticles(), countLast24Hours())
  • DataFixtures : Jeu de données réaliste pour développement et démonstration
  • Environnements séparés : Configuration .env distincte pour dev/prod avec secrets GitHub Actions

Conclusion : La qualité code comme différenciant

Dans un marché du recrutement tech saturé, la qualité du code est un différenciant majeur. Intégrer PHPStan, PHP CS Fixer et GitHub Actions sur un projet personnel démontre :

  • Une rigueur professionnelle au-delà du simple code fonctionnel
  • Une autonomie DevOps (CI/CD, automatisation, monitoring qualité)
  • Une veille technologique active (outils modernes, bonnes pratiques 2024-2026)

Si vous développez un portfolio ou un projet personnel, je vous encourage vivement à adopter ces pratiques. Le temps investi initial (environ 2 jours pour l'intégration complète) est largement compensé par la maintenabilité et la crédibilité du résultat.


Sources & références officielles

💡 Code source disponible : L'intégralité du code de ce portfolio, incluant la configuration PHPStan, PHP CS Fixer et GitHub Actions, est accessible sur github.com/olivaz92/portfolio