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 dearraygénérique - 15% Nullable handling : Les fonctions natives PHP comme
trim(),iconv(),preg_replace()n'acceptent passtring|null - 15% Type narrowing : Les paramètres
Request::get()retournentmixed, nécessitant des vérificationsis_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.jsonetcomposer.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
falseounull, 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.xmletrobots.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,JwtServiceavec 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
.envdistincte 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
- PHPStan — Documentation officielle : phpstan.org/user-guide
- PHP CS Fixer — Documentation officielle : cs.symfony.com
- Symfony — Documentation Best Practices : symfony.com/doc/current/best_practices
- GitHub Actions — Documentation CI/CD : docs.github.com/en/actions
- PSR-12 — Extended Coding Style Guide : php-fig.org/psr/psr-12
- Doctrine ORM — Best Practices : doctrine-project.org/reference/best-practices
- Ondřej Mirtes — "PHPStan: Find Bugs In Your Code Without Writing Tests" (PHP Conference 2023)
- Symfony Blog — "Static Analysis with PHPStan" (2024) : symfony.com/blog
💡 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