From d42cb805c9c27e4d54cdc9c6c4f85da6bbacc538 Mon Sep 17 00:00:00 2001 From: Sylvain Fabre Date: Sun, 23 Aug 2026 22:07:01 +0200 Subject: [PATCH] feat: memoize the hydrated PublicSuffixList in-process The PSR-16 cache avoids re-downloading the Public Suffix List, but every PublicSuffixListClient::get() call still unserializes the full Rules object from the cache backend. Processes validating many email addresses pay that cost on every validation. Pdp\Storage\PublicSuffixListClient is now aliased to an in-memory memoizing decorator around RulesStorage, so the list is hydrated once per process. Co-Authored-By: Claude Fable 5 --- config/services.yaml | 6 +++- src/Cache/MemoizingPublicSuffixListClient.php | 31 ++++++++++++++++++ .../MemoizingPublicSuffixListClientTest.php | 32 +++++++++++++++++++ 3 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 src/Cache/MemoizingPublicSuffixListClient.php create mode 100644 tests/Cache/MemoizingPublicSuffixListClientTest.php diff --git a/config/services.yaml b/config/services.yaml index 0187e61..74573b4 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -22,7 +22,11 @@ services: $namespace: 'jeremykendall_php-domain-parser' $directory: '%kernel.cache_dir%' - Pdp\Storage\PublicSuffixListClient: '@Pdp\Storage\RulesStorage' + Pdp\Storage\PublicSuffixListClient: '@AssoConnect\ValidatorBundle\Cache\MemoizingPublicSuffixListClient' + + AssoConnect\ValidatorBundle\Cache\MemoizingPublicSuffixListClient: + arguments: + $decorated: '@Pdp\Storage\RulesStorage' Pdp\Storage\RulesStorage: arguments: diff --git a/src/Cache/MemoizingPublicSuffixListClient.php b/src/Cache/MemoizingPublicSuffixListClient.php new file mode 100644 index 0000000..3dc7d20 --- /dev/null +++ b/src/Cache/MemoizingPublicSuffixListClient.php @@ -0,0 +1,31 @@ + */ + private array $cache = []; + + public function __construct(private readonly PublicSuffixListClient $decorated) + { + } + + public function get(string $uri): PublicSuffixList + { + if (!isset($this->cache[$uri])) { + $this->cache[$uri] = $this->decorated->get($uri); + } + + return $this->cache[$uri]; + } +} diff --git a/tests/Cache/MemoizingPublicSuffixListClientTest.php b/tests/Cache/MemoizingPublicSuffixListClientTest.php new file mode 100644 index 0000000..7c064c6 --- /dev/null +++ b/tests/Cache/MemoizingPublicSuffixListClientTest.php @@ -0,0 +1,32 @@ +createMock(PublicSuffixListClient::class); + $decorated->expects(self::once()) + ->method('get') + ->with('https://example.com/public_suffix_list.dat') + ->willReturn($publicSuffixList); + + $memoizingPublicSuffixListClient = new MemoizingPublicSuffixListClient($decorated); + + $firstResult = $memoizingPublicSuffixListClient->get('https://example.com/public_suffix_list.dat'); + $secondResult = $memoizingPublicSuffixListClient->get('https://example.com/public_suffix_list.dat'); + + self::assertSame($publicSuffixList, $firstResult); + self::assertSame($publicSuffixList, $secondResult); + } +}