Une fonction de mémoïsation pour PHP
Publié le 01/06/2024 • Actualisé le 01/06/2024
Dans ce bout de code, nous voyons comment mémoïser un résultats de fonction pure d'un objet avec PHP. Ce code provient de ce snippet, merci à l'auteur, calebporzio. Je l'ai légèrement modifié et fourni une démo complète avec les traces de temps d'exécution afin de s'assurer que tout fonctionne correctement.
<?php
declare(strict_types=1);
namespace App\Controller\Snippet;
use App\Dto\DummyMemoizable;
use Symfony\Component\Stopwatch\Stopwatch;
/**
* J'utilise un trait PHP afin d'isoler chaque snippet dans un fichier.
* Ce code doit être apellé d'un contrôleur Symfony étendant AbstractController (depuis Symfony 4.2)
* ou Symfony\Bundle\FrameworkBundle\Controller\Controller (Symfony <= 4.1).
* Les services sont injectés dans le constructeur du contrôleur principal.
*/
trait Snippet303Trait
{
public function snippet303(): void
{
$obj1 = new DummyMemoizable();
/** @var DummyMemoizable $memo */
$memo = $this->memoize($obj1);
/**
* The compute() function of the DummyMemoizable() object is:
*
* $str = $parameter1. $parameter2;
* usleep(10000); // = 10 milliseconds
*
* return $str;
*/
$timer = new Stopwatch();
// standard call without memoization
$event1 = 'std-call';
$timer->start($event1);
$res1 = $obj1->compute('foo', 1);
$timer->stop($event1);
echo \sprintf('Standard call: %s (%d ms)', $res1, $timer->getEvent($event1)->getDuration()).PHP_EOL;
// use memoization, the result is slow and stored
$event2 = 'memo-first-call';
$timer->start($event2);
$res2 = $memo->compute('foo', 1);
$timer->stop($event2);
echo \sprintf('Memo first call: %s (%d ms)', $res2, $timer->getEvent($event2)->getDuration()).PHP_EOL;
// with the same arguments, the result is taken from the cache
$event3 = 'memo-cached-call';
$timer->start($event3);
$res3 = $memo->compute('foo', 1);
$timer->stop($event3);
echo \sprintf('Memo cached call: %s (%d ms)', $res3, $timer->getEvent($event3)->getDuration()).PHP_EOL;
// with different arguments a new specific cache is stored
$event4 = 'memo-new-call';
$timer->start($event4);
$res4 = $memo->compute('bar', 2);
$timer->stop($event4);
echo \sprintf('Memo new call with other parameters: %s (%d ms)', $res4, $timer->getEvent($event4)->getDuration()).PHP_EOL;
// That's it! 😁
}
public function memoize(object $target): object
{
static $memo = new \WeakMap();
/** @phpstan-ignore-next-line */
return new class($target, $memo) {
public function __construct(
protected object $target,
protected \WeakMap $memo,
) {
}
public function __call(string $method, mixed $params): mixed
{
$this->memo[$this->target] ??= [];
$signature = $method.crc32(json_encode($params, JSON_THROW_ON_ERROR));
return $this->memo[$this->target][$signature]
??= $this->target->$method(...$params);
}
};
}
}
Exécuter le snippet Plus sur Stackoverflow Lire la doc Plus sur le web Snippet aléatoire