From d8679b7b1fcf21c990910c0c28ddc5df097b8bb4 Mon Sep 17 00:00:00 2001 From: Chad Sikorra Date: Sat, 9 May 2026 17:51:25 -0400 Subject: [PATCH] Add a way to close the socket FD without fully calling shutdown. Needed in PCNTL fork scenarios to clean-up resources properly. --- src/FreeDSx/Socket/Socket.php | 10 ++++++++-- tests/unit/SocketTest.php | 22 ++++++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/FreeDSx/Socket/Socket.php b/src/FreeDSx/Socket/Socket.php index c34d6a8..0b462df 100644 --- a/src/FreeDSx/Socket/Socket.php +++ b/src/FreeDSx/Socket/Socket.php @@ -14,6 +14,7 @@ namespace FreeDSx\Socket; use FreeDSx\Socket\Exception\ConnectionException; +use function fclose; use function fread; use function fwrite; use function stream_context_create; @@ -122,10 +123,15 @@ public function isEncrypted(): bool return $this->isEncrypted; } - public function close(): static + /** + * @param bool $shutdown False closes only this process's FD copy without affecting shared socket state. + */ + public function close(bool $shutdown = true): static { if ($this->socket !== null) { - stream_socket_shutdown($this->socket, STREAM_SHUT_RDWR); + $shutdown + ? stream_socket_shutdown($this->socket, STREAM_SHUT_RDWR) + : fclose($this->socket); } $this->socket = null; $this->isEncrypted = false; diff --git a/tests/unit/SocketTest.php b/tests/unit/SocketTest.php index cd713c9..cff0950 100644 --- a/tests/unit/SocketTest.php +++ b/tests/unit/SocketTest.php @@ -162,6 +162,28 @@ public function test_it_should_tell_whether_it_is_connected_for_unix(): void self::assertFalse($subject->isConnected()); } + public function test_close_without_shutdown_disconnects_tcp(): void + { + $subject = Socket::tcp( + 'www.google.com', + (new SocketOptions())->setPort(80), + ); + + self::assertTrue($subject->isConnected()); + $subject->close(shutdown: false); + self::assertFalse($subject->isConnected()); + } + + public function test_close_without_shutdown_disconnects_unix(): void + { + $path = $this->createUnixServer(); + $subject = Socket::unix($path); + + self::assertTrue($subject->isConnected()); + $subject->close(shutdown: false); + self::assertFalse($subject->isConnected()); + } + public function test_it_should_return_at_most_buffer_size_bytes_per_read(): void { [$local, $remote] = $this->createSocketPair();