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 <noreply@anthropic.com>
This commit is contained in:
Blax Software 2026-07-07 14:04:58 +02:00
parent 9173a8897b
commit 5eee2f83b8
2 changed files with 24 additions and 3 deletions

View File

@ -67,7 +67,8 @@ final class ConnectionRegistry implements \Countable
/** @return array<int,string> */
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)
}
}
}

View File

@ -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;