Une fonction de mémoïsation pour PHP

Publié le 01/06/2024 • Actualisé le 01/06/2024


English language detected! 🇬🇧

  We noticed that your browser is using English. Do you want to read this post in this language?

Read the english version 🇬🇧 Close

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

  Travaillez avec moi !