From 5eee2f83b8adba966e14361bbbd8c146b5a54343 Mon Sep 17 00:00:00 2001 From: Blax Software Date: Tue, 7 Jul 2026 14:04:58 +0200 Subject: [PATCH] fix: ConnectionRegistry robust to numeric-string ids PHP coerces numeric-string array keys ("1") to int keys, so ids()/each()/clear() could hand ints to remove(string) and TypeError. Cast to string in those paths. Surfaced by the laravel-ws integration (connection ids are sequential numbers). Regression test added. Suite: 20 tests. rel learn-atc #1055 #1057 Co-Authored-By: Claude Opus 4.8 --- src/Support/ConnectionRegistry.php | 7 ++++--- tests/ConnectionRegistryTest.php | 20 ++++++++++++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/Support/ConnectionRegistry.php b/src/Support/ConnectionRegistry.php index de092b7..88a3b65 100644 --- a/src/Support/ConnectionRegistry.php +++ b/src/Support/ConnectionRegistry.php @@ -67,7 +67,8 @@ final class ConnectionRegistry implements \Countable /** @return array */ public function ids(): array { - return array_keys($this->items); + // Cast: PHP coerces numeric-string keys (e.g. "1") to int keys internally. + return array_map(strval(...), array_keys($this->items)); } public function count(): int @@ -82,7 +83,7 @@ final class ConnectionRegistry implements \Countable public function each(callable $callback): void { foreach ($this->items as $id => $connection) { - $callback($connection, $id); + $callback($connection, (string) $id); } } @@ -102,7 +103,7 @@ final class ConnectionRegistry implements \Countable public function clear(): void { foreach (array_keys($this->items) as $id) { - $this->remove($id); + $this->remove((string) $id); // keys may be int (PHP numeric-string coercion) } } } diff --git a/tests/ConnectionRegistryTest.php b/tests/ConnectionRegistryTest.php index bbb2f10..2a48dd1 100644 --- a/tests/ConnectionRegistryTest.php +++ b/tests/ConnectionRegistryTest.php @@ -55,6 +55,26 @@ final class ConnectionRegistryTest extends TestCase $this->assertSame(['x'], $added); } + public function test_numeric_string_ids_survive_ids_each_and_clear(): void + { + // PHP coerces numeric-string array keys ("1") to int keys internally, which + // used to hand ints to remove(string) during clear(). Regression guard. + $registry = new ConnectionRegistry; + $registry->add('1', new \stdClass); + $registry->add('2', new \stdClass); + + $this->assertSame(['1', '2'], $registry->ids()); + + $seen = []; + $registry->each(function (object $conn, string $id) use (&$seen) { + $seen[] = $id; // must receive strings, not ints + }); + $this->assertSame(['1', '2'], $seen); + + $registry->clear(); // must not TypeError + $this->assertCount(0, $registry); + } + public function test_each_visits_every_connection(): void { $registry = new ConnectionRegistry;