Passer au contenu principal
Pour fournir un backend de cache propre depuis un package, séparez le Store qui implémente le stockage du Repository qui expose l’API de Laravel. Ce que vous enregistrez avec Cache::extend() est une méthode de création : Laravel n’enveloppe pas automatiquement le Store retourné dans un Repository. Cette page a été vérifiée sur l’implémentation de Laravel Framework v13.39.0. Pour les opérations de base, consultez Cache, et pour préparer le provider, consultez Développement de packages.

Distinguer le nom du driver du nom du store

CacheManager::build() appelle la création personnalisée correspondant au driver de la configuration, si elle existe, avant les drivers intégrés. Ne réutilisez pas des noms comme redis ou array : utilisez un nom propre au package.

Enregistrer avant le boot des autres providers

Si un autre package utilise le cache dans son boot(), enregistrer votre driver dans votre propre boot() peut arriver trop tard selon l’ordre de chargement. Conformément à la documentation officielle, enregistrez dans register() un callback booting de l’application. L’exemple minimal suivant utilise l’ArrayStore standard afin de vérifier le chemin d’enregistrement sans service externe. Placez-le dans src/MemoServiceProvider.php du package Acme\Memo.
Chargez le provider, par exemple via la découverte automatique des packages. Ce provider, qui se contente d’enregistrer une extension, ne doit pas être différé : il doit être chargé aussi bien en HTTP qu’avec Artisan.
  • Le booting de l’application s’exécute, lors d’un démarrage normal, après la fin du register() de chaque provider et avant chaque boot(). Il est distinct du $this->booting() propre au provider.
  • Le callback de extend() reçoit l’application et le tableau de configuration du store. Placez la création du Store et les connexions externes dans ce callback de création.
  • Ce moment d’enregistrement ne garantit pas l’utilisation du cache dans le register() d’autres providers. Séparez l’enregistrement des services de leur utilisation effective.
Si vous avez seulement besoin du cache en mémoire standard, le driver array suffit. Cet exemple sert à expliquer l’enregistrement et les contrats ; il ne permet ni le partage entre processus ni la persistance. Pour un backend propre, remplacez la création de l’ArrayStore par une classe qui implémente Illuminate\Contracts\Cache\Store.
CacheManager::extend() relie à nouveau la portée des closures anonymes au Manager. Ne considérez pas que $this dans une closure non statique désigne le provider : utilisez, comme dans l’exemple, une closure statique et l’argument $app.

Ajouter le store dans l’application consommatrice

Ajoutez-le au tableau stores existant de config/cache.php. Ne concevez pas le package pour réécrire le default ou l’ensemble des stores de l’application consommatrice.
Pour en faire le store par défaut, indiquez dans CACHE_STORE le nom du store catalog, et non le nom du driver. Si vous créez plusieurs configurations avec le même driver, un Repository distinct est généré pour chaque nom de store.

Construire le Repository soi-même

CacheManager::callCustomCreator() retourne tel quel le résultat du callback. Si vous ne retournez que le Store, get() ou put() peuvent fonctionner, mais les méthodes du Repository comme remember() ne seront pas disponibles et l’interprétation du TTL changera également. Dans l’exemple, repository($store, $config) se charge de :
  • envelopper le Store dans un Illuminate\Cache\Repository ;
  • transmettre au Repository le nom store que le Manager a ajouté à la configuration ;
  • définir le Dispatcher sur le Repository si un Dispatcher d’événements est enregistré et que le paramètre events n’est pas désactivé.
Ce traitement n’applique pas toute votre configuration personnalisée au Store. La destination de connexion, le préfixe, la sérialisation, etc. doivent être pris en compte par l’implémentation du Store et par le code de création. C’est aussi le cas de serialize, lu depuis la configuration dans l’exemple.

Confronter le contrat Store à l’implémentation actuelle

Implémentez les méthodes exigées par Illuminate\Contracts\Cache\Store dans Laravel 13. Ne distribuez pas tels quels, comme un driver fonctionnel, des listes de méthodes vides issues d’anciens exemples. null est traité comme un défaut de cache par le Repository. Faites en sorte que false ou 0 ne soient pas considérés comme absents. Stocker null ne sert pas non plus de marqueur empêchant le recalcul dans remember().

Vérifier le TTL via le Repository

Repository::put() traite le TTL avant de le transmettre au Store.
L’ArrayStore de l’exemple stocke sans expiration lorsqu’on passe directement 0 seconde à son put(). En revanche, put(..., 0) sur le Repository supprime l’élément. Tester uniquement le Store de bas niveau obtenu avec getStore() ne permet pas de vérifier le comportement de l’API appelée par les utilisateurs.

Vérifier séparément la prise en charge de add, des verrous et des tags

Implémenter le contrat Store ne garantit pas la prise en charge de toutes les fonctionnalités du cache.
  • add() atomique : il ne fait pas partie du contrat Store. Lorsqu’il est appelé avec un TTL positif et que le Store possède add(), le Repository l’utilise. Sinon, il se rabat sur get() et put() : envelopper le Store dans un Repository ne suffit donc pas à garantir l’atomicité. Sur un backend partagé, implémentez-le avec une opération atomique du backend et vérifiez aussi l’exécution concurrente.
  • Verrous : lock() et restoreLock() de Illuminate\Contracts\Cache\LockProvider relèvent d’un contrat distinct. Vérifiez l’acquisition, la correspondance du propriétaire, l’expiration et le fait de ne pas libérer le verrou d’un autre propriétaire. Les verrous de l’ArrayStore de l’exemple n’assurent pas d’exclusion mutuelle entre processus distincts.
  • Tags : le Repository vérifie si le Store possède tags(). Une implémentation reposant généralement sur TaggableStore ou équivalent est nécessaire ; le contrat Store seul ne les active pas automatiquement.
Ne supposez pas que flush() est une suppression partielle respectant le préfixe configuré. Vérifiez la portée de la suppression sur un backend partagé et expliquez-la aux utilisateurs, y compris pour php artisan cache:clear.

Distinguer la conservation des instances de la suppression des données

CacheManager est un singleton, et il conserve aussi le résultat de store('catalog') pour chaque nom de store. Modifier la configuration ou réenregistrer extend() ne met pas à jour un Repository déjà généré.
$same est le même objet que $before. C’est lors de la récupération après forgetDriver() que $after est généré à partir de la nouvelle configuration. forgetDriver() ne vide pas le backend. Toutefois, comme l’ArrayStore de l’exemple conserve ses données dans l’instance, les anciennes données ne sont pas visibles depuis le nouveau Store. Par ailleurs, les références à l’ancien Repository détenues par $before côté consommateur ou par le singleton cache.store déjà résolu ne sont pas remplacées. En production, mettez à jour le cache de configuration selon votre déploiement habituel et redémarrez les workers de longue durée. Tenez aussi compte du cycle de vie des services et ne considérez pas une nouvelle résolution comme une mise à jour simultanée de tous les processus.

Tester le chemin de création, pas seulement des mocks de façade

Dans l’environnement de test du package, passez par le véritable Manager et le véritable Repository. L’exemple Testbench suivant vérifie que le driver est utilisable même dans le boot() d’un autre provider enregistré avant. Il stocke un tableau sans modifier la restriction de désérialisation cache.serializable_classes, et vérifie aussi la valeur brute stockée avec ArrayStore::all(false).
Pour un backend propre, vérifiez en plus l’expiration, les clés absentes de many(), l’aller-retour de false et 0, touch(), les pannes de connexion, les mises à jour concurrentes, ainsi que le préfixe et la portée des suppressions. Un simple succès de Cache::shouldReceive('get') ne prouve ni l’enregistrement du driver ni la compatibilité avec un service externe.

Liste de contrôle pour la maintenance à long terme

  • Faire du nom du driver, de la configuration du store, du format de sérialisation, des exceptions et des valeurs de retour en cas d’échec une spécification publique.
  • Vérifier, pour chaque version de Laravel prise en charge, les méthodes du contrat Store et les fonctionnalités optionnelles comme les tags et les verrous.
  • Vérifier que l’enregistrement fonctionne lors d’un nouveau démarrage avec cache de configuration, et que le driver est utilisable depuis le boot() d’autres providers.
  • Annoncer les changements de compatibilité des données stockées, y compris la stratégie de migration ou de suppression des clés existantes.
  • Distinguer la conservation des instances dans le processus, les données du backend et le cache de configuration.

Sources primaires consultées

Dernière modification le 29 octobre 2026