Module 1 / 8 · 90 min
01. Installation et analyse reproductibles
Faire analyser le même contrat au poste et en CI.
Comprendre le mécanisme
PHPStan examine le code et ses types sans exécuter les parcours métier habituels. Son résultat dépend de sa version, de la configuration, des extensions, des chemins et de la cible PHP. Une installation locale de développement avec lock versionné rend le contrôle plus reproductible qu’un outil global mis à jour sans suivi. Distinguez le PHP qui exécute l’analyse et la version cible décrite par la configuration.
Raisonner et vérifier
Analysez les classes utiles et leurs contrats, avec les stubs ou extensions nécessaires. Une découverte de symboles mal configurée peut produire des erreurs trompeuses ; exclure massivement le code masque le problème. La pipeline doit échouer si l’analyse échoue et rendre les diagnostics exploitables. Ne publiez pas les secrets du fichier d’environnement pour obtenir un rapport de types.
Version de l’analyseur
La version de PHPStan influence les diagnostics. Les versions peuvent faire évoluer les vérifications. Deux versions peuvent comprendre différemment une annotation ou ajouter un diagnostic. Enregistrez la version utilisée avec le rapport pour pouvoir reproduire le résultat lors d’une revue de release.
Understand this common mistake Tout exécutable PHPStan produit le même rapport pour toujours.
Les versions peuvent faire évoluer les vérifications.
Dépendances verrouillées
Le lock aide à reproduire les dépendances. Il décrit les dépendances résolues, pas les credentials. Exécutez composer install à partir du lock révisé plutôt que résoudre de nouvelles versions à chaque job. Les extensions et leurs dépendances influencent aussi le résultat, pas seulement le binaire principal.
Understand this common mistake Le lock contient les secrets nécessaires à l’analyse.
Il décrit les dépendances résolues, pas les credentials.
Version cible et runtime
La cible PHP doit être configurée correctement. L’analyse et le runtime sont des environnements distincts. phpVersion: 80400 décrit PHP 8.4 pour l’analyse. Cette configuration n’installe aucun interpréteur ; vérifiez séparément les versions de PHP en CLI, dans les workers et dans le serveur web.
Understand this common mistake phpVersion met automatiquement à jour l’interpréteur réel.
L’analyse et le runtime sont des environnements distincts.
Périmètre des chemins
Les chemins analysés déterminent la couverture. Un fichier exclu échappe aux contrôles concernés. Si seul src est analysé, un script d’import placé dans bin peut rester hors rapport. Inventoriez les points d’entrée et justifiez exclusions et code généré avant d’interpréter une suite verte.
Understand this common mistake Exclure src conserve une analyse complète de src.
Un fichier exclu échappe aux contrôles concernés.
Extensions de framework
Les extensions peuvent préciser des contrats de framework. La découverte de symboles doit correspondre aux bibliothèques. Une extension Symfony ou Doctrine décrit des comportements que le code dynamique rend difficiles à inférer. Vérifiez sa compatibilité et gardez un test d’exécution pour les services ou repositories réellement utilisés.
Understand this common mistake Un framework dynamique n’a jamais besoin de configuration adaptée.
La découverte de symboles doit correspondre aux bibliothèques.
Échec observable
Une erreur d’analyse doit faire échouer la CI prévue. Le contrôle doit participer à la décision de qualité. Le pipeline doit respecter le code de sortie de l’analyseur. Une commande suivie de true peut imprimer des erreurs tout en laissant publier ; testez le job avec un défaut volontaire dans une branche de laboratoire.
Understand this common mistake La CI doit ignorer tout code de retour non nul.
Le contrôle doit participer à la décision de qualité.
Même configuration
Les configurations locale et CI doivent être cohérentes. La version et les options peuvent diverger. Un fichier local prioritaire peut masquer la configuration distribuée utilisée en CI. Appelez explicitement le fichier attendu et comparez niveau, chemins et extensions avant de conclure à une différence de comportement inexplicable.
Understand this common mistake Une installation globale non suivie est toujours identique à la CI.
La version et les options peuvent diverger.
Analyse sans parcours
L’analyse statique ne rejoue pas tous les parcours métier. Son périmètre diffère d’une exécution fonctionnelle. PHPStan raisonne sur le code et ses contrats ; il ne simule pas chaque demande utilisateur. Les permissions, courses concurrentes et effets prestataire doivent disposer d’autres preuves ciblées.
Understand this common mistake PHPStan se connecte automatiquement à tous les scénarios réels.
Son périmètre diffère d’une exécution fonctionnelle.
Sans secrets de production
Les secrets ne sont pas nécessaires à un rapport de types. Les diagnostics peuvent rester sans credentials réels. Ne chargez pas une connexion réelle simplement pour découvrir des types. Utilisez des contrats et une configuration de bootstrap minimale ; un analyseur ne doit pas devenir une voie d’accès aux données clientes.
Understand this common mistake Il faut publier .env pour comprendre un type de retour.
Les diagnostics peuvent rester sans credentials réels.
Découverte des symboles
Une mauvaise découverte doit être corrigée à sa cause. L’exclusion masque la couverture au lieu de rétablir les symboles. Si une classe reste inconnue, contrôlez autoload, namespace et dépendances. Un ignore global ferait disparaître le symptôme tout en laissant l’analyse perdre des informations sur les appels concernés.
Understand this common mistake Exclure tout le code est une correction de contrat.
L’exclusion masque la couverture au lieu de rétablir les symboles.
Atelier guidé : lire, expliquer et vérifier le code
parameters:
level: 8
phpVersion: 80400
paths:
- src
Lisez chaque clé de la configuration puis comparez-la aux commandes réellement exécutées en CI. La version cible n’est ni un gestionnaire de runtime ni une garantie de compatibilité de toutes les dépendances. Produisez un rapport reproductible à partir du même lock et du même fichier de configuration.
La valeur décrit la version cible, sans provisionner le runtime. Le paramètre n’installe aucun interpréteur. Seul src est explicitement dans les chemins montrés. Le typage ne remplace pas les scénarios exécutés.
Travail pratique : Exécutez l’analyse localement et en CI sur le même commit avec le même lock, puis introduisez un type de retour incohérent.
Critères de correction : Les deux environnements doivent signaler la régression et la CI doit retourner un échec. Conservez la version et le chemin de configuration dans le diagnostic, sans contenu sensible.
Worked example
composer require --dev phpstan/phpstan
vendor/bin/phpstan analyse -c phpstan.neon --no-progress
# Exemple de configuration pour le laboratoire
parameters:
level: 8
paths:
- src
phpVersion: 80400
La configuration vise PHP 8.4 ; le niveau choisi est un point de départ pédagogique, pas une certification.
Practical exercise
Exécutez l’analyse localement et en CI sur le même commit avec le même lock, puis introduisez un type de retour incohérent.
View a suggested solution
Les deux environnements doivent signaler la régression et la CI doit retourner un échec. Conservez la version et le chemin de configuration dans le diagnostic, sans contenu sensible.
- Outil versionné.
- Cible PHP définie.
- Échec propagé à la CI.
Knowledge check
Un rapport PHPStan dépend-il seulement du contenu de src ?
Explained answer: Non, version, configuration et extensions comptent aussi — L’environnement d’analyse fait partie du contrat.
Related lesson: 01. Installation et analyse reproductibles ↗
Reference documentation ↗ https://phpstan.org/user-guide/getting-started
Module 2 / 8 · 90 min
02. Niveaux 0 à 10 et maîtrise de mixed
Choisir une progression mesurable au lieu d’un score symbolique.
Comprendre le mécanisme
Les niveaux PHPStan sont cumulatifs : un niveau supérieur ajoute des vérifications à ceux qui le précèdent. Au référentiel vérifié pour ce cours, ils vont de 0 à 10 ; le niveau 9 restreint fortement le mixed explicite et le niveau 10 étend la sévérité au mixed implicite. L’alias max suit le plus haut niveau lors des évolutions de l’outil, ce qui demande une politique d’upgrade.
Raisonner et vérifier
mixed représente une information absente ou très générale ; un cast ou un PHPDoc inventé ne constitue pas une validation d’entrée. Réduisez l’incertitude par des gardes et des contrats précis aux frontières. Montez le niveau par étapes suivies, avec critères d’acceptation et budget de correction. Un niveau élevé ne démontre ni absence d’injection ni autorisation correcte : ces propriétés dépassent le typage seul.
Niveaux cumulatifs
Les niveaux sont cumulatifs. La sévérité s’ajoute au socle antérieur. Monter de niveau conserve les contrôles précédents. Gardez une fixture de diagnostic déjà détecté au niveau inférieur pour vérifier que votre changement de configuration n’a pas accidentellement retiré des règles utiles.
Understand this common mistake Le niveau supérieur désactive tous les précédents.
La sévérité s’ajoute au socle antérieur.
Échelle 0 à 10
PHPStan 2.x propose des niveaux de 0 à 10. Le référentiel du cours inclut les niveaux 9 et 10. Dans PHPStan 2.x, zéro est le point d’entrée le plus permissif et dix le plus strict de cette échelle. La version doit accompagner cette indication afin d’éviter une promesse valable pour toute version future.
Understand this common mistake Le plus haut niveau documenté du cours est 8.
Le référentiel du cours inclut les niveaux 9 et 10.
Valeur max
max suit le plus haut niveau disponible. Une mise à jour peut ajouter des contrôles au plus haut niveau. Le mot max suit le sommet disponible après mise à jour. Une release de l’outil peut donc ajouter du travail ; examinez les diagnostics dans une mise à jour dédiée et explicable.
Understand this common mistake max fige éternellement le niveau choisi aujourd’hui.
Une mise à jour peut ajouter des contrôles au plus haut niveau.
Mixed explicite
Le niveau 9 restreint mixed explicite. L’objectif est de limiter les opérations sans type précis. Au niveau neuf, appeler name sur un mixed n’est pas justifié. Vérifiez une interface ou validez une structure puis accédez à son contrat, au lieu d’ajouter une annotation locale qui prétend savoir.
Understand this common mistake Le niveau 9 rend tout mixed librement utilisable.
L’objectif est de limiter les opérations sans type précis.
Mixed implicite
Le niveau 10 étend le contrôle au mixed implicite. Il augmente la sévérité sur les informations absentes. Le niveau dix ajoute des contrôles sur les valeurs restées mixed faute de déclaration. Corrigez les frontières et signatures, plutôt que rendre toutes les valeurs explicitement mixed pour changer seulement le diagnostic.
Understand this common mistake Le niveau 10 supprime les diagnostics de types manquants.
Il augmente la sévérité sur les informations absentes.
Réduction réelle
Une garde réelle peut réduire un type. La conversion ne vérifie pas nécessairement le contrat d’entrée. Après un instanceof sur une interface, ses méthodes deviennent disponibles dans la branche admise. Le chemin contraire doit retourner ou lever une exception avant tout appel qui exige ce contrat.
Understand this common mistake Un cast arbitraire prouve toute validation de donnée.
La conversion ne vérifie pas nécessairement le contrat d’entrée.
Type et permission
Un niveau élevé ne prouve pas les permissions métier. Le typage ne connaît pas automatiquement chaque règle d’accès. Un objet Reservation correctement typé peut appartenir à un autre client. Ajoutez une décision de périmètre sur acteur, tenant et ressource ; le niveau maximal ne fournit pas cette relation métier.
Understand this common mistake Le niveau max certifie toute autorisation d’objet.
Le typage ne connaît pas automatiquement chaque règle d’accès.
Progression planifiée
La montée de niveau doit avoir un plan de correction. La progression doit changer les contrats et comportements concernés. Choisissez un niveau cible, des propriétaires et un ordre de correction. Traitez d’abord les frontières très utilisées ; chaque étape doit garder les diagnostics nouveaux visibles et les tests fonctionnels exécutables.
Understand this common mistake Augmenter le nombre sans traiter les écarts suffit au code.
La progression doit changer les contrats et comportements concernés.
Dette visible
Une baseline peut décrire de la dette. Elle reporte des diagnostics sans modifier le code. Une baseline permet de distinguer une dette connue du nouveau code. Elle doit rester revue et bornée ; régénérer automatiquement toutes les erreurs à chaque analyse détruirait cette distinction.
Understand this common mistake Une baseline transforme les défauts en comportements corrects.
Elle reporte des diagnostics sans modifier le code.
Vérification runtime
Les tests d’exécution complètent l’analyse. Les techniques ont des périmètres complémentaires. Une annotation précise peut mentir sur un résultat externe. Un test de parsing avec null, structure incomplète et mauvais type vérifie que le chemin d’exécution établit effectivement ce que l’analyse suppose.
Understand this common mistake Une analyse verte empêche toute erreur runtime imaginable.
Les techniques ont des périmètres complémentaires.
Atelier guidé : lire, expliquer et vérifier le code
<?php
declare(strict_types=1);
function label(mixed $value): string {
return $value->name();
}
Le paramètre de l’extrait est explicitement mixed et le corps appelle une méthode sans vérifier son existence. Introduisez une interface si le contrat est connu, ou une garde réelle lorsque la frontière reste ouverte. Exécutez aussi le refus avec une valeur incompatible pour vérifier le comportement du programme.
La suppression masque l’incertitude. Une annotation mensongère ne valide pas l’entrée. mixed n’accorde pas toutes les capacités. L’opération doit être justifiée par un type réellement établi.
Travail pratique : Analysez une donnée JSON inconnue à différents niveaux puis remplacez un accès direct par une garde de structure.
Critères de correction : Comparez les diagnostics et vérifiez les refus à l’exécution avec null, tableau et chaîne. Documentez niveau retenu et raisons des écarts temporaires.
Worked example
Entrée mixed → garde exécutée → type réduit → opération autorisée par ce type
Le passage de type doit correspondre à une observation réelle, pas à un commentaire rassurant.
Practical exercise
Analysez une donnée JSON inconnue à différents niveaux puis remplacez un accès direct par une garde de structure.
View a suggested solution
Comparez les diagnostics et vérifiez les refus à l’exécution avec null, tableau et chaîne. Documentez niveau retenu et raisons des écarts temporaires.
- Niveaux cumulatifs.
- Mixed réduit réellement.
- Max suivi lors des upgrades.
Knowledge check
Les niveaux supérieurs désactivent-ils les règles des niveaux inférieurs ?
Explained answer: Non, les niveaux sont cumulatifs — Ils ajoutent des contrôles au socle précédent.
Related lesson: 02. Niveaux 0 à 10 et maîtrise de mixed ↗
Reference documentation ↗ https://phpstan.org/user-guide/rule-levels
Module 3 / 8 · 90 min
03. Types riches et validation des frontières
Utiliser les formes de données sans prétendre valider le réseau par PHPDoc.
Comprendre le mécanisme
Les PHPDocs peuvent exprimer unions, listes, tableaux de forme précise et contraintes utiles à l’analyse. Ces annotations décrivent un contrat ; elles ne transforment pas un corps HTTP en donnée valide. Écrire qu’un tableau contient amount positif ne rend pas vraie une entrée négative. Le code exécuté doit refuser les types, clés et valeurs non conformes avant de produire l’objet du domaine.
Raisonner et vérifier
Séparez parseur, validateur et modèle métier. Une garde valide d’abord la structure puis les invariants : entier n’implique pas montant autorisé, chaîne n’implique pas destination sûre. Préférez des DTO et objets de valeur pour éviter de disperser des tableaux aux clés approximatives. Les erreurs d’entrée doivent être testées sans convertir silencieusement toute valeur en type attendu.
Décrire puis établir
Un PHPDoc décrit un contrat. Une annotation n’exécute pas une garde d’entrée. Le commentaire array{amount: positive-int} n’exécute aucune vérification du JSON. Le code doit contrôler structure, type natif et bornes avant de transmettre la valeur à une fonction qui suppose ce contrat.
Understand this common mistake Un PHPDoc valide automatiquement le JSON réseau.
Une annotation n’exécute pas une garde d’entrée.
Formes de tableau
Une forme de tableau précise ses clés attendues. Les données externes doivent établir ce contrat. Une array shape nomme les clés obligatoires et optionnelles. Testez clé manquante, clé présente avec null et valeur de mauvais type : ces situations ont des contrats distincts qu’un simple accès au tableau masque.
Understand this common mistake Toute array shape garantit la présence réelle de ses clés sans contrôle.
Les données externes doivent établir ce contrat.
Unions
Une union indique plusieurs types possibles. Une opération doit être compatible avec les possibilités restantes. Un retour User|null demande de gérer l’absence avant d’appeler une méthode utilisateur. Les unions documentent plusieurs chemins ; elles ne signifient pas que l’appelant peut choisir arbitrairement celui qui l’arrange.
Understand this common mistake Une union autorise toute méthode de l’un de ses membres.
Une opération doit être compatible avec les possibilités restantes.
Type et domaine
Une garde peut vérifier type et valeur. La positivité est un invariant supplémentaire. is_int puis une comparaison positive rejettent la chaîne '12'. Ajoutez un plafond et une règle de devise pour un montant ; les types raffinés ne doivent pas dépasser ce que la garde établit.
Understand this common mistake is_int garantit que tout montant est positif.
La positivité est un invariant supplémentaire.
Conversions destructrices
Un cast peut masquer une entrée incorrecte. La conversion ne remplace pas une politique d’acceptation. Caster 'inconnu' en entier peut produire zéro et perdre l’erreur initiale. Définissez les représentations admises puis convertissez après validation, en vérifiant dépassement et conservation de la précision.
Understand this common mistake Caster toute entrée en int vérifie la grammaire métier.
La conversion ne remplace pas une politique d’acceptation.
DTO validé
Un DTO peut structurer des données validées. La permission demeure une décision distincte. Construisez un objet d’entrée seulement après validation, ou faites refuser son constructeur en cas d’invariant violé. Un nom comme ValidPayment ne rend pas sûr un constructeur qui accepte toute donnée sans contrôle.
Understand this common mistake Construire un DTO prouve automatiquement l’autorisation.
La permission demeure une décision distincte.
Plafond métier
Un entier positif peut dépasser un plafond métier. Les limites du domaine peuvent être plus étroites. Un positive-int de plusieurs milliards peut être incompatible avec un remboursement de dix euros. Vérifiez solde, autorisation et limites en contexte ; la propriété arithmétique positive reste une seule condition parmi plusieurs.
Understand this common mistake positive-int suffit à autoriser tout montant.
Les limites du domaine peuvent être plus étroites.
Matrice de refus
Les erreurs de frontière doivent être testées. Les absences et mauvais types doivent être refusés selon le contrat. Envoyez tableaux, flottants, chaînes numériques, négatifs et valeurs extrêmes. Vérifiez l’erreur publique et l’absence d’écriture, pas seulement que PHPStan continue d’inférer int à la sortie nominale.
Understand this common mistake Seul un exemple nominal prouve toute validation.
Les absences et mauvais types doivent être refusés selon le contrat.
Parsing distinct
Le parsing et la validation sont des étapes différentes. Structure et invariants peuvent encore être invalides. json_decode peut produire un tableau valide syntaxiquement qui ne contient pas les champs requis. Une exception de parsing et une violation de schéma doivent être traitées comme deux causes différentes.
Understand this common mistake Un JSON syntaxiquement correct est toujours conforme au domaine.
Structure et invariants peuvent encore être invalides.
Politique de conversion
Un contrat doit définir les conversions acceptées. L’API choisit explicitement sa grammaire d’entrée. Une API peut accepter une chaîne décimale par contrat explicite ; une autre exige un entier JSON. Documentez ce choix et évitez qu’un cast opportuniste élargisse silencieusement les entrées acceptées.
Understand this common mistake La chaîne numérique doit toujours être acceptée comme entier dans toute API.
L’API choisit explicitement sa grammaire d’entrée.
Atelier guidé : lire, expliquer et vérifier le code
<?php
declare(strict_types=1);
function accept(mixed $value): bool {
return is_int($value) && $value > 0;
}
echo accept('12') ? 'accepted' : 'rejected';
Évaluez is_int avant la comparaison numérique : la valeur fournie est une chaîne et échoue donc à la première condition. Comparez ensuite avec un entier positif, zéro et un tableau. Pour transformer cet exemple en frontière de paiement, ajoutez un plafond métier et les contrôles d’autorisation nécessaires.
Le résultat affiché est un libellé. La fonction est syntaxiquement valide. La garde demande un int ; la valeur reçue est une string. Le contrat ne prévoit pas ici de conversion de chaîne.
Travail pratique : Envoyez 12, "12", 0, -1 et null à la frontière de laboratoire. Comparez un cast aveugle avec la garde.
Critères de correction : Seul l’entier positif respecte ce contrat strict. Vérifiez ensuite la limite métier et la permission séparément. Une API acceptant aussi une chaîne doit définir explicitement sa grammaire de conversion.
Worked example
<?php
declare(strict_types=1);
function positiveAmount(mixed $value): int
{
if (!is_int($value) || $value <= 0) {
throw new InvalidArgumentException('Montant invalide');
}
return $value;
}
La garde vérifie type et valeur ; elle ne décide pas si un utilisateur peut payer ce montant.
Practical exercise
Envoyez 12, "12", 0, -1 et null à la frontière de laboratoire. Comparez un cast aveugle avec la garde.
View a suggested solution
Seul l’entier positif respecte ce contrat strict. Vérifiez ensuite la limite métier et la permission séparément. Une API acceptant aussi une chaîne doit définir explicitement sa grammaire de conversion.
- Annotations honnêtes.
- Gardes exécutées.
- Invariants distincts des types.
Knowledge check
Un PHPDoc array{amount: positive-int} valide-t-il un JSON reçu ?
Explained answer: Non, il décrit un contrat à établir par le code — Les annotations ne sont pas un validateur de réseau.
Related lesson: 03. Types riches et validation des frontières ↗
Reference documentation ↗ https://phpstan.org/writing-php-code/phpdoc-types
Module 4 / 8 · 90 min
04. Génériques, collections et contrats
Préserver le type des éléments à travers les abstractions.
Comprendre le mécanisme
Les génériques exprimés en PHPDoc permettent de relier types d’entrée, de sortie et de collections. Une collection de Reservation peut ainsi garder son type au fil d’un repository sans retomber dans des tableaux non spécifiés. Cette information améliore la détection d’appels incohérents, mais ne prouve pas que les réservations appartiennent à l’utilisateur courant. Un paramètre de type n’exprime pas tout le contexte d’accès.
Raisonner et vérifier
N’élargissez pas un contrat uniquement pour faire disparaître un diagnostic. Vérifiez la variance et les capacités réellement promises par l’abstraction. Pour les données externes, construisez les objets après validation ; pour les objets persistés, appliquez les filtres et décisions de droits nécessaires. Une extension ou un stub doit décrire le comportement réel de la bibliothèque ciblée.
Relier entrée et sortie
Un paramètre T relie des types dans un contrat. Le générique décrit une relation pour l’analyse. Un paramètre T permet de dire que firstOrNull conserve le type des éléments reçus. Sans ce lien, retourner simplement mixed ferait perdre à chaque appelant la connaissance déjà disponible sur sa collection.
Understand this common mistake T chiffre la valeur de la collection.
Le générique décrit une relation pour l’analyse.
Listes indexées
list<T> décrit les éléments d’une liste. Le type ne donne pas une autorisation d’accès. list<T> décrit une suite à indices entiers contigus depuis zéro. Après une opération qui conserve des clés dispersées, réindexez seulement si cela correspond au contrat ; un tableau quelconque n’est pas nécessairement une liste.
Understand this common mistake list<T> prouve la propriété métier des éléments.
Le type ne donne pas une autorisation d’accès.
Absence explicite
Une sortie T ou null doit traiter null. La nullabilité reste dans le contrat. La liste vide produit null dans firstOrNull. L’appelant doit choisir un défaut, retourner une absence ou lever une erreur adaptée avant de traiter le résultat comme une instance certaine.
Understand this common mistake Le générique supprime automatiquement tous les cas vides.
La nullabilité reste dans le contrat.
Accès cohérents
Une collection typée réduit les accès incohérents. Les permissions ne dérivent pas du seul nom de classe. Une collection Reservation évite de supposer qu’un élément est un User. Ajoutez les méthodes nécessaires au contrat et laissez l’analyse repérer les appels incohérents sans recourir à des casts de confort.
Understand this common mistake Le nom Reservation accorde tous les droits de lecture.
Les permissions ne dérivent pas du seul nom de classe.
Variance et mutation
La variance doit respecter les capacités promises. Les entrées et sorties imposent des contraintes différentes. Une collection lisible d’un sous-type et une collection modifiable n’ont pas les mêmes possibilités de substitution. Si un appelant peut insérer un autre sous-type, la promesse faite au détenteur initial peut être violée.
Understand this common mistake Tout élargissement de type est compatible avec toute substitution.
Les entrées et sorties imposent des contraintes différentes.
Entrées externes
Les données externes doivent être validées avant construction du modèle. Le commentaire ne remplace pas le contrôle runtime. Une annotation list<Order> autour d’un JSON ne construit aucun Order. Parsez chaque élément et vérifiez sa structure avant de former une collection dont les méthodes supposent des objets valides.
Understand this common mistake Une annotation générique transforme toute entrée hostile en objet sûr.
Le commentaire ne remplace pas le contrôle runtime.
Stubs honnêtes
Un stub doit décrire le comportement réel. Une description fausse peut tromper l’analyse. Un stub de bibliothèque doit conserver null et les types d’erreur réellement possibles. Enlever une possibilité pour rendre l’analyse verte ne change pas le comportement du fournisseur exécuté en production.
Understand this common mistake Un stub peut inventer un retour pour faire taire toute erreur.
Une description fausse peut tromper l’analyse.
Périmètre du repository
Les repositories doivent conserver leurs règles de périmètre. Le filtre métier doit être implémenté. Un repository typé doit aussi filtrer le tenant ou appliquer la politique appropriée. La signature Order ne signifie pas Order autorisé pour cet acteur ; testez les identifiants d’un autre périmètre.
Understand this common mistake Un retour list<Reservation> filtre automatiquement le tenant.
Le filtre métier doit être implémenté.
Cas vide
Les tests doivent couvrir une liste vide. La non-vacuité demande une contrainte différente. Vérifiez une collection vide avant le scénario à plusieurs éléments. Ce cas expose le contrat nullable et les opérations qui supposent sans preuve un premier élément toujours présent.
Understand this common mistake Une liste typée est nécessairement non vide.
La non-vacuité demande une contrainte différente.
Abstraction précise
Une abstraction doit préciser les types d’éléments. Cela retire des informations utiles à l’analyse. Une interface de collection doit déclarer le type de ses valeurs et de ses clés lorsque ces deux informations comptent. Un simple iterable perd le lien qui rend les traitements suivants vérifiables.
Understand this common mistake Remplacer tous les éléments par mixed améliore toujours la précision.
Cela retire des informations utiles à l’analyse.
Atelier guidé : lire, expliquer et vérifier le code
<?php
declare(strict_types=1);
/** @template T
* @param list<T> $items
* @return T|null
*/
function firstOrNull(array $items): mixed {
return $items[0] ?? null;
}
$result = firstOrNull([]);
Suivez le type T depuis le paramètre jusqu’au retour puis exécutez le cas de la liste vide. La présence de null dans la signature décrit un résultat réel. Proposez un traitement d’absence compatible avec le métier, sans remplacer la signature par une promesse artificiellement plus forte.
Le générique relie des types, pas les permissions. La sortie annonce l’absence et le code la produit ici. La fonction ne crée pas un élément. Le type list ne donne pas cette cardinalité minimale.
Travail pratique : Utilisez la fonction avec une liste d’objets puis une liste de chaînes. Analysez un appel de méthode sans garde du résultat.
Critères de correction : L’analyse doit conserver le type de chaque liste et signaler le cas nullable pertinent. Ajoutez une garde explicite ; vérifiez aussi les listes vides à l’exécution.
Worked example
<?php
declare(strict_types=1);
/**
* @template T
* @param list<T> $items
* @return T|null
*/
function firstOrNull(array $items): mixed
{
return $items[0] ?? null;
}
La relation T est conservée par l’analyse, tandis que null reste une issue possible.
Practical exercise
Utilisez la fonction avec une liste d’objets puis une liste de chaînes. Analysez un appel de méthode sans garde du résultat.
View a suggested solution
L’analyse doit conserver le type de chaque liste et signaler le cas nullable pertinent. Ajoutez une garde explicite ; vérifiez aussi les listes vides à l’exécution.
- Relations de types conservées.
- Nullable traité.
- Droits hors du seul paramètre T.
Knowledge check
Que permet un paramètre générique T ?
Explained answer: Relier les types dans un contrat réutilisable — Il exprime une relation de types.
Related lesson: 04. Génériques, collections et contrats ↗
Reference documentation ↗ https://phpstan.org/blog/generics-in-php-using-phpdocs
Module 5 / 8 · 90 min
05. Réduction de types et assertions honnêtes
Faire correspondre preuve statique et garde réelle.
Comprendre le mécanisme
PHPStan réduit un type après certaines conditions et peut comprendre des fonctions de garde annotées avec phpstan-assert. Cette annotation est une promesse de comportement à vérifier dans le corps de la fonction. Une assertion documentaire qui ment peut conduire l’analyse à accepter un accès invalide. L’instruction assert de PHP ne doit pas porter seule une validation de sécurité indispensable, car sa configuration d’exécution peut la rendre inactive.
Raisonner et vérifier
Utilisez des conditions exécutées qui refusent le cas invalide. Testez la garde et ses chemins de retour, puis décrivez exactement ce qu’elle garantit. Elle peut établir une forme de donnée sans prouver son authenticité ou son autorisation. Évitez les PHPDocs locaux qui écrasent le type d’une donnée externe seulement pour faire passer la CI.
Garde exécutable
Une garde doit exécuter son refus. L’annotation décrit une promesse du code. Une fonction requireText doit tester réellement chaîne et non-vacuité, puis refuser le reste. Son annotation phpstan-assert décrit la propriété qui devient vraie après un retour normal de cette fonction.
Understand this common mistake phpstan-assert génère le corps de validation.
L’annotation décrit une promesse du code.
Annotation trompeuse
Une annotation fausse peut tromper l’analyse. Le contrat doit être vérifié et testé. Un commentaire local peut affirmer qu’une valeur est un User alors que le réseau a fourni un entier. Préférez établir le type à la frontière et conserver une preuve visible dans le flot de contrôle.
Understand this common mistake L’analyse corrige automatiquement toute promesse mensongère.
Le contrat doit être vérifié et testé.
Configuration assert
assert PHP dépend de sa configuration d’exécution. Une validation indispensable doit avoir un refus garanti. Les assertions PHP peuvent être désactivées selon la configuration de compilation et d’exécution. Une validation indispensable doit rester un contrôle explicite dont l’absence ne dépend pas d’une option d’optimisation.
Understand this common mistake assert est toujours une garde de sécurité active en production.
Une validation indispensable doit avoir un refus garanti.
Branches d’une union
Une condition peut réduire une union. La preuve doit correspondre au chemin réellement pris. Dans une branche où value est une string, strlen devient cohérent. Vérifiez le chemin des autres types et la sortie de chaque branche ; une condition sans refus ne protège pas le code placé ensuite.
Understand this common mistake Une condition non exécutée valide quand même l’entrée réseau.
La preuve doit correspondre au chemin réellement pris.
Chaîne non vide
non-empty-string n’est pas une preuve d’innocuité HTML. Le contexte de sortie exige d’autres contrôles. Un texte '<script>' est non vide. L’innocuité HTML, le paramétrage SQL et l’admission d’une destination URL nécessitent des protections propres à leur contexte, distinctes de la simple longueur.
Understand this common mistake Toute chaîne non vide est sûre pour innerHTML.
Le contexte de sortie exige d’autres contrôles.
Tester le refus
Un refus de garde doit être testé. Les chemins invalides demandent une vérification réelle. Appelez la garde avec null, chaîne vide et type inattendu. Une exception attendue et l’absence d’effet aval démontrent que l’annotation décrit bien un contrôle exécuté, plutôt qu’une convention ignorée.
Understand this common mistake Le seul PHPDoc suffit à prouver les exceptions.
Les chemins invalides demandent une vérification réelle.
Authenticité distincte
Un type précis ne prouve pas l’authenticité. Le type et la provenance sont deux propriétés. Une structure de jeton correctement typée peut être forgée. La signature, l’émetteur et le destinataire demandent leur validation ; un objet Claims n’établit pas à lui seul une relation de confiance.
Understand this common mistake Une chaîne de type correct est nécessairement signée.
Le type et la provenance sont deux propriétés.
PHPDoc local
Les PHPDocs locaux doivent rester honnêtes. Il peut masquer l’incertitude au lieu de la résoudre. Réservez les annotations locales aux limites réellement connues et explicables de l’analyse. Si une validation manque dans le programme, ajoutez ce code au lieu de modifier uniquement l’information donnée à l’outil.
Understand this common mistake Un @var inventé est une correction de toute donnée externe.
Il peut masquer l’incertitude au lieu de la résoudre.
Structure et droit
Une garde de structure ne donne pas de droit métier. Le modèle de permissions reste distinct. Une entrée contenant projectId:int peut viser le projet d’un tiers. Après validation structurelle, chargez la ressource dans le bon périmètre et appliquez la décision d’autorisation avant la mutation.
Understand this common mistake Une array shape valide autorise toute opération.
Le modèle de permissions reste distinct.
Exception explicite
Une exception explicite peut imposer le rejet. Le comportement doit respecter la promesse de validation. Une InvalidArgumentException permet de couper le chemin invalide. L’analyse peut alors déduire le type sur le chemin restant ; testez aussi que le gestionnaire d’erreur public ne divulgue pas la valeur sensible refusée.
Understand this common mistake Une garde peut retourner normalement sur toute entrée invalide.
Le comportement doit respecter la promesse de validation.
Atelier guidé : lire, expliquer et vérifier le code
<?php
declare(strict_types=1);
/** @phpstan-assert non-empty-string $value */
function requireText(mixed $value): void {
// aucune garde exécutée
}
requireText(null);
echo 'continued';
Dans l’extrait, le corps ne fait rien et null traverse la fonction. L’annotation ne produit donc aucun arrêt à l’exécution. Ajoutez une garde dont le retour normal garantit exactement non-empty-string, puis testez valeur vide, null et valeur admise sans dépendre d’assert PHP.
Le commentaire n’exécute pas le refus ; le programme continue. Aucun contrôle PHP natif n’est généré par ce PHPDoc. Aucune opération de validation ou transformation ne le fait. L’analyse ne réécrit pas la fonction.
Travail pratique : Retirez temporairement la condition dans le laboratoire, comparez l’analyse puis testez null et chaîne vide à l’exécution.
Critères de correction : La promesse peut tromper l’analyse si le corps ment. Restaurez la garde et ajoutez des tests des entrées invalides, puis vérifiez que les usages contextuels restent contrôlés.
Worked example
<?php
declare(strict_types=1);
/** @phpstan-assert non-empty-string $value */
function requireNonEmpty(mixed $value): void
{
if (!is_string($value) || $value === '') {
throw new InvalidArgumentException('Chaîne requise');
}
}
Le refus réel justifie la promesse non-empty-string. Cela n’assainit pas cette chaîne pour tous les contextes.
Practical exercise
Retirez temporairement la condition dans le laboratoire, comparez l’analyse puis testez null et chaîne vide à l’exécution.
View a suggested solution
La promesse peut tromper l’analyse si le corps ment. Restaurez la garde et ajoutez des tests des entrées invalides, puis vérifiez que les usages contextuels restent contrôlés.
- Garde réelle.
- Promesse exacte.
- Tests des refus.
Knowledge check
Une annotation phpstan-assert remplace-t-elle le corps de validation ?
Explained answer: Non, elle décrit ce que le corps doit garantir — La promesse doit correspondre à un comportement exécuté.
Related lesson: 05. Réduction de types et assertions honnêtes ↗
Reference documentation ↗ https://phpstan.org/writing-php-code/narrowing-types
Module 6 / 8 · 90 min
06. Baseline et retrait de dette
Adopter progressivement sans ouvrir une exclusion générale.
Comprendre le mécanisme
Une baseline enregistre des erreurs existantes pour permettre une adoption progressive à un niveau donné. Elle ne corrige pas les défauts et ne doit pas devenir une règle d’ignorance universelle. Son périmètre doit être versionné et relu comme de la dette : catégories, chemins, quantités et plan de retrait. Les erreurs nouvelles doivent rester visibles.
Raisonner et vérifier
Évitez de régénérer automatiquement la baseline à chaque exécution de CI, car une régression pourrait être absorbée. Une suppression doit être justifiée par un faux positif ou une dette connue, avec un périmètre étroit. Après correction, retirez l’entrée devenue inutile et gardez les mécanismes qui signalent les exceptions non pertinentes. La revue doit distinguer amélioration réelle et simple déplacement des diagnostics.
Inventaire de diagnostics
Une baseline décrit des diagnostics existants. Elle ne réécrit pas les comportements du code. Une baseline enregistre des écarts déjà connus sans corriger leur cause. Associez les entrées sensibles à un travail de résolution et conservez la possibilité de retrouver pourquoi elles ont été admises temporairement.
Understand this common mistake Une baseline corrige les bugs à l’exécution.
Elle ne réécrit pas les comportements du code.
Nouveaux défauts visibles
Les nouvelles erreurs doivent rester visibles. Elle doit distinguer dette connue et nouvel écart. La CI doit comparer le code à la dette révisée, pas recréer cette dette avant chaque analyse. Introduisez un défaut de laboratoire pour confirmer qu’un diagnostic nouveau fait effectivement échouer le pipeline.
Understand this common mistake La baseline doit absorber toute régression nouvelle en CI.
Elle doit distinguer dette connue et nouvel écart.
Régénération encadrée
La régénération doit être revue. Cette pratique peut normaliser chaque nouveau défaut. Une mise à jour d’outil peut modifier les diagnostics et nécessiter une régénération. Examinez le diff et ses raisons ; une hausse silencieuse du nombre d’erreurs masquées demande une explication.
Understand this common mistake Régénérer aveuglément avant chaque analyse protège les régressions.
Cette pratique peut normaliser chaque nouveau défaut.
Suppression ciblée
Une suppression doit être étroite et justifiée. Une suppression globale masque la couverture. Préférez un identifiant de diagnostic, un chemin et une justification précis à une expression globale. Une règle très large peut masquer demain une nouvelle erreur dans une zone jusque-là correcte.
Understand this common mistake Ignorer tout message est une stratégie de précision.
Une suppression globale masque la couverture.
Exception devenue inutile
La correction doit retirer l’exception devenue inutile. La dette doit diminuer avec les corrections. Quand le code est corrigé, retirez l’entrée associée. Vérifiez le signal d’erreur ignorée non retrouvée si configuré ; il aide à éviter que des suppressions obsolètes restent actives sans propriétaire.
Understand this common mistake Une exception ancienne doit être conservée pour toujours.
La dette doit diminuer avec les corrections.
Revue du diff
Le diff de baseline doit être relu. Des motifs élargis peuvent cacher davantage d’erreurs. Lisez les messages, chemins et nombres d’occurrences modifiés. Une petite variation textuelle peut cacher un élargissement important du périmètre ; le nombre total seul ne décrit pas le risque ajouté.
Understand this common mistake Un nombre de lignes réduit prouve forcément moins de dette.
Des motifs élargis peuvent cacher davantage d’erreurs.
Exclusions
Les chemins exclus doivent être compris. La propriété du code n’a pas changé. Exclure tout un dossier supprime aussi les futurs diagnostics de ce dossier. Documentez pourquoi le code est hors périmètre et quelle autre vérification couvre ses points d’entrée réels.
Understand this common mistake Déplacer un défaut vers un dossier exclu est une correction.
La propriété du code n’a pas changé.
Adoption progressive
Une baseline peut faciliter une montée progressive. Elle peut servir à planifier la réduction de dette. Vous pouvez viser un niveau plus strict en acceptant une dette initiale limitée. Empêchez les ajouts et retirez les entrées à mesure des corrections afin que cette stratégie produise une progression mesurable.
Understand this common mistake La baseline impose de ne jamais augmenter le niveau.
Elle peut servir à planifier la réduction de dette.
Faux positif démontré
Le faux positif doit être démontré. Le diagnostic peut révéler un défaut réel. Un diagnostic gênant n’est pas automatiquement faux. Réduisez le cas, vérifiez le contrat de la dépendance et fournissez une fixture prouvant pourquoi le code est sûr avant d’ajouter une suppression.
Understand this common mistake Toute erreur gênante est automatiquement un faux positif.
Le diagnostic peut révéler un défaut réel.
Après correction
Les tests restent nécessaires après suppression d’un diagnostic. La suppression ne prouve aucune correction runtime. Une modification de type peut changer un chemin d’erreur ou une conversion. Gardez les tests de comportement concernés ; la disparition du diagnostic ne démontre pas que les sorties métier restent conformes.
Understand this common mistake Un ignore dispense de vérifier le comportement.
La suppression ne prouve aucune correction runtime.
Atelier guidé : lire, expliquer et vérifier le code
vendor/bin/phpstan analyse --generate-baseline
vendor/bin/phpstan analyse
La séquence proposée génère une baseline à partir du code courant avant de l’analyser. Elle risque donc d’admettre le défaut même qu’elle devait détecter. Réorganisez la CI pour utiliser la baseline révisée du dépôt et gardez la génération comme opération de maintenance dont le diff est relu.
La baseline n’édite pas le code pour le corriger. Rien ne prouve une réduction du périmètre ignoré. L’analyse statique n’exécute pas toute la suite. Une régénération aveugle peut normaliser de nouveaux écarts.
Travail pratique : Générez une baseline de laboratoire puis introduisez une nouvelle erreur et corrigez une ancienne.
Critères de correction : La nouvelle erreur fait échouer la CI ; l’ancienne exception est supprimée après correction. Comparez le diff de baseline et refusez les motifs globaux ajoutés sans justification.
Worked example
Dette existante documentée → baseline revue
Nouvelle erreur → échec de CI
Correction → retrait de l’exception correspondante
Le cycle protège la progression plutôt qu’un compteur artificiellement toujours vert.
Practical exercise
Générez une baseline de laboratoire puis introduisez une nouvelle erreur et corrigez une ancienne.
View a suggested solution
La nouvelle erreur fait échouer la CI ; l’ancienne exception est supprimée après correction. Comparez le diff de baseline et refusez les motifs globaux ajoutés sans justification.
- Baseline revue.
- Pas de régénération aveugle en CI.
- Exceptions retirées après correction.
Knowledge check
Une baseline corrige-t-elle les bugs enregistrés ?
Explained answer: Non, elle reporte des diagnostics connus — Le code et sa dette restent à traiter.
Related lesson: 06. Baseline et retrait de dette ↗
Reference documentation ↗ https://phpstan.org/user-guide/baseline
Module 7 / 8 · 90 min
07. Règles personnalisées et limites de sécurité
Automatiser une politique locale sans promettre un audit complet.
Comprendre le mécanisme
Une règle PHPStan personnalisée peut examiner des nœuds et des types pour signaler un motif interdit dans un contexte. Elle doit formuler une propriété précise et être testée avec cas acceptés et refusés. Interdire un appel direct à une fonction legacy peut être utile ; cela ne démontre pas à soi seul l’absence d’injection, de fuite de secrets ou de défaut d’autorisation dans tous les chemins.
Raisonner et vérifier
PHPStan standard n’est pas un analyseur complet de flux de données sensibles pour toutes les vulnérabilités. Des outils spécialisés, des extensions, la revue et les tests de sécurité ont des périmètres complémentaires. Évaluez faux positifs, faux négatifs et coût de maintenance avant de rendre une règle bloquante. Une règle ignorant un wrapper ou un appel dynamique doit l’indiquer dans ses limites.
Propriété contrôlée
Une règle doit exprimer une propriété précise. Une règle locale a un périmètre limité. Définissez précisément si une règle interdit une API, un argument ou un flux dangereux. Un diagnostic doit annoncer ce qu’il établit, sans promettre une sécurité générale à partir d’un motif syntaxique étroit.
Understand this common mistake Interdire une fonction prouve toute sécurité applicative.
Une règle locale a un périmètre limité.
Cas admis
Les fixtures doivent inclure du code accepté. Elle doit discriminer selon la politique. Une règle qui bloque tout semble détecter chaque défaut mais empêche aussi le code correct. Ajoutez des fixtures positives et négatives afin de mesurer les deux côtés de sa frontière de décision.
Understand this common mistake Une règle refusant tout est toujours correcte.
Elle doit discriminer selon la politique.
Angles morts
Les faux négatifs doivent être documentés. Les chemins non suivis peuvent conserver un risque. Documentez les formes que la règle ne reconnaît pas : appel dynamique, alias ou wrapper. Cette information permet aux réviseurs de compléter l’analyse au lieu de considérer le rapport vert comme une preuve exhaustive.
Understand this common mistake Une absence de diagnostic prouve toute absence de vulnérabilité.
Les chemins non suivis peuvent conserver un risque.
Wrappers et indirection
Un wrapper peut échapper à une règle syntaxique naïve. Les capacités de la règle doivent être vérifiées. Un helper peut appeler hash avec un nom d’algorithme reçu par paramètre. Une recherche de md5 comme nom de fonction ne voit pas forcément ce chemin ; réduisez le cas dans une fixture.
Understand this common mistake Tout appel indirect est automatiquement compris par toute règle.
Les capacités de la règle doivent être vérifiées.
Flux de données
PHPStan standard n’est pas un audit complet de taint. Le typage ne couvre pas toutes les vulnérabilités. Le typage standard ne suit pas automatiquement toute provenance d’une entrée jusqu’à SQL ou HTML. Utilisez requêtes paramétrées, encodage adapté et, lorsque pertinent, des outils spécialisés avec hypothèses connues.
Understand this common mistake Le niveau max suit automatiquement toutes les données sensibles.
Le typage ne couvre pas toutes les vulnérabilités.
Autorisation métier
La revue d’autorisation complète les règles de types. La politique métier demande une vérification dédiée. Une règle syntaxique peut vérifier qu’un appel de contrôle existe sans prouver que son sujet est correct. Testez aussi que l’acteur Bob ne peut modifier l’objet d’Alice avec une référence valide.
Understand this common mistake Des types cohérents prouvent tout droit d’objet.
La politique métier demande une vérification dédiée.
Maintenance
Le coût de maintenance d’une règle compte. Les API et hypothèses peuvent évoluer. L’AST et les contrats des bibliothèques peuvent évoluer. Versionnez la règle avec ses fixtures et vérifiez son comportement lors des mises à jour de PHPStan afin d’éviter des exceptions devenues trompeuses.
Understand this common mistake Une règle personnalisée reste correcte pour toute version future.
Les API et hypothèses peuvent évoluer.
Diagnostic utile
Un diagnostic doit être exploitable. La localisation et la propriété facilitent une action correcte. Indiquez l’emplacement, le risque précis et une correction possible. Un message vague comme code dangereux encourage les suppressions ; une explication testable aide à corriger la cause.
Understand this common mistake Un message sans contexte suffit à toute correction.
La localisation et la propriété facilitent une action correcte.
Stockage de mots de passe
Interdire MD5 ne valide pas tout stockage de mot de passe. D’autres mauvais choix restent possibles. Interdire MD5 laisse possibles un hash rapide différent, un coût inadapté ou des logs contenant le secret. Vérifiez algorithme adaptatif, vérification, rehash et cycle de stockage séparément.
Understand this common mistake L’absence de md5 prouve un coût adaptatif approprié.
D’autres mauvais choix restent possibles.
Outils complémentaires
Les outils spécialisés peuvent compléter l’analyse. Les contrôles ont des périmètres complémentaires. Analyse des dépendances, revue de secrets, tests d’injection et contrôle d’exploitation répondent à des risques distincts. Définissez la preuve attendue pour chacun plutôt qu’additionner des logos d’outils sans critère d’acceptation.
Understand this common mistake Installer PHPStan rend tous les autres contrôles inutiles.
Les contrôles ont des périmètres complémentaires.
Atelier guidé : lire, expliquer et vérifier le code
<?php
declare(strict_types=1);
$algorithm = 'md5';
$fingerprint = hash($algorithm, 'lab-data');
echo strlen($fingerprint);
L’extrait calcule un condensat MD5 via hash et une variable. Une règle limitée aux appels directs à md5 peut ne rien signaler. Écrivez une fixture pour ce cas, puis précisez le périmètre revendiqué par la règle. Même une détection améliorée ne valide pas tout le cycle des mots de passe.
Ce calcul reste un hash rapide. Le périmètre annoncé ne comprend pas ce motif. Interdire un seul nom de fonction ne couvre pas tous les usages. hash peut calculer cet algorithme.
Travail pratique : Écrivez la spécification d’une règle interdisant un appel legacy dans un module donné. Préparez un wrapper et un appel hors périmètre.
Critères de correction : Définissez les résultats attendus pour chaque fixture et vérifiez les diagnostics. Documentez ce que la règle ne suit pas, puis complétez avec une revue d’usage et des tests de migration.
Worked example
Politique locale : interdire md5 dans le stockage de nouveaux mots de passe
Règle → fixtures positives/négatives → diagnostic → revue des limites
Le contrôle syntaxique local ne remplace pas l’inventaire des usages cryptographiques.
Practical exercise
Écrivez la spécification d’une règle interdisant un appel legacy dans un module donné. Préparez un wrapper et un appel hors périmètre.
View a suggested solution
Définissez les résultats attendus pour chaque fixture et vérifiez les diagnostics. Documentez ce que la règle ne suit pas, puis complétez avec une revue d’usage et des tests de migration.
- Propriété précise.
- Fixtures acceptées et refusées.
- Angles morts documentés.
Knowledge check
Une règle interdisant md5 prouve-t-elle la sécurité de tous les mots de passe ?
Explained answer: Non, coût, stockage et cycle doivent aussi être vérifiés — L’absence d’un motif ne garantit pas tout le système.
Related lesson: 07. Règles personnalisées et limites de sécurité ↗
Reference documentation ↗ https://phpstan.org/developing-extensions/rules
Module 8 / 8 · 90 min
08. Cycle sécurisé et préparation aux certifications
Relier exigences, implémentation, tests et exploitation.
Comprendre le mécanisme
Les domaines publics CSSLP abordent la sécurité sur le cycle de vie : concepts, exigences, architecture, implémentation, vérification, exploitation et chaîne d’approvisionnement. Un cours PHPStan ne couvre qu’une partie de ces compétences. Une préparation sérieuse associe lecture du plan officiel en vigueur, exercices de raisonnement et preuves de pratique ; elle n’utilise pas des banques de questions divulguées.
Raisonner et vérifier
Construisez une matrice exigence, menace, contrôle, preuve et propriétaire. Les types réduisent certaines erreurs de programmation ; les tests négatifs vérifient le comportement ; la revue examine l’autorisation ; le contrôle de dépendances détecte des risques connus dans son périmètre. La CI doit préserver cette distinction et l’exploitation doit fournir un suivi d’incident et de renouvellement. Un score pédagogique ne constitue ni une certification de personne ni un audit complet du produit.
Exigences vérifiables
Le cycle sécurisé commence dès les exigences. Les choix de conception conditionnent les contrôles possibles. Avant l’implémentation, exprimez la propriété attendue : par exemple aucun client ne lit les factures d’un autre tenant. Déclinez-la en contrôle concret, scénario de refus et preuve conservée avec la release.
Understand this common mistake La sécurité commence seulement après le déploiement.
Les choix de conception conditionnent les contrôles possibles.
Repères de préparation
Les domaines publics CSSLP servent de repère de préparation. Le fournisseur officiel définit son processus de certification. Les domaines publics CSSLP permettent d’organiser conception, développement, vérification et exploitation sécurisés. Utilisez les objectifs publiés et des exercices originaux ; une matrice personnelle aide à repérer les domaines peu travaillés.
Understand this common mistake Ce cours délivre automatiquement le titre CSSLP.
Le fournisseur officiel définit son processus de certification.
Évaluation indépendante
Un QCM indépendant ne reproduit pas l’examen officiel. La mesure pédagogique a un autre périmètre. Les QCM du site entraînent et mesurent une progression locale. Leur score ne délivre aucun titre officiel et ne reproduit ni le contenu confidentiel ni les conditions administratives d’un organisme certificateur.
Understand this common mistake Un score local équivaut nécessairement au seuil officiel.
La mesure pédagogique a un autre périmètre.
Chaîne de preuves
Les preuves doivent relier risque et contrôle. Le volume ne montre pas les propriétés vérifiées. Pour chaque menace, notez exigence, contrôle, test, résultat et propriétaire. Un scanner vert ne suffit pas si son périmètre ne contient pas le composant exposé ou la propriété annoncée.
Understand this common mistake Compter les classes suffit à une assurance de sécurité.
Le volume ne montre pas les propriétés vérifiées.
Tests négatifs
Les tests négatifs complètent les tests nominaux. Les frontières doivent être exercées directement. Un scénario nominal prouve qu’un accès autorisé fonctionne. Ajoutez acteur tiers, jeton expiré et entrée malformée en vérifiant l’absence d’effet ; ces refus établissent d’autres frontières que le succès.
Understand this common mistake Un succès prouve tous les refus d’accès.
Les frontières doivent être exercées directement.
Chaîne de dépendances
Les dépendances font partie du cycle de risque. La chaîne d’approvisionnement demande ses propres contrôles. Une bibliothèque et son système de build participent à la surface d’attaque. Verrouillez versions, révisez mises à jour et évitez d’exécuter du code non fiable avec les secrets d’une pipeline privilégiée.
Understand this common mistake Un code bien typé rend toute dépendance sûre.
La chaîne d’approvisionnement demande ses propres contrôles.
Après déploiement
L’exploitation inclut surveillance et réponse à incident. Le système évolue et peut subir des incidents. Définissez surveillance, triage et réponse à incident avant la panne. Les journaux doivent être utiles sans exposer les credentials ; les procédures de rotation et restauration doivent être exercées.
Understand this common mistake La CI verte supprime tout besoin de surveillance.
Le système évolue et peut subir des incidents.
Lacunes déclarées
Un dossier doit annoncer les lacunes restantes. L’assurance doit rester proportionnée aux preuves. Une propriété non testée doit rester visible dans le dossier. Assignez un traitement et un délai au risque résiduel ; masquer l’absence de preuve derrière un score élevé empêche une décision éclairée.
Understand this common mistake Une lacune non documentée constitue une garantie.
L’assurance doit rester proportionnée aux preuves.
Questions originales
Les questions confidentielles ne sont pas nécessaires à la préparation. Les plans publics et exercices originaux suffisent à guider l’étude. Préparez-vous avec documentation publique, cas pratiques et questions authored pour le cours. L’apprentissage doit porter sur les mécanismes et leur application, sans rechercher de banques confidentielles d’examen officiel.
Understand this common mistake Une préparation sérieuse exige des banques divulguées.
Les plans publics et exercices originaux suffisent à guider l’étude.
Responsabilité du risque
Chaque risque doit avoir un traitement et un propriétaire. Le suivi opérationnel ne se résume pas au score. Un risque sans propriétaire reste facilement sans suite. Désignez qui accepte, corrige ou surveille chaque risque et précisez le signal qui doit déclencher une nouvelle évaluation.
Understand this common mistake Un score global remplace toute responsabilité de suivi.
Le suivi opérationnel ne se résume pas au score.
Atelier guidé : lire, expliquer et vérifier le code
# Périmètre de l’exercice : seule cette commande est exécutée.
script:
- vendor/bin/phpstan analyse --no-progress
La pipeline d’exemple exécute uniquement l’analyse statique. Demandez quelle preuve manque pour l’isolation des réservations, puis écrivez un test à deux acteurs sur le même objet. Associez ce test à une menace explicite et à un responsable, sans attribuer au typage une propriété qu’il ne mesure pas.
Le bloc existe dans l’extrait. La politique d’objet demande des tests et une revue dédiés. La commande le montre bien. Le chemin est explicite.
Travail pratique : Établissez un dossier de préparation pour l’API de synthèse : trois risques, contrôles associés, preuves et lacunes restantes.
Critères de correction : Joignez configuration versionnée, résultat d’analyse, tests de refus, revue de droits et procédure d’incident. Reliez chaque pièce à une exigence et à un domaine du plan public. Consultez le fournisseur de certification pour ses conditions et modalités en vigueur.
CI : exécuter un fork sans lui donner les secrets de déploiement
pull_request_target s’exécute dans un contexte qui peut disposer de privilèges du dépôt cible. Si le workflow checkout le commit non fiable d’un fork puis lance ses tests, ce code peut contenir des scripts capables de lire secrets et credentials du job.
Séparez les tests de code non approuvé dans un contexte sans secrets et sans droits d’écriture. Réservez publication et déploiement à une chaîne de confiance explicite. La séparation doit aussi considérer les artefacts transmis : un job privilégié ne doit pas exécuter aveuglément un script ou un binaire produit par le job non fiable.
Testez les permissions effectives et les événements autorisés avec des données factices. Réduisez GITHUB_TOKEN aux besoins du job et épinglez les dépendances d’action selon votre politique. Le masquage de logs ne constitue pas une frontière de confidentialité contre un programme qui possède déjà le secret et peut l’envoyer par un autre canal.
Points de vigilance : Le code peut exfiltrer un secret autrement que par son affichage littéral dans les logs. Le nom du workflow et l’extension du fichier ne créent pas de frontière de confiance. Un délai court laisse le temps de détourner un secret ou une permission. La séparation empêche le code non approuvé d’hériter de privilèges de déploiement ; contrôler aussi les artefacts transmis.
Reference documentation ↗
Migration Strangler : une seule implémentation propriétaire de l’effet
Une extraction progressive laisse le monolithe et le nouveau service coexister. Définissez une frontière fonctionnelle et un contrat stable, puis routez une cohorte maîtrisée vers une seule implémentation active pour chaque intention de paiement.
La réversibilité doit porter sur les demandes futures et les états que l’ancienne implémentation sait encore comprendre. Elle ne signifie pas pouvoir annuler un débit externe déjà accepté. Évitez un double appel PSP pour comparer les sorties ; utilisez tests de contrat ou comparaison sans effet lorsque cela est réellement possible.
Définissez propriétaire de l’écriture, identité d’opération, compatibilité des messages et procédure de rapprochement pendant la transition. Testez bascule partielle et retour de routage avec états en cours. Les métriques doivent détecter divergence et erreur sans exposer les credentials. Une migration réussie retire progressivement les anciens chemins après preuve ; laisser deux producteurs d’effets en concurrence sans règle claire ajoute une nouvelle source de doublons.
Points de vigilance : La migration incrémentale exige un routage maîtrisé et un propriétaire clair de chaque effet irréversible. La première réponse ne supprime pas l’autre débit éventuellement exécuté. La suppression préalable retire le filet de retour demandé. Des écritures et décisions concurrentes sans propriétaire compliquent les invariants et le retour arrière.
Reference documentation ↗
Worked example
Exigence → conception → code → analyse + tests + revue → déploiement → surveillance
Chaque étape conserve ses preuves et ses limites.
Le parcours rejoint des domaines publics de compétences sans prétendre reproduire l’examen officiel.
Practical exercise
Établissez un dossier de préparation pour l’API de synthèse : trois risques, contrôles associés, preuves et lacunes restantes.
View a suggested solution
Joignez configuration versionnée, résultat d’analyse, tests de refus, revue de droits et procédure d’incident. Reliez chaque pièce à une exigence et à un domaine du plan public. Consultez le fournisseur de certification pour ses conditions et modalités en vigueur.
- Couverture partielle annoncée.
- Questions originales.
- Preuves sur tout le cycle.
Knowledge check
Un succès au QCM du site délivre-t-il le titre CSSLP ?
Explained answer: Non, il s’agit d’une évaluation pédagogique indépendante — Le fournisseur officiel définit son processus et ses conditions.
Related lesson: 08. Cycle sécurisé et préparation aux certifications ↗
Reference documentation ↗ https://www.isc2.org/certifications/csslp/csslp-certification-exam-outline