Si vous n'avez pas besoin de la sécurité de type vous pouvez utiliser un décorateur de cache générique :
class Cached
{
public function __construct($instance, $cacheDir = null)
{
$this->instance = $instance;
$this->cacheDir = $cacheDir === null ? sys_get_temp_dir() : $cacheDir;
}
public function defineCachingForMethod($method, $timeToLive)
{
$this->methods[$method] = $timeToLive;
}
public function __call($method, $args)
{
if ($this->hasActiveCacheForMethod($method, $args)) {
return $this->getCachedMethodCall($method, $args);
} else {
return $this->cacheAndReturnMethodCall($method, $args);
}
}
// … followed by private methods implementing the caching
Vous pouvez alors envelopper une instance qui a besoin d'être mise en cache dans ce décorateur comme ceci :
$cachedInstance = new Cached(new Instance);
$cachedInstance->defineCachingForMethod('foo', 3600);
De toute évidence, le $cachedInstance
n'a pas de foo()
méthode. L'astuce consiste ici à utiliser la magie __call
pour intercepter tous les appels à des méthodes inaccessibles ou inexistantes et les déléguer à l'instance décorée. De cette façon, nous exposons l'API publique entière de l'instance décorée à travers le décorateur.
Comme vous pouvez le voir, le __call
contient également le code permettant de vérifier si une mise en cache est définie pour cette méthode. Si c'est le cas, elle renvoie l'appel de la méthode mise en cache. Sinon, elle appellera l'instance et renverra le retour en cache.
Vous pouvez également transmettre un CacheBackend dédié au décorateur au lieu d'implémenter la mise en cache dans le décorateur lui-même. Le décorateur fonctionnerait alors uniquement comme un médiateur entre l'instance décorée et le backend.
El inconvénient de cette approche générique est que votre décorateur de cache n'aura pas le type de l'instance décorée. Lorsque votre code de consommation attendra des instances de type Instance, vous obtiendrez des erreurs.
Si vous avez besoin de décorateurs sûrs pour les types vous devez utiliser l'approche "classique" :
- Créer une interface de l'API publique de l'instance décorée. Vous pouvez le faire manuellement ou, si c'est beaucoup de travail, utiliser ma méthode de création d'une interface publique. Distillateur d'interface )
- Changez les TypeHints de chaque méthode attendant l'instance décorée pour l'Interface
- Faites en sorte que l'instance Decorated l'implémente.
- Demandez au décorateur de l'implémenter et de déléguer toutes les méthodes à l'instance décorée.
- Modifier toutes les méthodes qui nécessitent une mise en cache
- Répétez pour toutes les classes qui veulent utiliser le décorateur
En bref
class CachedInstance implements InstanceInterface
{
public function __construct($instance, $cachingBackend)
{
// assign to properties
}
public function foo()
{
// check cachingBackend whether we need to delegate call to $instance
}
}
El inconvénient c'est qu'il y a plus de travail. Vous devez le faire pour chaque classe censée utiliser la mise en cache. Vous devrez aussi mettre la vérification du backend du cache dans chaque fonction (duplication de code) et déléguer tous les appels qui ne nécessitent pas de cache à l'instance décorée (fastidieux et source d'erreurs).