From 909d9a308a65b780bab2b0269123db5104e0ad79 Mon Sep 17 00:00:00 2001 From: "hubert.lenoir" Date: Fri, 27 May 2022 16:00:39 +0200 Subject: [PATCH] [DependencyInjection] [ProxyManagerBridge] Add Memoize attribute --- .../AccessInterceptorValueHolderFactory.php | 9 +++ .../Attribute/Memoizable.php | 7 +++ .../DependencyInjection/Attribute/Memoize.php | 14 +++++ .../Compiler/MemoizePass.php | 60 +++++++++++++++++++ .../Compiler/PassConfig.php | 1 + .../MemoizeProxy/BaseKeyGenerator.php | 14 +++++ .../MemoizeProxy/Interceptor.php | 34 +++++++++++ .../MemoizeProxy/KeyGeneratorInterface.php | 11 ++++ .../MemoizeProxy/MemoizeFactory.php | 47 +++++++++++++++ 9 files changed, 197 insertions(+) create mode 100644 src/Symfony/Bridge/ProxyManager/AccessInterceptor/Instanciator/AccessInterceptorValueHolderFactory.php create mode 100644 src/Symfony/Component/DependencyInjection/Attribute/Memoizable.php create mode 100644 src/Symfony/Component/DependencyInjection/Attribute/Memoize.php create mode 100644 src/Symfony/Component/DependencyInjection/Compiler/MemoizePass.php create mode 100644 src/Symfony/Component/DependencyInjection/MemoizeProxy/BaseKeyGenerator.php create mode 100644 src/Symfony/Component/DependencyInjection/MemoizeProxy/Interceptor.php create mode 100644 src/Symfony/Component/DependencyInjection/MemoizeProxy/KeyGeneratorInterface.php create mode 100644 src/Symfony/Component/DependencyInjection/MemoizeProxy/MemoizeFactory.php diff --git a/src/Symfony/Bridge/ProxyManager/AccessInterceptor/Instanciator/AccessInterceptorValueHolderFactory.php b/src/Symfony/Bridge/ProxyManager/AccessInterceptor/Instanciator/AccessInterceptorValueHolderFactory.php new file mode 100644 index 0000000000000..0888be03c50ae --- /dev/null +++ b/src/Symfony/Bridge/ProxyManager/AccessInterceptor/Instanciator/AccessInterceptorValueHolderFactory.php @@ -0,0 +1,9 @@ +register('dependency_injection.memoize_proxy.factory', MemoizeFactory::class) + ->setArguments([$container->getParameter('kernel.cache_dir').'/memoize']); + $container->register('dependency_injection.memoize_proxy.key_generator.base', BaseKeyGenerator::class); + + foreach ($container->getDefinitions() as $id => $definition) { + if (!$definition->isAutoconfigured()) { + continue; + } + + // Is MemoizeClass + if (!$class = $container->getReflectionClass($definition->getClass())) { + continue; + } + if (!$class->getAttributes(Memoizable::class, \ReflectionAttribute::IS_INSTANCEOF)) { + continue; + } + + // Get Memoize methods + $methods = []; + foreach ($class->getMethods() as $method) { + if (!$memoize = $method->getAttributes(Memoize::class, \ReflectionAttribute::IS_INSTANCEOF)) { + continue; + } + $memoize = $memoize[0]->newInstance(); + + $methods[$method->getName()] = [ + new Reference($memoize->pool), + new Reference($memoize->keyGenerator ?: 'dependency_injection.memoize_proxy.key_generator.base'), + $memoize->ttl, + ]; + } + + if (!$methods) { + continue; + } + + // Create proxy + $proxy = $container->register($id.'.memoized', $definition->getClass()); + $proxy->setDecoratedService($id); + $proxy->setFactory(new Reference('dependency_injection.memoize_proxy.factory')); + $proxy->setArguments([new Reference('.inner'), $methods]); + } + } + +} diff --git a/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php b/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php index 3acbe26de0d3c..30fead4ae407e 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php @@ -47,6 +47,7 @@ public function __construct() new AttributeAutoconfigurationPass(), new ResolveInstanceofConditionalsPass(), new RegisterEnvVarProcessorsPass(), + new MemoizePass(), ], -1000 => [new ExtensionCompilerPass()], ]; diff --git a/src/Symfony/Component/DependencyInjection/MemoizeProxy/BaseKeyGenerator.php b/src/Symfony/Component/DependencyInjection/MemoizeProxy/BaseKeyGenerator.php new file mode 100644 index 0000000000000..30fcc5ef8ce54 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/MemoizeProxy/BaseKeyGenerator.php @@ -0,0 +1,14 @@ +item = $this->cache->getItem(($this->keyGenerator)(\get_class($instance), $method, $params)); + if ($this->item->isHit()) { + $returnEarly = true; + + return $this->item->get(); + } + } + + public function getSuffixInterceptor($proxy, $instance, $method, $params, $returnValue) { + $this->item->expiresAfter($this->ttl); + $this->item->set($returnValue); + $this->cache->save($this->item); + } +} diff --git a/src/Symfony/Component/DependencyInjection/MemoizeProxy/KeyGeneratorInterface.php b/src/Symfony/Component/DependencyInjection/MemoizeProxy/KeyGeneratorInterface.php new file mode 100644 index 0000000000000..5e7810ca2d3b3 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/MemoizeProxy/KeyGeneratorInterface.php @@ -0,0 +1,11 @@ +setGeneratorStrategy(new FileWriterGeneratorStrategy(new FileLocator($cacheDirectory))); + $config->setProxiesTargetDir($cacheDirectory); + + $this->factory = new AccessInterceptorValueHolderFactory($config); + } + + /** + * @param object $service Service to memoize + * @param array $methods + */ + public function __invoke(object $service, array $methods): AccessInterceptorValueHolderInterface + { + $proxy = $this->factory->createProxy($service); + foreach ($methods as $name => [$cache, $key, $ttl]) { + $interceptor = new Interceptor($cache, $key, $ttl); + $proxy->setMethodPrefixInterceptor($name, $interceptor->getPrefixInterceptor(...)); + $proxy->setMethodSuffixInterceptor($name, $interceptor->getSuffixInterceptor(...)); + } + + return $proxy; + } +}