Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion phpstan.neon
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
parameters:
level: 6
level: max
paths:
- %currentWorkingDirectory%/src
phpVersion: 80200
Expand Down
2 changes: 1 addition & 1 deletion phpstan.tests.neon
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
parameters:
treatPhpDocTypesAsCertain: false
level: 10
level: max
paths:
- %currentWorkingDirectory%/tests
6 changes: 6 additions & 0 deletions src/FreeDSx/Socket/HasSocketOptions.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ trait HasSocketOptions

private int $timeoutRead = 15;

/**
* @var positive-int
*/
private int $bufferSize = 8192;

public function setTransport(Transport $transport): self
Expand Down Expand Up @@ -231,6 +234,9 @@ public function setBufferSize(int $bufferSize): self
return $this;
}

/**
* @return positive-int
*/
public function getBufferSize(): int
{
return $this->bufferSize;
Expand Down
20 changes: 14 additions & 6 deletions src/FreeDSx/Socket/Queue/Asn1MessageQueue.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,13 @@

use FreeDSx\Asn1\Encoder\EncoderInterface;
use FreeDSx\Asn1\Exception\PartialPduException;
use FreeDSx\Asn1\Type\AbstractType;
use FreeDSx\Socket\Exception\PartialMessageException;
use FreeDSx\Socket\PduInterface;
use FreeDSx\Socket\Socket;
use RuntimeException;
use UnexpectedValueException;
use function get_debug_type;

/**
* Represents an ASN.1 based message queue using the FreeDSx ASN.1 library.
Expand All @@ -35,7 +39,7 @@ public function __construct(
protected ?string $pduClass = null,
) {
if ($pduClass !== null && !\is_subclass_of($pduClass, PduInterface::class)) {
throw new \RuntimeException(sprintf(
throw new RuntimeException(sprintf(
'The class "%s" must implement "%s", but it does not.',
$pduClass,
PduInterface::class,
Expand All @@ -61,13 +65,17 @@ protected function constructMessage(
?int $id = null,
): mixed {
if ($this->pduClass === null) {
throw new \RuntimeException('You must either define a PDU class or override getPdu().');
throw new RuntimeException('You must either define a PDU class or override getPdu().');
}
$callable = [$this->pduClass, 'fromAsn1'];
if (!\is_callable($callable)) {
throw new \RuntimeException(sprintf('The class %s is not callable.', $this->pduClass));
$asn1 = $message->getMessage();
if (!$asn1 instanceof AbstractType) {
throw new UnexpectedValueException(sprintf(
'Expected an instance of %s, got %s.',
AbstractType::class,
get_debug_type($asn1),
));
}

return \call_user_func($callable, $message->getMessage());
return ($this->pduClass)::fromAsn1($asn1);
}
}
57 changes: 44 additions & 13 deletions src/FreeDSx/Socket/Socket.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,16 +61,17 @@ public function __construct(

public function read(bool $block = true): string|false
{
stream_set_blocking($this->socket, $block);
$stream = $this->getStream();
stream_set_blocking($stream, $block);

$data = fread(
$this->socket,
$stream,
$this->options->getBufferSize(),
);

if (!$block) {
stream_set_blocking(
$this->socket,
$stream,
true,
);
}
Expand All @@ -82,14 +83,20 @@ public function read(bool $block = true): string|false

public function write(string $data): static
{
@fwrite($this->socket, $data);
@fwrite(
$this->getStream(),
$data,
);

return $this;
}

public function block(bool $block): static
{
stream_set_blocking($this->socket, $block);
stream_set_blocking(
$this->getStream(),
$block
);

return $this;
}
Expand Down Expand Up @@ -134,13 +141,21 @@ public function close(): static
*/
public function encrypt(bool $encrypt): static
{
stream_set_blocking($this->socket, true);
$stream = $this->getStream();

stream_set_blocking(
$stream,
true,
);
$result = stream_socket_enable_crypto(
$this->socket,
$stream,
$encrypt,
$this->options->getSslCryptoMethod(),
);
stream_set_blocking($this->socket, false);
stream_set_blocking(
$stream,
false,
);

if ($result !== true) {
throw new ConnectionException(sprintf(
Expand Down Expand Up @@ -179,8 +194,8 @@ public function connect(string $host): static
STREAM_CLIENT_CONNECT,
$this->createSocketContext(),
);
$this->errorNumber = $errorNumber;
$this->errorMessage = $errorMessage;
$this->errorNumber = $errorNumber ?? 0;
$this->errorMessage = $errorMessage ?? '';

if ($socket === false) {
throw new ConnectionException(sprintf(
Expand Down Expand Up @@ -208,9 +223,12 @@ public function getOptions(): SocketOptionsInterface
*/
public static function create(
string $host,
?SocketOptions $options = null,
SocketOptions $options = new SocketOptions(),
): Socket {
return (new self(null, $options))->connect($host);
return (new self(
null,
$options,
))->connect($host);
}

/**
Expand Down Expand Up @@ -283,8 +301,21 @@ protected function createSocketContext()
protected function setStreamOpts(): void
{
stream_set_timeout(
$this->socket,
$this->getStream(),
$this->options->getTimeoutRead(),
);
}

/**
* @return resource
* @throws ConnectionException
*/
protected function getStream()
{
if ($this->socket === null) {
throw new ConnectionException('The socket is not connected.');
}

return $this->socket;
}
}
3 changes: 3 additions & 0 deletions src/FreeDSx/Socket/SocketOptionsInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ public function getTimeoutConnect(): int;

public function getTimeoutRead(): int;

/**
* @return positive-int
*/
public function getBufferSize(): int;

/**
Expand Down
29 changes: 18 additions & 11 deletions src/FreeDSx/Socket/SocketServer.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@
namespace FreeDSx\Socket;

use FreeDSx\Socket\Exception\ConnectionException;
use function array_search;
use function array_filter;
use function array_values;
use function is_resource;
use function is_string;
use function stream_socket_accept;
use function stream_socket_recvfrom;
use function stream_socket_server;
Expand Down Expand Up @@ -81,8 +82,8 @@ public function listen(
$flags,
$this->createSocketContext(),
);
$this->errorNumber = $errorNumber;
$this->errorMessage = $errorMessage;
$this->errorNumber = $errorNumber ?? 0;
$this->errorMessage = $errorMessage ?? '';

if ($socket === false) {
throw new ConnectionException(sprintf(
Expand Down Expand Up @@ -128,20 +129,27 @@ private static function optionsForAcceptedClient(SocketServerOptions $server): S
/**
* Receive data from a UDP based socket. Optionally get the IP address the data was received from.
*
* @param-out string|null $ipAddress
* @todo Buffer size should be adjustable. Max UDP packet size is 65507. Currently this avoids possible truncation.
*/
public function receive(?string &$ipAddress = null): ?string
{
$this->block(true);

$received = null;
$data = stream_socket_recvfrom(
$this->socket,
$this->getStream(),
65507,
0,
$ipAddress,
$received,
);
$ipAddress = is_string($received)
? $received
: null;

return $data === false ? null : $data;
return $data === false
? null
: $data;
}

/**
Expand All @@ -154,11 +162,10 @@ public function getClients(): array

public function removeClient(Socket $socket): void
{
$index = array_search($socket, $this->clients, true);
if ($index !== false) {
unset($this->clients[$index]);
$this->clients = array_values($this->clients);
}
$this->clients = array_values(array_filter(
$this->clients,
fn (Socket $client): bool => $client !== $socket,
));
}

/**
Expand Down
Loading