À propos de cette page
Dans Développement de packages Laravel, nous avons vu qu’il suffit de renseigner la sectionextra.laravel du composer.json pour que les service providers et les façades soient enregistrés automatiquement. Cette page en explique les coulisses, en décryptant au niveau du code source le fonctionnement de la classe Illuminate\Foundation\PackageManifest.
Cette page complète Développement de packages Laravel. Nous vous recommandons de lire d’abord l’utilisation de base de la découverte automatique.
Vue d’ensemble du mécanisme de découverte automatique
La classe PackageManifest
Le cœur de la découverte automatique est Illuminate\Foundation\PackageManifest. Voici (en résumé) l’implémentation dans le framework en version 13.x.
- Le manifeste est mis en cache en mémoire dès sa première lecture (propriété
$this->manifest). Même siproviders()est appelée plusieurs fois dans la même requête, l’accès disque n’a lieu qu’une seule fois. build()n’est exécuté que si le fichier manifeste n’existe pas. En fonctionnement normal, il n’est pas reconstruit à chaque requête.- Le fichier lui-même n’est qu’un simple tableau PHP retourné via
return(bootstrap/cache/packages.php), la forme la plus rapide qui peut être chargée par un simplerequire.
Processus de construction du manifeste
C’est la méthodebuild() qui agrège les informations issues des composer.json.
composer.json : il consulte vendor/composer/installed.json. Ce fichier est généré par Composer lors de composer install / composer update et regroupe les métadonnées de tous les packages installés. Les sections extra déclarées dans le composer.json de chaque package y sont agrégées, si bien que Laravel ne se fie qu’aux informations sous la responsabilité de Composer.
Comme
installed.json est en général ignoré par Git (.gitignore), la découverte automatique ne fonctionne pas lors du tout premier setup (quand vendor/ n’existe pas). Le cache n’est construit qu’après un premier composer install.Deux manières d’utiliser dont-discover
dont-discover peut être déclaré dans le composer.json du package ou dans celui de l’application, mais avec des significations différentes.
composer.json côté package
composer.json côté application
build(), on voit que le tableau $ignore est alimenté en cumulant, via array_merge, la valeur configuration['dont-discover'] de chaque package. Un package peut donc techniquement « désactiver la découverte automatique de ses propres dépendances » (par exemple pour éviter la double inscription d’un provider de sous-package qu’il utilise en interne). En pratique, la désactivation est cependant surtout utilisée côté application.
Utiliser * dans dont-discover
Si le tableau retourné par packagesToIgnore() contient *, $ignoreAll vaut true et la découverte automatique est complètement désactivée pour tous les packages. C’est utile en CI ou en environnement de test pour éviter le coût de la découverte, ou lorsque vous souhaitez tout gérer manuellement dans bootstrap/providers.php.
Le fichier de cache
getCachedPackagesPath() renvoie la valeur de la variable d’environnement APP_PACKAGES_CACHE si elle est définie, sinon bootstrap/cache/packages.php.
php artisan package:discover vous permet de vérifier « quels packages ont été détectés ».
Quand le cache est-il reconstruit ?
Illuminate\Foundation\ComposerScripts s’abonne à trois événements Composer et appelle à chaque fois la même méthode clearCompiled().
composer install / composer update / composer dump-autoload), les caches de configuration, de services et de packages sont supprimés d’un seul coup. Au démarrage suivant de Laravel, PackageManifest::build() s’exécute et reconstruit tout à partir de la dernière version d’installed.json.
Ce comportement standard des projets Laravel est déclaré dans les scripts du composer.json.
composer.json d'une application Laravel (extrait)
Reconstruction manuelle via php artisan package:discover
Si le cache est resté obsolète, ou si vendor/ a été modifié sans passer par Composer, vous pouvez reconstruire manuellement le manifeste via la commande package:discover.
$manifest->build() puis à afficher le résultat : il n’y a quasiment aucune logique propre. Dans un pipeline CI qui utilise composer install --no-scripts par exemple, les événements Composer ne sont pas déclenchés et il faut donc appeler explicitement cette commande.
Points d’attention lors du déploiement
En production, on exécute courammentcomposer install --no-dev --optimize-autoloader. Mais avec l’option --no-scripts, le cache des packages n’est pas mis à jour. Il est donc plus sûr d’ajouter une étape explicite de reconstruction à votre script de déploiement.
package:discover avant config:cache. Si le cache de configuration est généré en premier, la configuration ajoutée ultérieurement par les packages (par exemple via mergeConfigFrom) risque de ne pas être prise en compte.
Récapitulatif
- La découverte automatique s’appuie non pas sur le contenu statique du
composer.json, mais survendor/composer/installed.jsongénéré par Composer. - Le résultat est mis en cache dans
bootstrap/cache/packages.phpsous forme de tableau PHP brut, lisible très rapidement via un simplerequire. - Ce cache est automatiquement supprimé lors des événements
composer install/update/dump-autoload, puis reconstruit au prochain démarrage. - Dans les environnements qui appellent Composer avec
--no-scripts, il faut invoquerphp artisan package:discoverexplicitement. - En plaçant
*dansdont-discover, vous désactivez entièrement la découverte automatique.
Pages associées
- Développement de packages Laravel — utilisation de base des service providers et de
extra.laravel. - Deferred service providers — optimiser le moment de chargement des providers détectés.
- Tester un package avec Orchestra Testbench — la commande
package:discoverspécifique à Testbench.