[ Article ]
DevOps Experience Conf' 2026 : 6 enseignements clés sur la maîtrise des infrastructures

Marie-Pierre Goetz

Stéphane Meaudre
Publié le 3 mars 2021
Parmi tous ses différents composants, Symfony en propose un qui peut se révéler très pratique pour gérer et utiliser différents systèmes de cache : le cache pool. Pour rappel, le but du cache est de permettre l’accélération d’exécution de nos pages et ainsi améliorer grandement les performances de notre site ou de notre application. Le composant se base sur des classes Adapters, implémentant l’interface AdapterInterface, et pouvant être prêt à l’emploi pour interagir avec Redis, Memcached et plein d’autres systèmes de cache.
L’installation du composant s’effectue via composer :
composer require symfony/cache
Son fonctionnement est basé sur deux implémentations des règles PSR (PHP Standards Recommendations) :
Dans le cas d’une utilisation de Redis ou Memcached, certaines librairies PHP supplémentaires sont à installer.
Exemple avec Memcached :
sudo apt-get install memcached sudo apt-get install -y php7.x-memcached #php7.x correspond à la version de PHP voulu
L’utilisation d’un Adapter est possible directement, sans forcément passer par un service. Symfony proposant plusieurs Adapters, chacun possédant diverses méthodes permettant de manipuler le cache. Certains sont destinés à des besoins bien précis.
Exemple avec le FileSystemAdapter :
use Symfony\Component\Cache\Adapter\FilesystemAdapter; // Set CacheAdapter $cachePool = new FilesystemAdapter('', 0, "cache"); // Check if item exist if ($cachePool->hasItem('key_item')) { // Récupération $item = $cachePool->getItem('key_item'); if ($item->isHit()) { echo $item->get(); } } //Save item or create new one $item = $cachePool->getItem('key_item'); if (!item->isHit()) { // Sauvegarde $item->set('new_value'); $cachePool->save($item); } // Suppression d'un item et vérification si item existe ou non $cachePool->deleteItem('key_item'); if (!$cachePool->hasItem('key_item')) { echo 'Item deleted'; } // Suppression de tous les items du cache $cachePool->clear();
À partir de là, nous pouvons développer notre propre service dans lequel nous injectons l’Adapter souhaité ainsi qu’une interface contenant diverses méthodes permettant de manipuler très facilement notre Cache Adapter. Néanmoins, un peu de configuration est nécessaire avant de le coder. Nous allons illustrer ceci avec un cas d’utilisation concret : développons un service qui utilise l’Adapter de Memcached.
Dans le fichier config\packages\framework.yaml, il faut définir l’Adapter qui sera utilisé :
framework: cache: prefix_seed: <mypool> default_memcached_provider: '%env(MEMCACHED_URL)%' # specific memcached pools: app.cache.mypool: adapter: cache.adapter.memcached public: false default_lifetime: 31536000
Nous allons maintenant créer la classe dans laquelle nous injecterons app.cache.mypool (le terme “mypool” est à changer selon votre besoin) définie juste avant. Notre classe va implémenter une interface nommée CachePoolInterface dans laquelle nous allons déclarer la liste des différentes méthodes que nous voulons utiliser : ce qui est très utile si nous avons plusieurs services avec chacun un Adapter qui lui est propre.
Déclarons notre service dans le services.yaml :
services:
// ...
# Cache pool
App\Services\Cache\MemcachedService:
arguments:
$cachePool: '@app.cache.mypool'
L’ossature de notre service :
namespace App\Services\Cache; use Symfony\Contracts\Cache\CacheInterface; final class MemcachedService { private CachePoolInterface $cachePool; /** * CachepoolService constructor. * * @param CachePoolInterface $cachePool */ public function __construct(CachePoolInterface $cachePool) { $this->cachePool = $cachePool; } }
Créons maintenant l’interface dans laquelle nous ajouterons 5 méthodes :
interface CachePoolInterface { /** * @param $key * * @return string|null */ public function getItem(string $key): ?string; /** * @param $key * @param $value * * @return bool */ public function saveItem(string $key, $value): bool; /** * @param $key * * @return bool */ public function hasItem(string $key): bool; /** * @param $key * * @return bool */ public function deleteItem(string $key): bool; /** * @return bool */ public function deleteAll(): bool; }
Implémentons l’interface à notre service et générons les méthodes :
final class CachepoolService implements CachePoolInterface { private CachePoolInterface $cachePool; /** * CachepoolService constructor. * @param CachePoolInterface $cachePool */ public function __construct(CachePoolInterface $cachePool) { $this->cachePool = $cachePool; } /** * @param $key * @return bool|mixed */ public function getItem(string $key): ?string { $cacheItem = $this->cachePool->getItem($key); return ($cacheItem->isHit()) ? $cacheItem->get(): null; } /** * @param $key * @param $value * @return bool */ public function saveItem(string $key, $value): bool { $cacheItem = $this->cachePool->getItem($key); $cacheItem->set($value); return $this->cachePool->save($cacheItem); } /** * @param $key * @return bool */ public function hasItem(string $key): bool { return $this->cachePool->hasItem($key); } /** * @param $key * @return bool */ public function deleteItem(string $key): bool { return $this->cachePool->deleteItem($key); } /** * @return bool */ public function deleteAll(): bool { return $this->cachePool->clear(); } }
Notre service étant maintenant opérationnel, nous pouvons donc l’utiliser. Dans l’exemple ci-dessous, nous souhaitons aller chercher un item dans le cache si ce dernier existe.
/* * @Route("/my-route/$id", name="my_route") * * @param string $id * @param CachePoolInterface $cachePool * * @return Response */ public function show(string $id, CachePoolInterface $cachePool): Response { $keyItem = md5($id); if ($cachePool->hasItem($keyitem)) { $item = $cachePool->getItem($keyitem); // If we store Object, we need to deserialize it $itemEntity = unserialize($item); } //... $response = new Response(); //... return $this->render('template.html.twig', ['entity' => $itemEntity], $response); }
Autre exemple pour sauvegarder les données :
$id = 1; /** @var Foo $foo */ $foo = new Foo(); $foo->setId($id); $foo->setBar('toto'); // Add data $cachePool->saveItem(md5($id), serialize($foo)); // ... // Retrieve class Foo stored in Cache Pool $serializedFoo = $cachePool->getItem(md5($id)); $foo = unserialize(serializedFoo, ['allowed_classes' => [Foo:class]]);
Quelques commandes Symfony utile pour manipuler notre cache :
bin/console cache:pool:clear app.cache.mypoolbin/console cache:pool:delete app.cache.mypool <key>bin/console cache:pool:pruneL’utilisation du cache pool et des Adapters n’est pas obscure et compliquée ; faire un service permettant de manipuler des données en cache est simple à mettre en place et permet de mieux comprendre le fonctionnement de ce composant ainsi que du cache plus généralement. Cela vous permettra d’obtenir des performances et des temps de chargement réduits pour l’affichage de vos pages.
Retour du Symfony Live Paris, édition 2020
[ Article ]

Marie-Pierre Goetz