Skip to content

Commit

Permalink
Fix "Call to a member function message() on null" w/ open sockets
Browse files Browse the repository at this point in the history
  • Loading branch information
thekid committed Jan 12, 2025
1 parent d784ec4 commit cca1f6e
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 7 deletions.
4 changes: 4 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ WebSockets change log

## ?.?.? / ????-??-??

* Fix "Call to a member function message() on null" errors when using an
already connected socket in the `WebSocket` constructor.
(@thekid)

## 4.0.0 / 2024-10-05

* Dropped support for PHP < 7.4, see xp-framework/rfc#343 - @thekid
Expand Down
19 changes: 12 additions & 7 deletions src/main/php/websocket/WebSocket.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public function path() { return $this->path; }
public function origin() { return $this->origin; }

/** @return bool */
public function connected() { return $this->socket->isConnected(); }
public function connected() { return null !== $this->conn; }

/** @param function(int): string */
public function random($function) {
Expand All @@ -77,11 +77,11 @@ public function listening(Listener $listener) {
* @return void
*/
public function connect($headers= []) {
if ($this->socket->isConnected()) return;
if ($this->conn) return;

$key= base64_encode(($this->random)(16));
$headers+= ['Host' => $this->socket->host, 'Origin' => $this->origin];
$this->socket->connect();
$this->socket->isConnected() || $this->socket->connect();
$this->socket->write(
"GET {$this->path} HTTP/1.1\r\n".
"Upgrade: websocket\r\n".
Expand Down Expand Up @@ -135,7 +135,7 @@ public function connect($headers= []) {
* @throws peer.ProtocolException
*/
public function ping($payload= '') {
if (!$this->socket->isConnected()) throw new ProtocolException('Not connected');
if (!$this->conn) throw new ProtocolException('Not connected');

$this->conn->message(Opcodes::PING, $payload, ($this->random)(4));
}
Expand All @@ -148,7 +148,7 @@ public function ping($payload= '') {
* @throws peer.ProtocolException
*/
public function send($message) {
if (!$this->socket->isConnected()) throw new ProtocolException('Not connected');
if (!$this->conn) throw new ProtocolException('Not connected');

if ($message instanceof Bytes) {
$this->conn->message(Opcodes::BINARY, $message, ($this->random)(4));
Expand All @@ -165,12 +165,13 @@ public function send($message) {
* @throws peer.ProtocolException
*/
public function receive($timeout= null) {
if (!$this->socket->isConnected()) throw new ProtocolException('Not connected');
if (!$this->conn) throw new ProtocolException('Not connected');

if (null !== $timeout && !$this->socket->canRead($timeout)) return null;

$result= null;
foreach ($this->conn->receive() as $opcode => $packet) {
// echo "<<< ", Opcodes::nameOf($opcode), " -> ", addcslashes($packet, "\0..\37!\177..\377"), "\n";
switch ($opcode) {
case Opcodes::TEXT:
$result= $packet;
Expand All @@ -192,6 +193,8 @@ public function receive($timeout= null) {
case Opcodes::CLOSE:
$close= unpack('ncode/a*reason', $packet);
$this->conn->close($close['code'], $close['reason']);
$this->conn= null;
$this->socket->close();

// 1000 is a normal close, all others indicate an error
if (1000 === $close['code']) return null;
Expand All @@ -209,14 +212,16 @@ public function receive($timeout= null) {
* @return void
*/
public function close($code= 1000, $reason= '') {
if (!$this->socket->isConnected()) return;
if (!$this->conn) return;

try {
$this->conn->message(Opcodes::CLOSE, pack('na*', $code, $reason), ($this->random)(4));
} catch (Throwable $ignored) {
// ...
}

$this->conn->close($code, $reason);
$this->conn= null;
$this->socket->close();
}

Expand Down
9 changes: 9 additions & 0 deletions src/test/php/websocket/unittest/WebSocketTest.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,15 @@ public function connect() {
Assert::true($fixture->connected());
}

#[Test]
public function connect_connected() {
$fixture= $this->fixture();
$fixture->socket()->connect();
$fixture->connect();

Assert::true($fixture->connected());
}

#[Test]
public function close() {
$fixture= $this->fixture();
Expand Down

0 comments on commit cca1f6e

Please sign in to comment.