From 4484d565fd274b678a3459848923f4fc876c54bb Mon Sep 17 00:00:00 2001 From: Timm Friebe Date: Wed, 1 Apr 2026 12:43:47 +0200 Subject: [PATCH 1/3] Add Connection::params() to access parameters passed in handshake request --- .../websocket/protocol/Connection.class.php | 16 +++++++++++++-- .../unittest/ConnectionTest.class.php | 20 ++++++++++++++++++- .../websocket/unittest/MessagesTest.class.php | 2 +- 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/src/main/php/websocket/protocol/Connection.class.php b/src/main/php/websocket/protocol/Connection.class.php index ba9f8a1..bc0289f 100755 --- a/src/main/php/websocket/protocol/Connection.class.php +++ b/src/main/php/websocket/protocol/Connection.class.php @@ -12,7 +12,7 @@ class Connection { const MAXLENGTH= 0x8000000; - private $socket, $id, $listener, $path, $headers; + private $socket, $id, $listener, $path, $params, $headers; /** * Creates a new connection @@ -27,7 +27,16 @@ public function __construct($socket, $id, $listener, $path= '/', $headers= []) { $this->socket= $socket; $this->id= $id; $this->listener= $listener; - $this->path= $path; + + $p= strpos($path, '?'); + if (false === $p) { + $this->path= $path; + $this->params= []; + } else { + $this->path= substr($path, 0, $p); + parse_str(substr($path, $p + 1), $this->params); + } + $this->headers= $headers; } @@ -40,6 +49,9 @@ public function listener() { return $this->listener; } /** @return string */ public function path() { return $this->path; } + /** @return [:var] */ + public function params() { return $this->params; } + /** @return [:var] */ public function headers() { return $this->headers; } diff --git a/src/test/php/websocket/unittest/ConnectionTest.class.php b/src/test/php/websocket/unittest/ConnectionTest.class.php index 1601a72..393ea1a 100755 --- a/src/test/php/websocket/unittest/ConnectionTest.class.php +++ b/src/test/php/websocket/unittest/ConnectionTest.class.php @@ -25,7 +25,7 @@ private function listener($callable= null) { * @return [] */ private function receive($channel) { - $conn= new Connection($channel->connect(), self::ID, $this->listener(), []); + $conn= new Connection($channel->connect(), self::ID, $this->listener()); $r= []; foreach ($conn->receive() as $type => $message) { $r[]= [$type => $message]; @@ -54,6 +54,24 @@ public function path($value) { Assert::equals($value, (new Connection(new Channel(), self::ID, $this->listener(), $value))->path()); } + #[Test] + public function path_does_not_contain_params() { + Assert::equals('/', (new Connection(new Channel(), self::ID, $this->listener(), '/?for=test'))->path()); + } + + #[Test, Values(['/?for=test', '/ws?for=test', '/feed/6100?for=test'])] + public function params($value) { + Assert::equals(['for' => 'test'], (new Connection(new Channel(), self::ID, $this->listener(), $value))->params()); + } + + #[Test] + public function array_param() { + Assert::equals( + ['for' => ['a', 'b']], + (new Connection(new Channel(), self::ID, $this->listener(), '?for[]=a&for[]=b'))->params() + ); + } + #[Test, Values([[[]], [['User-Agent' => 'Test', 'Accept' => '*/*']]])] public function headers($value) { Assert::equals($value, (new Connection(new Channel(), self::ID, $this->listener(), '/', $value))->headers()); diff --git a/src/test/php/websocket/unittest/MessagesTest.class.php b/src/test/php/websocket/unittest/MessagesTest.class.php index 51301e3..41b71ed 100755 --- a/src/test/php/websocket/unittest/MessagesTest.class.php +++ b/src/test/php/websocket/unittest/MessagesTest.class.php @@ -33,7 +33,7 @@ private function fixture($channel, $listener= null) { // Simulate handshake $channel->connect(); - $listeners->connections[self::ID]= new Connection($channel, self::ID, $listeners->listener('/ws'), []); + $listeners->connections[self::ID]= new Connection($channel, self::ID, $listeners->listener('/ws')); return new Messages($listeners, $this->log); } From 5dc193389b8c5afd2db10046484f26b8d98b5f6b Mon Sep 17 00:00:00 2001 From: Timm Friebe Date: Wed, 1 Apr 2026 12:48:47 +0200 Subject: [PATCH 2/3] Add param() --- .../websocket/protocol/Connection.class.php | 10 ++++++++++ .../unittest/ConnectionTest.class.php | 18 +++++++++++++----- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/main/php/websocket/protocol/Connection.class.php b/src/main/php/websocket/protocol/Connection.class.php index bc0289f..419d4e1 100755 --- a/src/main/php/websocket/protocol/Connection.class.php +++ b/src/main/php/websocket/protocol/Connection.class.php @@ -55,6 +55,16 @@ public function params() { return $this->params; } /** @return [:var] */ public function headers() { return $this->headers; } + /** + * Returns a named parameter, or NULL if it does not exist. + * + * @param string $name + * @return var + */ + public function param($name) { + return $this->params[$name] ?? null; + } + /** * Opens connection * diff --git a/src/test/php/websocket/unittest/ConnectionTest.class.php b/src/test/php/websocket/unittest/ConnectionTest.class.php index 393ea1a..0809e43 100755 --- a/src/test/php/websocket/unittest/ConnectionTest.class.php +++ b/src/test/php/websocket/unittest/ConnectionTest.class.php @@ -61,15 +61,23 @@ public function path_does_not_contain_params() { #[Test, Values(['/?for=test', '/ws?for=test', '/feed/6100?for=test'])] public function params($value) { - Assert::equals(['for' => 'test'], (new Connection(new Channel(), self::ID, $this->listener(), $value))->params()); + $conn= new Connection(new Channel(), self::ID, $this->listener(), $value); + + Assert::equals('test', $conn->param('for')); + Assert::equals(['for' => 'test'], $conn->params()); + } + + #[Test] + public function non_existant_param() { + Assert::null((new Connection(new Channel(), self::ID, $this->listener(), '?for=test'))->param('non-existant')); } #[Test] public function array_param() { - Assert::equals( - ['for' => ['a', 'b']], - (new Connection(new Channel(), self::ID, $this->listener(), '?for[]=a&for[]=b'))->params() - ); + $conn= new Connection(new Channel(), self::ID, $this->listener(), '?for[]=a&for[]=b'); + + Assert::equals(['a', 'b'], $conn->param('for')); + Assert::equals(['for' => ['a', 'b']], $conn->params()); } #[Test, Values([[[]], [['User-Agent' => 'Test', 'Accept' => '*/*']]])] From 952d830d3930394d2d72d9eb7983011ec85abb14 Mon Sep 17 00:00:00 2001 From: Timm Friebe Date: Wed, 1 Apr 2026 12:50:50 +0200 Subject: [PATCH 3/3] Add `$default` to param() - consistent with util.URI --- src/main/php/websocket/protocol/Connection.class.php | 5 +++-- src/test/php/websocket/unittest/ConnectionTest.class.php | 5 ++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/main/php/websocket/protocol/Connection.class.php b/src/main/php/websocket/protocol/Connection.class.php index 419d4e1..880be46 100755 --- a/src/main/php/websocket/protocol/Connection.class.php +++ b/src/main/php/websocket/protocol/Connection.class.php @@ -59,10 +59,11 @@ public function headers() { return $this->headers; } * Returns a named parameter, or NULL if it does not exist. * * @param string $name + * @param var $default * @return var */ - public function param($name) { - return $this->params[$name] ?? null; + public function param($name, $default= null) { + return $this->params[$name] ?? $default; } /** diff --git a/src/test/php/websocket/unittest/ConnectionTest.class.php b/src/test/php/websocket/unittest/ConnectionTest.class.php index 0809e43..eb2e238 100755 --- a/src/test/php/websocket/unittest/ConnectionTest.class.php +++ b/src/test/php/websocket/unittest/ConnectionTest.class.php @@ -69,7 +69,10 @@ public function params($value) { #[Test] public function non_existant_param() { - Assert::null((new Connection(new Channel(), self::ID, $this->listener(), '?for=test'))->param('non-existant')); + $conn= new Connection(new Channel(), self::ID, $this->listener(), '?for=test'); + + Assert::null($conn->param('non-existant')); + Assert::equals('default', $conn->param('non-existant', 'default')); } #[Test]