rikudou/memoize-bundle

为您的 Symfony 服务提供自动记忆功能

资助包维护!
Ko Fi
Liberapay

安装: 968

依赖项: 0

建议者: 0

安全: 0

星级: 20

观察者: 2

分支: 2

开放性问题: 2

类型:symfony-bundle

v1.1.0 2024-02-07 10:41 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();
	}

}