Skip to content

Commit 26355fe

Browse files
committed
Socket Refactoring
1 parent a26a7a6 commit 26355fe

File tree

6 files changed

+377
-157
lines changed

6 files changed

+377
-157
lines changed

src/EventLoop/Interfaces/EventLoopHandlerInterface.php

Lines changed: 0 additions & 10 deletions
This file was deleted.

src/EventLoop/Interfaces/StreamHandlerInterface.php

Lines changed: 0 additions & 12 deletions
This file was deleted.

src/EventLoop/Interfaces/TimerHandlerInterface.php

Lines changed: 0 additions & 14 deletions
This file was deleted.

src/EventLoop/ValueObjects/Socket.php

Lines changed: 126 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -2,139 +2,196 @@
22

33
namespace Rcalicdan\FiberAsync\EventLoop\ValueObjects;
44

5-
use Rcalicdan\FiberAsync\Promise\Interfaces\PromiseInterface;
6-
use Rcalicdan\FiberAsync\Socket\AsyncSocketOperations;
7-
use Rcalicdan\FiberAsync\Socket\Exceptions\SocketException;
8-
95
/**
10-
* Socket wrapper for asynchronous socket operations.
6+
* Socket value object representing socket connection data.
117
*
12-
* This class provides a high-level interface for asynchronous socket operations
13-
* including reading, writing, and connection management. It wraps a socket resource
14-
* and delegates operations to the AsyncSocketOperations handler.
8+
* This is a pure value object that holds socket-related data without dependencies
9+
* or business logic. It's immutable and focused on data representation.
1510
*/
16-
class Socket
11+
final class Socket
1712
{
1813
/**
1914
* The underlying socket resource.
2015
*
21-
* @var resource
16+
* @var resource|null
2217
*/
2318
private $resource;
2419

2520
/**
26-
* Handler for asynchronous socket operations.
27-
*/
28-
private AsyncSocketOperations $operations;
29-
30-
/**
31-
* Whether the socket has been closed.
21+
* Whether the socket has been marked as closed.
3222
*/
33-
private bool $isClosed = false;
23+
private bool $isClosed;
3424

3525
/**
36-
* Default byte size for read operations.
26+
* Socket metadata (address, port, etc.)
27+
*
28+
* @var array<string, mixed>
3729
*/
38-
private const DEFAULT_BYTE_SIZE = 8192;
30+
private array $metadata;
3931

4032
/**
41-
* Creates a new Socket instance.
33+
* Creates a new Socket value object.
4234
*
43-
* @param resource $resource The socket resource
44-
* @param AsyncSocketOperations $operations Handler for async operations
35+
* @param resource|null $resource The socket resource
36+
* @param bool $isClosed Whether the socket is closed
37+
* @param array<string, mixed> $metadata Additional socket metadata
4538
*
46-
* @throws \TypeError If resource is not a valid resource type
39+
* @throws \TypeError If resource is not a valid resource type or null
4740
*/
48-
public function __construct($resource, AsyncSocketOperations $operations)
41+
public function __construct($resource, bool $isClosed = false, array $metadata = [])
4942
{
50-
if (! is_resource($resource)) {
51-
throw new \TypeError('Expected resource, got '.gettype($resource));
43+
if (!is_resource($resource) && $resource !== null) {
44+
throw new \TypeError('Expected resource or null, got ' . gettype($resource));
5245
}
5346

5447
$this->resource = $resource;
55-
$this->operations = $operations;
56-
stream_set_blocking($this->resource, false);
48+
$this->isClosed = $isClosed;
49+
$this->metadata = $metadata;
5750
}
5851

5952
/**
60-
* Asynchronously reads data from the socket.
53+
* Gets the underlying socket resource.
6154
*
62-
* @param int|null $length Maximum number of bytes to read (default: 8192)
63-
* @param float|null $timeout Timeout in seconds (default: 10.0)
64-
* @return PromiseInterface<string> Promise that resolves with the read data
55+
* @return resource|null The socket resource
56+
*/
57+
public function getResource()
58+
{
59+
return $this->resource;
60+
}
61+
62+
/**
63+
* Checks if the socket is marked as closed.
6564
*
66-
* @throws SocketException If the socket is closed
65+
* @return bool True if the socket is closed, false otherwise
6766
*/
68-
public function read(?int $length = null, ?float $timeout = 10.0): PromiseInterface
67+
public function isClosed(): bool
6968
{
70-
if ($this->isClosed) {
71-
return $this->operations->getAsyncOps()->rejected(new SocketException('Socket is closed.'));
72-
}
69+
return $this->isClosed;
70+
}
7371

74-
$readLength = $length ?? self::DEFAULT_BYTE_SIZE;
72+
/**
73+
* Gets socket metadata.
74+
*
75+
* @return array<string, mixed> The metadata array
76+
*/
77+
public function getMetadata(): array
78+
{
79+
return $this->metadata;
80+
}
7581

76-
return $this->operations->read($this, $readLength, $timeout);
82+
/**
83+
* Gets a specific metadata value.
84+
*
85+
* @param string $key The metadata key
86+
* @param mixed $default Default value if key doesn't exist
87+
* @return mixed The metadata value or default
88+
*/
89+
public function getMetadataValue(string $key, mixed $default = null): mixed
90+
{
91+
return $this->metadata[$key] ?? $default;
7792
}
7893

7994
/**
80-
* Asynchronously writes data to the socket.
95+
* Creates a new Socket instance with updated closed status.
8196
*
82-
* @param string $data The data to write
83-
* @param float|null $timeout Timeout in seconds (default: 10.0)
84-
* @return PromiseInterface<int> Promise that resolves with the number of bytes written
97+
* @param bool $isClosed The new closed status
98+
* @return self A new Socket instance
99+
*/
100+
public function withClosedStatus(bool $isClosed): self
101+
{
102+
return new self($this->resource, $isClosed, $this->metadata);
103+
}
104+
105+
/**
106+
* Creates a new Socket instance with additional metadata.
85107
*
86-
* @throws SocketException If the socket is closed
108+
* @param array<string, mixed> $metadata The metadata to merge
109+
* @return self A new Socket instance
87110
*/
88-
public function write(string $data, ?float $timeout = 10.0): PromiseInterface
111+
public function withMetadata(array $metadata): self
89112
{
90-
if ($this->isClosed) {
91-
return $this->operations->getAsyncOps()->rejected(new SocketException('Socket is closed.'));
92-
}
113+
return new self($this->resource, $this->isClosed, array_merge($this->metadata, $metadata));
114+
}
93115

94-
return $this->operations->write($this, $data, $timeout);
116+
/**
117+
* Creates a new Socket instance with a single metadata value.
118+
*
119+
* @param string $key The metadata key
120+
* @param mixed $value The metadata value
121+
* @return self A new Socket instance
122+
*/
123+
public function withMetadataValue(string $key, mixed $value): self
124+
{
125+
$newMetadata = $this->metadata;
126+
$newMetadata[$key] = $value;
127+
128+
return new self($this->resource, $this->isClosed, $newMetadata);
95129
}
96130

97131
/**
98-
* Closes the socket connection.
132+
* Checks if the socket resource is valid.
99133
*
100-
* This method ensures the socket is properly closed and cleaned up.
101-
* It's safe to call multiple times - subsequent calls will be ignored.
134+
* @return bool True if resource is valid, false otherwise
102135
*/
103-
public function close(): void
136+
public function hasValidResource(): bool
104137
{
105-
if (! $this->isClosed) {
106-
$this->isClosed = true;
107-
$this->operations->close($this);
108-
}
138+
return is_resource($this->resource);
109139
}
110140

111141
/**
112-
* Gets the underlying socket resource.
142+
* Gets the socket type from metadata.
113143
*
114-
* @return resource The socket resource
144+
* @return string|null The socket type or null if not set
115145
*/
116-
public function getResource()
146+
public function getType(): ?string
117147
{
118-
return $this->resource;
148+
return $this->getMetadataValue('type');
119149
}
120150

121151
/**
122-
* Checks if the socket is closed.
152+
* Gets the socket address from metadata.
123153
*
124-
* @return bool True if the socket is closed, false otherwise
154+
* @return string|null The socket address or null if not set
125155
*/
126-
public function isClosed(): bool
156+
public function getAddress(): ?string
127157
{
128-
return $this->isClosed;
158+
return $this->getMetadataValue('address');
159+
}
160+
161+
/**
162+
* Gets the socket port from metadata.
163+
*
164+
* @return int|null The socket port or null if not set
165+
*/
166+
public function getPort(): ?int
167+
{
168+
return $this->getMetadataValue('port');
169+
}
170+
171+
/**
172+
* String representation of the socket.
173+
*
174+
* @return string
175+
*/
176+
public function __toString(): string
177+
{
178+
$address = $this->getAddress() ?? 'unknown';
179+
$port = $this->getPort() ?? 'unknown';
180+
$status = $this->isClosed ? 'closed' : 'open';
181+
182+
return "Socket({$address}:{$port}, {$status})";
129183
}
130184

131185
/**
132-
* Destructor ensures the socket is properly closed.
186+
* Compare two Socket instances for equality.
133187
*
134-
* @return void
188+
* @param Socket $other The other socket to compare
189+
* @return bool True if sockets are equal, false otherwise
135190
*/
136-
public function __destruct()
191+
public function equals(Socket $other): bool
137192
{
138-
$this->close();
193+
return $this->resource === $other->resource
194+
&& $this->isClosed === $other->isClosed
195+
&& $this->metadata === $other->metadata;
139196
}
140197
}

0 commit comments

Comments
 (0)