From 4c1c51f196d11cc3c67043dacb79bf10c7c99c10 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Sat, 30 May 2026 04:23:44 +0900 Subject: [PATCH] Deduplicate surrogate keys when concatenating with manually-set header setSurrogateHeader() applied array_unique() only to the internally generated keys. When the ResourceObject already had a manually-set Surrogate-Key header, the keys were simply concatenated, leaving duplicates between the manual and internal sets. This happens when a parent and its embedded child share a common tag: the child's tag is merged into the internal collection and then concatenated with the parent's manual header, producing duplicates. Build the full key set first and apply array_unique() once, so manual and internal keys are deduplicated consistently. Duplicates are harmless to purging but bloat the header, risking purge misses on CDNs that cap header length. Also add /.phpunit.cache/ to .gitignore (PHPUnit's cacheDirectory), which did not match the stale /.phpunit-cache/ entry. Fixes #175 --- .gitignore | 1 + src/SurrogateKeys.php | 11 ++++------- tests/SurrogateKeysTest.php | 19 +++++++++++++++++++ 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index 7cae452b..a5a487f7 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,5 @@ /vendor-bin/**/vendor /.php_cs.cache /.phpunit-cache/ +/.phpunit.cache/ /.dump.rdb diff --git a/src/SurrogateKeys.php b/src/SurrogateKeys.php index 073120a2..f9ff9a42 100644 --- a/src/SurrogateKeys.php +++ b/src/SurrogateKeys.php @@ -38,14 +38,11 @@ public function addTag(ResourceObject $ro): void public function setSurrogateHeader(ResourceObject $ro): void { - $key = implode(' ', array_unique($this->surrogateKeys)); - $wasSetManually = isset($ro->headers[Header::SURROGATE_KEY]); - if ($wasSetManually) { - $ro->headers[Header::SURROGATE_KEY] .= ' ' . $key; - - return; + $keys = $this->surrogateKeys; + if (isset($ro->headers[Header::SURROGATE_KEY])) { + $keys = array_merge(explode(' ', $ro->headers[Header::SURROGATE_KEY]), $keys); } - $ro->headers[Header::SURROGATE_KEY] = $key; + $ro->headers[Header::SURROGATE_KEY] = implode(' ', array_unique($keys)); } } diff --git a/tests/SurrogateKeysTest.php b/tests/SurrogateKeysTest.php index 7206373d..3ca95bda 100644 --- a/tests/SurrogateKeysTest.php +++ b/tests/SurrogateKeysTest.php @@ -73,4 +73,23 @@ public function testOnePurgeKey(): void $etags->setSurrogateHeader($foo); $this->assertSame('_foo_', $foo->headers[Header::SURROGATE_KEY]); } + + public function testSetSurrogateHeaderDeduplicatesWithManualKeys(): void + { + $uri = new Uri('app://self/foo'); + $etags = new SurrogateKeys($uri); + $child = new class extends ResourceObject{ + /** @var array */ + public $headers = [Header::SURROGATE_KEY => 'shared-tag']; // phpcs:ignore + }; + $child->uri = new Uri('app://self/foo1'); + $etags->addTag($child); + $ro = new class extends ResourceObject{ + /** @var array */ + public $headers = [Header::SURROGATE_KEY => 'shared-tag _foo_']; // phpcs:ignore + }; + $ro->uri = $uri; + $etags->setSurrogateHeader($ro); + $this->assertSame('shared-tag _foo_ _foo1_', $ro->headers[Header::SURROGATE_KEY]); + } }