A memoization function for PHP

Published on 2024-06-01 • Modified on 2024-06-01

This snippet shows how to memoize a pure function result on an object. The code comes from this snippet, thanks to the author, calebporzio. I have slightly modified it and provided a complete working example with time execution traces to show that is works correclty. Check out the links below for more explanations.



namespace App\Controller\Snippet;

use App\Dto\DummyMemoizable;
use Symfony\Component\Stopwatch\Stopwatch;

 * I am using a PHP trait to isolate each snippet in a file.
 * This code should be called from a Symfony controller extending AbstractController (as of Symfony 4.2)
 * or Symfony\Bundle\FrameworkBundle\Controller\Controller (Symfony <= 4.1).
 * Services are injected in the main controller constructor.
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';
        $res1 = $obj1->compute('foo', 1);
        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';
        $res2 = $memo->compute('foo', 1);
        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';
        $res3 = $memo->compute('foo', 1);
        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';
        $res4 = $memo->compute('bar', 2);
        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);

 Run this snippet  More on Stackoverflow   Read the doc  More on the web  Random snippet

  Work with me!