rikudou / memoize-bundle
为您的 Symfony 服务提供自动记忆功能
Requires
- php: ^8.1
- symfony/framework-bundle: ^5.4|^6.0|^7.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.5
- jetbrains/phpstorm-attributes: ^1.0
- phpstan/phpstan: ^1.4
This package is auto-updated.
Last update: 2024-09-07 12:05:43 UTC
README
此包为您的服务提供记忆功能 - 每次使用相同的参数调用相同的方法时,将返回缓存的响应而不是执行方法。
安装
需要 php 8.1+。
composer require rikudou/memoize-bundle
之后,将以下内容添加到您的 composer.json
的 PSR-4 自动加载部分:"App\\Memoized\\": "memoized/"
在新的 Symfony 项目中的示例
{ "autoload": { "psr-4": { "App\\": "src/", "App\\Memoized\\": "memoized/" // add this line } } }
用法
您可以直接使用提供的属性。
#[Memoizable]
- 每个具有记忆方法的类都必须具有此属性。#[Memoize]
- 一个可以用于类或方法以标记给定的类/方法为记忆化的属性。有一个可选的参数,表示缓存有效性的秒数。默认为-1
,表示直到进程结束(在标准 php-fpm 和 apache2 配置中为请求结束)#[NoMemoize]
- 一个允许您在已记忆化整个类的情况下标记方法为非记忆化的属性。
每个记忆化类都需要实现至少一个您用于类型提示给定服务的接口。为具有 #[Memoizable]
属性的每个服务创建一个代理类。此代理类装饰了您的服务,以便每次请求您的服务时都会注入代理。
对于非记忆化方法,代理类简单地将参数传递给您的服务;而对于记忆化方法,它还会根据参数创建一个缓存键,并在缓存中查找结果。
代理类是在编译器遍历过程中生成的,这意味着一旦容器已导出,代理创建就不会增加开销。
示例
具有一个记忆化方法的类
<?php use Rikudou\MemoizeBundle\Attribute\Memoizable; use Rikudou\MemoizeBundle\Attribute\Memoize; interface CalculatorInterface { public function add(int $a, int $b): int; public function sub(int $a, int $b): int; } #[Memoizable] final class Calculator implements CalculatorInterface { #[Memoize] public function add(int $a, int $b): int { return $a + $b; } public function sub(int $a, int $b): int { return $a - $b; } }
此类将简单地记忆化 add()
方法,而 sub()
将不会被记忆化。
整个类记忆化
<?php use Rikudou\MemoizeBundle\Attribute\Memoizable; use Rikudou\MemoizeBundle\Attribute\Memoize; #[Memoizable] #[Memoize] final class Calculator implements CalculatorInterface { public function add(int $a, int $b): int { return $a + $b; } public function sub(int $a, int $b): int { return $a - $b; } }
在这里,add()
和 sub()
方法都将被记忆化。
整个类记忆化,但有一个方法除外
<?php use Rikudou\MemoizeBundle\Attribute\Memoizable; use Rikudou\MemoizeBundle\Attribute\Memoize; use Rikudou\MemoizeBundle\Attribute\NoMemoize; #[Memoizable] #[Memoize] final class Calculator implements CalculatorInterface { public function add(int $a, int $b): int { return $a + $b; } #[NoMemoize] public function sub(int $a, int $b): int { return $a - $b; } }
在这里,整个类将被记忆化,除了 sub()
方法,因为它具有 #[NoMemoize]
属性。
自定义记忆化有效性
<?php use Rikudou\MemoizeBundle\Attribute\Memoizable; use Rikudou\MemoizeBundle\Attribute\Memoize; #[Memoizable] #[Memoize(seconds: 30)] final class Calculator implements CalculatorInterface { public function add(int $a, int $b): int { return $a + $b; } public function sub(int $a, int $b): int { return $a - $b; } }
在这里,整个类将被记忆化,缓存有效性设置为 30 秒。
配置
提示:使用此命令创建默认配置文件:
php bin/console config:dump rikudou_memoize > config/packages/rikudou_memoize.yaml
有以下配置
enabled
default_memoize_seconds
cache_service
key_specifier_service
enabled
简单的布尔值,用于启用或禁用记忆化。
default_memoize_seconds
此参数设置记忆化缓存的默认生存时间(以秒为单位)。默认为 -1
,表示直到进程结束(在标准 php-fpm 或 apache2 配置中为请求结束)
注意,当使用默认值 -1
时,会忽略 cache_service
配置。
cache_service
此参数控制用于记忆化的缓存服务。默认为 cache.app
,表示您的应用程序的默认缓存。
如果将 default_memoize_seconds
设置为 -1
,则忽略此配置并使用默认的内存实现(服务 rikudou.memoize.internal_cache
,类 InMemoryCachePool
)。
key_specifier_service
此参数允许您指定一个将修改缓存键的服务。如果您的应用程序依赖于某种全局状态(如当前认证用户等),这将非常有用。它必须实现 CacheKeySpecifier 接口。
默认配置
由 php bin/console config:dump rikudou_memoize
生成
# Default configuration for extension with alias: "rikudou_memoize" rikudou_memoize: # Whether memoization is enabled or not. enabled: true # The default memoization period if none is specified in attribute. -1 means until end of request. default_memoize_seconds: -1 # The default cache service to use. If default_memoize_seconds is set to -1 this setting is ignored and internal service is used. cache_service: cache.app # The service to use to alter the cache key. Useful if you need to alter the cache key based on some global state. key_specifier_service: rikudou.memoize.key_specifier.null
示例代理类
第一个代码块是原始类,第二个代码块是代理。
<?php namespace App\Service; use Rikudou\MemoizeBundle\Attribute\Memoizable; use Rikudou\MemoizeBundle\Attribute\Memoize; use Rikudou\MemoizeBundle\Attribute\NoMemoize; use RuntimeException; #[Memoizable] #[Memoize(seconds: 10)] class Calculator implements CalculatorInterface { public function add(int $a, int $b): int { return $a + $b; } #[Memoize(seconds: -1)] public function sub(int $a, int $b): int { return $a - $b; } #[NoMemoize] public function mul(int $a, int $b): int { return $a * $b; } public function someVoidMethod(): void { } public function throwException(): never { throw new RuntimeException(); } }
<?php namespace App\Memoized; final class Calculator_Proxy_422705a42881a5490e7996fe93245734 implements \App\Service\CalculatorInterface { public function __construct( private readonly \App\Service\Calculator $original, private readonly \Psr\Cache\CacheItemPoolInterface $cache, private readonly \Rikudou\MemoizeBundle\Cache\InMemoryCachePool $internalCache, private readonly \Rikudou\MemoizeBundle\Cache\KeySpecifier\CacheKeySpecifier $cacheKeySpecifier, ) {} public function add(int $a, int $b): int { $cacheKey = ''; $cacheKey .= serialize($a); $cacheKey .= serialize($b); $cacheKey .= $this->cacheKeySpecifier->generate(); $cacheKey = hash('sha512', $cacheKey); $cacheKey = "rikudou_memoize_AppServiceCalculator_add_{$cacheKey}"; $cacheItem = $this->cache->getItem($cacheKey); if ($cacheItem->isHit()) { return $cacheItem->get(); } $cacheItem->set($this->original->add($a, $b)); $cacheItem->expiresAfter(10); $this->cache->save($cacheItem); return $cacheItem->get(); } public function sub(int $a, int $b): int { $cacheKey = ''; $cacheKey .= serialize($a); $cacheKey .= serialize($b); $cacheKey .= $this->cacheKeySpecifier->generate(); $cacheKey = hash('sha512', $cacheKey); $cacheKey = "rikudou_memoize_AppServiceCalculator_sub_{$cacheKey}"; $cacheItem = $this->internalCache->getItem($cacheKey); if ($cacheItem->isHit()) { return $cacheItem->get(); } $cacheItem->set($this->original->sub($a, $b)); $cacheItem->expiresAfter(0); $this->internalCache->save($cacheItem); return $cacheItem->get(); } public function mul(int $a, int $b): int { return $this->original->mul($a, $b); } public function someVoidMethod(): void { $cacheKey = ''; $cacheKey .= $this->cacheKeySpecifier->generate(); $cacheKey = hash('sha512', $cacheKey); $cacheKey = "rikudou_memoize_AppServiceCalculator_someVoidMethod_{$cacheKey}"; $cacheItem = $this->cache->getItem($cacheKey); if ($cacheItem->isHit()) { return; } $cacheItem->set($this->original->someVoidMethod()); $cacheItem->expiresAfter(10); $this->cache->save($cacheItem); } public function throwException(): never { $this->original->throwException(); } }