diff --git a/README.md b/README.md
index 3d79801..2d384ad 100644
--- a/README.md
+++ b/README.md
@@ -3,13 +3,13 @@
> [!IMPORTANT]
> Dotkernel component used to queue tasks to be processed asynchronously based on [netglue/laminas-messenger](https://github.com/netglue/laminas-messenger)
- A queue system is a vital component in modern web applications that enables the decoupling of certain tasks from the regular request-response cycle.
+A queue system is a vital part in modern web applications that enables the decoupling of certain tasks from the regular request-response cycle.
- This is especially useful for time-consuming and resource-intensive operations which are thus handled asynchronously by background workers on a separate system.
+This is especially useful for time-consuming and resource-intensive operations which are thus handled asynchronously by background workers on a separate system.
-The greatest benefit is to application responsiveness which allows faster execution, while the heavy lifting is scheduled in the queue based on available resources.
+The greatest benefit is to application responsiveness, which allows faster execution, while the heavy lifting is scheduled in the queue based on available resources.
- The queue system uses logs to ensure maintainability and implements retry features for reliability and stability.
+The queue system uses logs to ensure maintainability and implements retry features for reliability and stability.

@@ -28,10 +28,6 @@ The greatest benefit is to application responsiveness which allows faster execut
[](https://github.com/dotkernel/queue/actions/workflows/qodana_code_quality.yml)
[](https://github.com/dotkernel/queue/actions/workflows/static-analysis.yml)
-## Installation
-
-> Until we have a compiled documentation, read the files from /doc/book/v1 folder
-
## Documentation
Documentation is available at: https://docs.dotkernel.org/queue-documentation
diff --git a/composer.json b/composer.json
index cc845be..46ae5ed 100644
--- a/composer.json
+++ b/composer.json
@@ -55,12 +55,12 @@
},
"require-dev": {
"laminas/laminas-coding-standard": "^3.0",
- "phpunit/phpunit": "^10.5.45",
- "roave/security-advisories": "dev-master",
- "swoole/ide-helper": "~5.0.0",
"phpstan/phpstan": "^2.0",
"phpstan/phpstan-doctrine": "^2.0",
- "phpstan/phpstan-phpunit": "^2.0"
+ "phpstan/phpstan-phpunit": "^2.0",
+ "phpunit/phpunit": "^10.5.45",
+ "roave/security-advisories": "dev-master",
+ "swoole/ide-helper": "~5.0.0"
},
"autoload": {
"psr-4": {
diff --git a/config/autoload/local.php.dist b/config/autoload/local.php.dist
index 429a793..3de5fb1 100644
--- a/config/autoload/local.php.dist
+++ b/config/autoload/local.php.dist
@@ -9,7 +9,7 @@
declare(strict_types=1);
-$baseUrl = 'http://queue.dotkernel.net';
+$baseUrl = 'https://queue.dotkernel.net';
$databases = [
'default' => [
@@ -43,13 +43,13 @@ return [
'protocol' => 'tcp',
'host' => 'localhost',
'port' => '8556',
- 'eof' => "\n",
+ 'eof' => PHP_EOL,
],
],
//delay time until the message is added back to the queue if an error occurs during processing
'fail-safe' => [
- 'first_retry' => 3600000, // 1h
- 'second_retry' => 43200000, // 12h
- 'third_retry' => 86400000, // 24h
- ],
+ 'first_retry' => 3600000, // 1h
+ 'second_retry' => 43200000, // 12h
+ 'third_retry' => 86400000, // 24h
+ ],
];
diff --git a/config/autoload/log.local.php.dist b/config/autoload/log.local.php.dist
index 1422cc8..e88e592 100644
--- a/config/autoload/log.local.php.dist
+++ b/config/autoload/log.local.php.dist
@@ -1,5 +1,7 @@
[
'writers' => [
'FileWriter' => [
- 'name' => 'stream',
- 'level' => \Dot\Log\Logger::ALERT, // this is equal to 1
+ 'name' => 'stream',
+ 'level' => Logger::ALERT,
'options' => [
- 'stream' => __DIR__ . '/../../log/queue-log.log',
+ 'stream' => __DIR__ . '/../../log/queue-log.log',
'formatter' => [
'name' => Json::class,
],
],
],
],
- ]
+ ],
],
],
];
diff --git a/config/autoload/messenger.local.php.dist b/config/autoload/messenger.local.php.dist
index 4268d3b..4ccffb6 100644
--- a/config/autoload/messenger.local.php.dist
+++ b/config/autoload/messenger.local.php.dist
@@ -1,25 +1,28 @@
[
- "messenger" => [
- "transports" => [
- "redis_transport" => [
- 'dsn' => 'redis://127.0.0.1:6379/messages',
- 'options' => [], // Redis specific options
+ 'symfony' => [
+ 'messenger' => [
+ 'transports' => [
+ 'redis_transport' => [
+ 'dsn' => 'redis://127.0.0.1:6379/messages',
+ 'options' => [], // Redis specific options
'serializer' => SymfonySerializer::class,
- ]
- ]
- ]
+ ],
+ ],
+ ],
+ ],
+ 'dependencies' => [
+ 'factories' => [
+ 'redis_transport' => [TransportFactory::class, 'redis_transport'],
+ SymfonySerializer::class => fn(ContainerInterface $container) => new PhpSerializer(),
+ ],
],
- "dependencies" => [
- "factories" => [
- "redis_transport" => [TransportFactory::class, 'redis_transport'],
- SymfonySerializer::class => fn(\Psr\Container\ContainerInterface $container) => new PhpSerializer()
- ]
- ]
-];
\ No newline at end of file
+];
diff --git a/config/autoload/swoole.local.php.dist b/config/autoload/swoole.local.php.dist
index 84262fe..7085ee0 100644
--- a/config/autoload/swoole.local.php.dist
+++ b/config/autoload/swoole.local.php.dist
@@ -1,9 +1,10 @@
[
- // Available in Swoole 4.1 and up; enables coroutine support
- // for most I/O operations:
+ // Available in Swoole 4.1 and up; enables coroutine support for most I/O operations:
'enable_coroutine' => true,
// Configure Swoole TCP Server:
@@ -13,17 +14,17 @@ return [
'mode' => SWOOLE_BASE, // SWOOLE_BASE or SWOOLE_PROCESS;
// SWOOLE_BASE is the default
'protocol' => SWOOLE_SOCK_TCP, // SWOOLE_SSL, // SSL-enable the server
- 'options' => [
+ 'options' => [
// Set the SSL certificate and key paths for SSL support:
//'ssl_cert_file' => 'path/to/ssl.crt',
//'ssl_key_file' => 'path/to/ssl.key',
- // Whether or not the HTTP server should use coroutines;
+ // Whether the HTTP server should use coroutines;
// enabled by default, and generally should not be disabled:
- 'package_eof' => "\n",
- 'open_eof_check' => true,
+ 'package_eof' => PHP_EOL,
+ 'open_eof_check' => true,
'open_length_check' => true,
- // in order to run swoole as daemon
+ // to run swoole as a daemon
'daemonize' => true,
// Overwrite the default location of the pid file;
diff --git a/daemon/messenger.service b/daemon/messenger.service
index f1dcea0..daa7fb2 100644
--- a/daemon/messenger.service
+++ b/daemon/messenger.service
@@ -12,4 +12,4 @@ WorkingDirectory=/home/dotkernel/queue/
ExecStart=/usr/bin/php /home/dotkernel/queue/bin/cli.php messenger:start
[Install]
-WantedBy=swoole.service
\ No newline at end of file
+WantedBy=swoole.service
diff --git a/daemon/swoole.service b/daemon/swoole.service
index 21ad0c2..b6d2c90 100644
--- a/daemon/swoole.service
+++ b/daemon/swoole.service
@@ -12,4 +12,4 @@ WorkingDirectory=/home/dotkernel/queue/
ExecStart=/usr/bin/php /home/dotkernel/queue/bin/cli.php swoole:start
[Install]
-WantedBy=multi-user.target
\ No newline at end of file
+WantedBy=multi-user.target
diff --git a/phpcs.xml b/phpcs.xml
index 1eddf9f..1d560a0 100644
--- a/phpcs.xml
+++ b/phpcs.xml
@@ -13,10 +13,10 @@
config
+ public
src
test
config/config.php
- config/routes.php
diff --git a/phpstan.neon b/phpstan.neon
index ed056f0..9da6dbc 100644
--- a/phpstan.neon
+++ b/phpstan.neon
@@ -4,6 +4,8 @@ includes:
parameters:
level: 5
paths:
+ - config
+ - public
- src
- test
treatPhpDocTypesAsCertain: false
diff --git a/public/index.php b/public/index.php
index 1c0c9bc..ee49413 100644
--- a/public/index.php
+++ b/public/index.php
@@ -2,6 +2,10 @@
declare(strict_types=1);
+use Mezzio\Application;
+use Mezzio\MiddlewareFactory;
+use Psr\Container\ContainerInterface;
+
// Delegate static file requests back to the PHP built-in webserver
if (PHP_SAPI === 'cli-server' && $_SERVER['SCRIPT_FILENAME'] !== __FILE__) {
return false;
@@ -14,17 +18,15 @@
* Self-called anonymous function that creates its own scope and keeps the global namespace clean.
*/
(function () {
- /** @var \Psr\Container\ContainerInterface $container */
+ /** @var ContainerInterface $container */
$container = require 'config/container.php';
- /** @var \Mezzio\Application $app */
- $app = $container->get(\Mezzio\Application::class);
- $factory = $container->get(\Mezzio\MiddlewareFactory::class);
+ /** @var Application $app */
+ $app = $container->get(Application::class);
+ $factory = $container->get(MiddlewareFactory::class);
- // Execute programmatic/declarative middleware pipeline and routing
- // configuration statements
+ // Execute programmatic/declarative middleware pipeline and routing configuration statements
(require 'config/pipeline.php')($app, $factory, $container);
- (require 'config/routes.php')($app, $factory, $container);
$app->run();
})();
diff --git a/src/App/ConfigProvider.php b/src/App/ConfigProvider.php
index 32641e6..8040eb4 100644
--- a/src/App/ConfigProvider.php
+++ b/src/App/ConfigProvider.php
@@ -19,7 +19,7 @@ class ConfigProvider
public function __invoke(): array
{
return [
- "dependencies" => $this->getDependencies(),
+ 'dependencies' => $this->getDependencies(),
'symfony' => [
'messenger' => [
'buses' => $this->busConfig(),
@@ -31,15 +31,15 @@ public function __invoke(): array
private function getDependencies(): array
{
return [
- "factories" => [
- "message_bus" => [MessageBusStaticFactory::class, "message_bus"],
- "message_bus_stamp_middleware" => [BusNameStampMiddlewareStaticFactory::class, "message_bus"],
- "message_bus_sender_middleware" => [MessageSenderMiddlewareStaticFactory::class, "message_bus"],
- "message_bus_handler_middleware" => [MessageHandlerMiddlewareStaticFactory::class, "message_bus"],
+ 'factories' => [
+ 'message_bus' => [MessageBusStaticFactory::class, 'message_bus'],
+ 'message_bus_stamp_middleware' => [BusNameStampMiddlewareStaticFactory::class, 'message_bus'],
+ 'message_bus_sender_middleware' => [MessageSenderMiddlewareStaticFactory::class, 'message_bus'],
+ 'message_bus_handler_middleware' => [MessageHandlerMiddlewareStaticFactory::class, 'message_bus'],
MessageHandler::class => AttributedServiceFactory::class,
],
- "aliases" => [
- MessageBusInterface::class => "message_bus",
+ 'aliases' => [
+ MessageBusInterface::class => 'message_bus',
],
];
}
@@ -47,7 +47,7 @@ private function getDependencies(): array
private function busConfig(): array
{
return [
- "message_bus" => [
+ 'message_bus' => [
// Means that it's an error if no handlers are defined for a given message
'allows_zero_handlers' => false,
@@ -58,7 +58,7 @@ private function busConfig(): array
*/
'middleware' => [
// … Middleware that inspects the message before it has been sent to a transport would go here.
- "message_bus_stamp_middleware",
+ 'message_bus_stamp_middleware',
'message_bus_sender_middleware', // Sends messages via a transport if configured.
'message_bus_handler_middleware', // Executes the handlers configured for the message
],
@@ -78,16 +78,16 @@ private function busConfig(): array
* Routes define which transport(s) that messages dispatched on this bus should be sent with.
*
* The * wildcard applies to all messages.
- * The transport for each route must be an array of one or more transport identifiers. Each transport
- * is retrieved from the DI container by this value.
+ * The transport for each route must be an array of one or more transport identifiers.
+ * This value retrieves each transport from the DI container.
*
* An empty routes definition would mean that messages would be handled immediately and synchronously,
- * i.e. no transport would be used.
+ * i.e., no transport would be used.
*
* Route specific messages to specific transports by using the message name as the key.
*/
'routes' => [
- Message::class => ["redis_transport"],
+ Message::class => ['redis_transport'],
],
],
];
diff --git a/src/App/Message/MessageHandler.php b/src/App/Message/MessageHandler.php
index 74d008b..36b6e1a 100644
--- a/src/App/Message/MessageHandler.php
+++ b/src/App/Message/MessageHandler.php
@@ -6,9 +6,11 @@
use Dot\DependencyInjection\Attribute\Inject;
use Dot\Log\Logger;
+use Exception;
use Symfony\Component\Messenger\Exception\ExceptionInterface;
use Symfony\Component\Messenger\MessageBusInterface;
use Symfony\Component\Messenger\Stamp\DelayStamp;
+use Throwable;
class MessageHandler
{
@@ -24,6 +26,9 @@ public function __construct(
) {
}
+ /**
+ * @throws ExceptionInterface
+ */
public function __invoke(Message $message): void
{
$payload = $message->getPayload();
@@ -35,9 +40,9 @@ public function __invoke(Message $message): void
if ($payload['foo'] === 'control') {
$this->logger->info($payload['foo'] . ': was processed successfully');
} else {
- throw new \Exception("Failed to execute");
+ throw new Exception('Failed to execute');
}
- } catch (\Throwable $exception) {
+ } catch (Throwable $exception) {
$this->logger->error($payload['foo'] . ' failed with message: '
. $exception->getMessage() . ' after ' . ($payload['retry'] ?? 0) . ' retries');
$this->retry($payload);
@@ -50,7 +55,7 @@ public function __invoke(Message $message): void
public function retry(array $payload): void
{
if (! isset($payload['retry'])) {
- $this->bus->dispatch(new Message(["foo" => $payload['foo'], 'retry' => 1]), [
+ $this->bus->dispatch(new Message(['foo' => $payload['foo'], 'retry' => 1]), [
new DelayStamp($this->config['fail-safe']['first_retry']),
]);
} else {
@@ -58,13 +63,13 @@ public function retry(array $payload): void
switch ($retry) {
case 1:
$delay = $this->config['fail-safe']['second_retry'];
- $this->bus->dispatch(new Message(["foo" => $payload['foo'], 'retry' => ++$retry]), [
+ $this->bus->dispatch(new Message(['foo' => $payload['foo'], 'retry' => ++$retry]), [
new DelayStamp($delay),
]);
break;
case 2:
$delay = $this->config['fail-safe']['third_retry'];
- $this->bus->dispatch(new Message(["foo" => $payload['foo'], 'retry' => ++$retry]), [
+ $this->bus->dispatch(new Message(['foo' => $payload['foo'], 'retry' => ++$retry]), [
new DelayStamp($delay),
]);
break;
diff --git a/src/Swoole/Command/Factory/StopCommandFactory.php b/src/Swoole/Command/Factory/StopCommandFactory.php
index d9d3de4..f21ee9f 100644
--- a/src/Swoole/Command/Factory/StopCommandFactory.php
+++ b/src/Swoole/Command/Factory/StopCommandFactory.php
@@ -4,12 +4,18 @@
namespace Queue\Swoole\Command\Factory;
+use Psr\Container\ContainerExceptionInterface;
use Psr\Container\ContainerInterface;
+use Psr\Container\NotFoundExceptionInterface;
use Queue\Swoole\Command\StopCommand;
use Queue\Swoole\PidManager;
class StopCommandFactory
{
+ /**
+ * @throws ContainerExceptionInterface
+ * @throws NotFoundExceptionInterface
+ */
public function __invoke(ContainerInterface $container): StopCommand
{
return new StopCommand($container->get(PidManager::class));
diff --git a/src/Swoole/Command/GetFailedMessagesCommand.php b/src/Swoole/Command/GetFailedMessagesCommand.php
index 83da758..02c6638 100644
--- a/src/Swoole/Command/GetFailedMessagesCommand.php
+++ b/src/Swoole/Command/GetFailedMessagesCommand.php
@@ -4,7 +4,10 @@
namespace Queue\Swoole\Command;
+use DateTime;
+use DateTimeImmutable;
use Dot\DependencyInjection\Attribute\Inject;
+use Exception;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
@@ -31,7 +34,7 @@ class GetFailedMessagesCommand extends Command
/** @var string $defaultName */
protected static $defaultName = 'failed';
- #[Inject()]
+ #[Inject]
public function __construct()
{
parent::__construct(self::$defaultName);
@@ -52,15 +55,15 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$endOption = $input->getOption('end');
$limit = $input->getOption('limit');
- $startDate = $startOption ? new \DateTimeImmutable($startOption) : null;
- $endDate = $endOption ? new \DateTimeImmutable($endOption) : null;
- } catch (\Exception $e) {
+ $startDate = $startOption ? new DateTimeImmutable($startOption) : null;
+ $endDate = $endOption ? new DateTimeImmutable($endOption) : null;
+ } catch (Exception) {
$output->writeln('Invalid date format provided.');
return Command::FAILURE;
}
if ($startDate && $startDate->format('H:i:s') === '00:00:00') {
- $startDate = $startDate->setTime(0, 0, 0);
+ $startDate = $startDate->setTime(0, 0);
}
if ($endDate && $endDate->format('H:i:s') === '00:00:00') {
@@ -69,14 +72,14 @@ protected function execute(InputInterface $input, OutputInterface $output): int
if ($limit && is_numeric($limit)) {
if ($startDate && ! $endDate) {
- $endDate = $startDate->modify("+{$limit} days");
+ $endDate = $startDate->modify("+$limit days");
} elseif (! $startDate && $endDate) {
- $startDate = $endDate->modify("-{$limit} days");
+ $startDate = $endDate->modify("-$limit days");
}
}
if (! $endDate) {
- $endDate = new \DateTime();
+ $endDate = new DateTime();
}
if ($startDate > $endDate) {
@@ -87,7 +90,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$logPath = dirname(__DIR__, 3) . '/log/queue-log.log';
if (! file_exists($logPath)) {
- $output->writeln("Log file not found: $logPath");
+ $output->writeln("Log file was not found: $logPath");
return Command::FAILURE;
}
diff --git a/src/Swoole/Command/GetProcessedMessagesCommand.php b/src/Swoole/Command/GetProcessedMessagesCommand.php
index 846dfe7..d92ff4b 100644
--- a/src/Swoole/Command/GetProcessedMessagesCommand.php
+++ b/src/Swoole/Command/GetProcessedMessagesCommand.php
@@ -4,7 +4,10 @@
namespace Queue\Swoole\Command;
+use DateTime;
+use DateTimeImmutable;
use Dot\DependencyInjection\Attribute\Inject;
+use Exception;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
@@ -31,7 +34,7 @@ class GetProcessedMessagesCommand extends Command
/** @var string $defaultName */
protected static $defaultName = 'processed';
- #[Inject()]
+ #[Inject]
public function __construct()
{
parent::__construct(self::$defaultName);
@@ -52,15 +55,15 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$endOption = $input->getOption('end');
$limit = $input->getOption('limit');
- $startDate = $startOption ? new \DateTimeImmutable($startOption) : null;
- $endDate = $endOption ? new \DateTimeImmutable($endOption) : null;
- } catch (\Exception $e) {
+ $startDate = $startOption ? new DateTimeImmutable($startOption) : null;
+ $endDate = $endOption ? new DateTimeImmutable($endOption) : null;
+ } catch (Exception) {
$output->writeln('Invalid date format provided.');
return Command::FAILURE;
}
if ($startDate && $startDate->format('H:i:s') === '00:00:00') {
- $startDate = $startDate->setTime(0, 0, 0);
+ $startDate = $startDate->setTime(0, 0);
}
if ($endDate && $endDate->format('H:i:s') === '00:00:00') {
@@ -69,14 +72,14 @@ protected function execute(InputInterface $input, OutputInterface $output): int
if ($limit && is_numeric($limit)) {
if ($startDate && ! $endDate) {
- $endDate = $startDate->modify("+{$limit} days");
+ $endDate = $startDate->modify("+$limit days");
} elseif (! $startDate && $endDate) {
- $startDate = $endDate->modify("-{$limit} days");
+ $startDate = $endDate->modify("-$limit days");
}
}
if (! $endDate) {
- $endDate = new \DateTime();
+ $endDate = new DateTime();
}
if ($startDate > $endDate) {
@@ -87,7 +90,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$logPath = dirname(__DIR__, 3) . '/log/queue-log.log';
if (! file_exists($logPath)) {
- $output->writeln("Log file not found: $logPath");
+ $output->writeln("Log file was not found: $logPath");
return Command::FAILURE;
}
diff --git a/src/Swoole/Command/IsRunningTrait.php b/src/Swoole/Command/IsRunningTrait.php
index 6e7966a..a7ceb02 100644
--- a/src/Swoole/Command/IsRunningTrait.php
+++ b/src/Swoole/Command/IsRunningTrait.php
@@ -9,7 +9,7 @@
trait IsRunningTrait
{
/**
- * Is the swoole server running?
+ * Is the Swoole server running?
*/
public function isRunning(): bool
{
diff --git a/src/Swoole/Command/StartCommand.php b/src/Swoole/Command/StartCommand.php
index 62ac0b2..c1eb1cd 100644
--- a/src/Swoole/Command/StartCommand.php
+++ b/src/Swoole/Command/StartCommand.php
@@ -4,7 +4,9 @@
namespace Queue\Swoole\Command;
+use Psr\Container\ContainerExceptionInterface;
use Psr\Container\ContainerInterface;
+use Psr\Container\NotFoundExceptionInterface;
use Queue\Swoole\PidManager;
use Swoole\Server as SwooleServer;
use Symfony\Component\Console\Attribute\AsCommand;
@@ -24,8 +26,6 @@ class StartCommand extends Command
public const DEFAULT_PROCESS_NAME = 'dotkernel-queue';
- public const DEFAULT_NUM_WORKERS = 4;
-
public const HELP = <<<'EOH'
Start the web server. If --daemonize is provided, starts the server as a
background process and returns handling to the shell; otherwise, the
@@ -35,8 +35,7 @@ class StartCommand extends Command
do not provide the option, 4 workers will be started.
EOH;
- /** @var ContainerInterface */
- private $container;
+ private ContainerInterface $container;
public function __construct(ContainerInterface $container, string $name = 'start')
{
@@ -51,6 +50,10 @@ protected function configure(): void
$this->setHelp(self::HELP);
}
+ /**
+ * @throws NotFoundExceptionInterface
+ * @throws ContainerExceptionInterface
+ */
protected function execute(InputInterface $input, OutputInterface $output): int
{
$this->pidManager = $this->container->get(PidManager::class);
@@ -61,8 +64,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$server = $this->container->get(SwooleServer::class);
$config = $this->container->get('config');
- $processName = $config['dotkernel-queue-swoole']['swoole-server']['process-name']
- ?? self::DEFAULT_PROCESS_NAME;
+ $processName = $config['dotkernel-queue-swoole']['swoole-server']['process-name'] ?? self::DEFAULT_PROCESS_NAME;
$pidManager = $this->pidManager;
diff --git a/src/Swoole/Command/StopCommand.php b/src/Swoole/Command/StopCommand.php
index 614b544..b64d2b3 100644
--- a/src/Swoole/Command/StopCommand.php
+++ b/src/Swoole/Command/StopCommand.php
@@ -28,22 +28,20 @@ class StopCommand extends Command
/**
* @internal
*
- * @var Closure Callable to execute when attempting to kill the server
- * process. Generally speaking, this is SwooleProcess::kill; only
- * change the value when testing.
+ * Callable to execute when attempting to kill the server process.
+ * Generally speaking, this is SwooleProcess::kill; only change the value when testing.
*/
- public $killProcess;
+ public Closure $killProcess;
/**
* @internal
*
- * @var int How long to wait for the server process to end. Only change
- * the value when testing.
+ * How long to wait for the server process to end.
+ * Only change the value when testing.
*/
- public $waitThreshold = 60;
+ public int $waitThreshold = 60;
- /** @var PidManager */
- private $pidManager;
+ private PidManager $pidManager;
public function __construct(PidManager $pidManager, string $name = 'stop')
{
diff --git a/src/Swoole/ConfigProvider.php b/src/Swoole/ConfigProvider.php
index a7976f8..4a30284 100644
--- a/src/Swoole/ConfigProvider.php
+++ b/src/Swoole/ConfigProvider.php
@@ -27,10 +27,10 @@ public function __invoke(): array
public function getDependencies(): array
{
return [
- "delegators" => [
+ 'delegators' => [
TCPSwooleServer::class => [TCPServerDelegator::class],
],
- "factories" => [
+ 'factories' => [
TCPSwooleServer::class => ServerFactory::class,
PidManager::class => PidManagerFactory::class,
StartCommand::class => StartCommandFactory::class,
@@ -39,7 +39,6 @@ public function getDependencies(): array
GetFailedMessagesCommand::class => AttributedServiceFactory::class,
GetQueuedMessagesCommand::class => AttributedServiceFactory::class,
],
- "aliases" => [],
];
}
}
diff --git a/src/Swoole/Delegators/TCPServerDelegator.php b/src/Swoole/Delegators/TCPServerDelegator.php
index 3b9b67a..5bcbf84 100644
--- a/src/Swoole/Delegators/TCPServerDelegator.php
+++ b/src/Swoole/Delegators/TCPServerDelegator.php
@@ -4,7 +4,9 @@
namespace Queue\Swoole\Delegators;
+use Psr\Container\ContainerExceptionInterface;
use Psr\Container\ContainerInterface;
+use Psr\Container\NotFoundExceptionInterface;
use Queue\App\Message\Message;
use Queue\Swoole\Command\GetFailedMessagesCommand;
use Queue\Swoole\Command\GetProcessedMessagesCommand;
@@ -15,6 +17,7 @@
use Symfony\Component\Console\Output\BufferedOutput;
use Symfony\Component\Messenger\MessageBusInterface;
use Symfony\Component\Messenger\Stamp\DelayStamp;
+use Throwable;
use function array_merge;
use function array_shift;
@@ -23,8 +26,14 @@
use function str_starts_with;
use function trim;
+use const PHP_EOL;
+
class TCPServerDelegator
{
+ /**
+ * @throws ContainerExceptionInterface
+ * @throws NotFoundExceptionInterface
+ */
public function __invoke(ContainerInterface $container, string $serviceName, callable $callback): TCPSwooleServer
{
/** @var TCPSwooleServer $server */
@@ -33,22 +42,20 @@ public function __invoke(ContainerInterface $container, string $serviceName, cal
/** @var MessageBusInterface $bus */
$bus = $container->get(MessageBusInterface::class);
- $logger = $container->get("dot-log.queue-log");
-
- $commandMap = [
- 'processed' => GetProcessedMessagesCommand::class,
- 'failed' => GetFailedMessagesCommand::class,
- 'inventory' => GetQueuedMessagesCommand::class,
- ];
+ $logger = $container->get('dot-log.queue-log');
- $server->on('Connect', function ($server, $fd) {
- echo "Client: Connect.\n";
+ $server->on('connect', function (TCPSwooleServer $server, int $fd) {
+ echo 'Client: Connect.' . PHP_EOL;
});
- $server->on('receive', function ($server, $fd, $fromId, $data) use ($logger, $bus, $commandMap, $container) {
- $message = trim($data);
- $response = '';
+ $server->on('receive', function ($server, $fd, $fromId, $data) use ($logger, $bus, $container) {
+ $commandMap = [
+ 'processed' => GetProcessedMessagesCommand::class,
+ 'failed' => GetFailedMessagesCommand::class,
+ 'inventory' => GetQueuedMessagesCommand::class,
+ ];
+ $message = trim($data);
$args = explode(' ', $message);
$commandName = array_shift($args);
@@ -75,16 +82,16 @@ public function __invoke(ContainerInterface $container, string $serviceName, cal
$application->run($input, $output);
$response = $output->fetch();
$server->send($fd, $response);
- } catch (\Throwable $e) {
- $logger->error("Error running command: " . $e->getMessage());
+ } catch (Throwable $e) {
+ $logger->error('Error running command: ' . $e->getMessage());
}
} else {
- $bus->dispatch(new Message(["foo" => $message]));
- $bus->dispatch(new Message(["foo" => "with 5 seconds delay"]), [
+ $bus->dispatch(new Message(['foo' => $message]));
+ $bus->dispatch(new Message(['foo' => 'with 5 seconds delay']), [
new DelayStamp(5000),
]);
- $logger->notice("TCP request received", [
+ $logger->notice('TCP request received', [
'fd' => $fd,
'from_id' => $fromId,
'data' => $data,
@@ -92,8 +99,8 @@ public function __invoke(ContainerInterface $container, string $serviceName, cal
}
});
- $server->on('Close', function ($server, $fd) {
- echo "Client: Close.\n";
+ $server->on('close', function (TCPSwooleServer $server, int $fd) {
+ echo 'Client: Close.' . PHP_EOL;
});
return $server;
diff --git a/src/Swoole/PidManager.php b/src/Swoole/PidManager.php
index b21c1ab..427d529 100644
--- a/src/Swoole/PidManager.php
+++ b/src/Swoole/PidManager.php
@@ -22,7 +22,7 @@ public function __construct(private string $pidFile)
}
/**
- * Write master pid and manager pid to pid file
+ * Write master pid and manager pid to a pid file
*
* @throws RuntimeException When $pidFile is not writable.
*/
@@ -36,7 +36,7 @@ public function write(int $masterPid, int $managerPid): void
}
/**
- * Read master pid and manager pid from pid file
+ * Read master pid and manager pid from a pid file
*
* @return string[] Array with master and manager PID values as strings
*/
diff --git a/src/Swoole/PidManagerFactory.php b/src/Swoole/PidManagerFactory.php
index 9036816..8c06498 100644
--- a/src/Swoole/PidManagerFactory.php
+++ b/src/Swoole/PidManagerFactory.php
@@ -4,15 +4,22 @@
namespace Queue\Swoole;
+use Psr\Container\ContainerExceptionInterface;
use Psr\Container\ContainerInterface;
+use Psr\Container\NotFoundExceptionInterface;
use function sys_get_temp_dir;
class PidManagerFactory
{
+ /**
+ * @throws ContainerExceptionInterface
+ * @throws NotFoundExceptionInterface
+ */
public function __invoke(ContainerInterface $container): PidManager
{
$config = $container->get('config');
+
return new PidManager(
$config['dotkernel-queue-swoole']['swoole-tcp-server']['options']['pid_file']
?? sys_get_temp_dir() . '/dotkernel-queue-swoole.pid'
diff --git a/src/Swoole/ServerFactory.php b/src/Swoole/ServerFactory.php
index 63354c1..5f7a59f 100644
--- a/src/Swoole/ServerFactory.php
+++ b/src/Swoole/ServerFactory.php
@@ -5,7 +5,10 @@
namespace Queue\Swoole;
use ArrayAccess;
+use ErrorException;
+use Psr\Container\ContainerExceptionInterface;
use Psr\Container\ContainerInterface;
+use Psr\Container\NotFoundExceptionInterface;
use Queue\Swoole\Exception\InvalidArgumentException;
use Swoole\Runtime as SwooleRuntime;
use Swoole\Server as SwooleServer;
@@ -66,10 +69,12 @@ class ServerFactory
* @see https://www.swoole.co.uk/docs/modules/swoole-server-methods#swoole_server-__construct
* @see https://www.swoole.co.uk/docs/modules/swoole-server/predefined-constants for $mode and $protocol constant
*
+ * @throws ContainerExceptionInterface
+ * @throws ErrorException
* @throws InvalidArgumentException For invalid $port values.
* @throws InvalidArgumentException For invalid $mode values.
* @throws InvalidArgumentException For invalid $protocol values.
- * @throws \ErrorException
+ * @throws NotFoundExceptionInterface
*/
public function __invoke(ContainerInterface $container): SwooleServer
{
diff --git a/test/App/Message/MessageHandlerTest.php b/test/App/Message/MessageHandlerTest.php
index 9e50151..82453cf 100644
--- a/test/App/Message/MessageHandlerTest.php
+++ b/test/App/Message/MessageHandlerTest.php
@@ -16,11 +16,11 @@
use Symfony\Component\Messenger\MessageBusInterface;
use Symfony\Component\Messenger\Stamp\DelayStamp;
+use const PHP_EOL;
+
class MessageHandlerTest extends TestCase
{
private MessageBusInterface|MockObject $bus;
- private Logger $logger;
- private array $config;
private MessageHandler $handler;
/**
@@ -29,8 +29,9 @@ class MessageHandlerTest extends TestCase
*/
protected function setUp(): void
{
- $this->bus = $this->createMock(MessageBusInterface::class);
- $this->logger = new Logger([
+ $this->bus = $this->createMock(MessageBusInterface::class);
+
+ $logger = new Logger([
'writers' => [
'FileWriter' => [
'name' => 'null',
@@ -38,7 +39,7 @@ protected function setUp(): void
],
],
]);
- $this->config = [
+ $config = [
'fail-safe' => [
'first_retry' => 1000,
'second_retry' => 2000,
@@ -49,7 +50,7 @@ protected function setUp(): void
'protocol' => 'tcp',
'host' => 'localhost',
'port' => '8556',
- 'eof' => "\n",
+ 'eof' => PHP_EOL,
],
],
'application' => [
@@ -57,11 +58,12 @@ protected function setUp(): void
],
];
- $this->handler = new MessageHandler($this->bus, $this->logger, $this->config);
+ $this->handler = new MessageHandler($this->bus, $logger, $config);
}
/**
* @throws Exception
+ * @throws ExceptionInterface
*/
public function testInvokeSuccessfulProcessing(): void
{
@@ -76,6 +78,7 @@ public function testInvokeSuccessfulProcessing(): void
/**
* @throws Exception
+ * @throws ExceptionInterface
*/
public function testInvokeFailureTriggersFirstRetry(): void
{
diff --git a/test/App/Message/MessageTest.php b/test/App/Message/MessageTest.php
index bf98557..8c733a6 100644
--- a/test/App/Message/MessageTest.php
+++ b/test/App/Message/MessageTest.php
@@ -11,7 +11,7 @@ class MessageTest extends TestCase
{
public function testMessageAccessors(): void
{
- $admin = new Message(["payload" => "test message payload"]);
- $this->assertSame(["payload" => "test message payload"], $admin->getPayload());
+ $admin = new Message(['payload' => 'test message payload']);
+ $this->assertSame(['payload' => 'test message payload'], $admin->getPayload());
}
}
diff --git a/test/Swoole/Command/GetDataFromLogsCommandTest.php b/test/Swoole/Command/GetDataFromLogsCommandTest.php
index 92803d2..14f5e99 100644
--- a/test/Swoole/Command/GetDataFromLogsCommandTest.php
+++ b/test/Swoole/Command/GetDataFromLogsCommandTest.php
@@ -4,7 +4,9 @@
namespace QueueTest\Swoole\Command;
+use DateTime;
use DateTimeImmutable;
+use Exception;
use PHPUnit\Framework\TestCase;
use Queue\Swoole\Command\GetFailedMessagesCommand;
use Queue\Swoole\Command\GetProcessedMessagesCommand;
@@ -25,15 +27,15 @@
class GetDataFromLogsCommandTest extends TestCase
{
- private string $logDir;
private string $logPath;
protected function setUp(): void
{
- $this->logDir = dirname(__DIR__, 3) . '/log';
- $this->logPath = $this->logDir . '/queue-log.log';
- if (! is_dir($this->logDir)) {
- mkdir($this->logDir, 0777, true);
+ $logDir = dirname(__DIR__, 3) . '/log';
+
+ $this->logPath = $logDir . '/queue-log.log';
+ if (! is_dir($logDir)) {
+ mkdir($logDir, 0777, true);
}
file_put_contents($this->logPath, '');
}
@@ -92,7 +94,7 @@ public function testMissingLogFile(string $commandClass): void
$exit = $command->run($input, $output);
$this->assertEquals(Command::FAILURE, $exit);
- $this->assertStringContainsString('Log file not found', $output->fetch());
+ $this->assertStringContainsString('Log file was not found', $output->fetch());
}
/**
@@ -120,7 +122,7 @@ public function testNoMatchingEntries(string $commandClass): void
*/
public function testMalformedLogLineIgnored(string $commandClass): void
{
- file_put_contents($this->logPath, "not-a-json\n");
+ file_put_contents($this->logPath, 'not-a-json' . PHP_EOL);
$command = new $commandClass();
$input = new ArrayInput([]);
@@ -139,7 +141,7 @@ public function testMatchEntryOutput(string $commandClass, string $expectedLevel
{
$line = json_encode([
'levelName' => $expectedLevel,
- 'timestamp' => (new \DateTime())->format('Y-m-d H:i:s'),
+ 'timestamp' => (new DateTime())->format('Y-m-d H:i:s'),
'message' => 'Message here',
]);
file_put_contents($this->logPath, $line . PHP_EOL);
@@ -155,8 +157,8 @@ public function testMatchEntryOutput(string $commandClass, string $expectedLevel
}
/**
+ * @throws Exception
* @throws ExceptionInterface
- * @throws \DateMalformedStringException
*/
public function testLimitAddsDaysToStartDateOnly(): void
{
@@ -170,7 +172,7 @@ public function testLimitAddsDaysToStartDateOnly(): void
]);
$output = new BufferedOutput();
- $logDate = (new DateTimeImmutable($start))->modify("+{$limit} days")->format('Y-m-d H:i:s');
+ $logDate = (new DateTimeImmutable($start))->modify("+$limit days")->format('Y-m-d H:i:s');
file_put_contents($this->logPath, json_encode([
'levelName' => 'info',
diff --git a/test/Swoole/Command/GetQueuedMessagesCommandTest.php b/test/Swoole/Command/GetQueuedMessagesCommandTest.php
index a8edf4b..969842a 100644
--- a/test/Swoole/Command/GetQueuedMessagesCommandTest.php
+++ b/test/Swoole/Command/GetQueuedMessagesCommandTest.php
@@ -30,6 +30,9 @@ protected function setUp(): void
$this->redisMock = $this->createMock(Redis::class);
}
+ /**
+ * @throws ExceptionInterface
+ */
public function testExecuteWithNoMessages(): void
{
$this->redisMock
@@ -75,7 +78,7 @@ public function testExecuteWithMessages(): void
$this->assertEquals(Command::SUCCESS, $exitCode);
foreach (array_keys($fakeMessages) as $id) {
- $this->assertStringContainsString("Message ID:", $outputText);
+ $this->assertStringContainsString('Message ID:', $outputText);
$this->assertStringContainsString($id, $outputText);
}
@@ -91,7 +94,7 @@ public function testRedisThrowsException(): void
$this->redisMock
->expects($this->once())
->method('xRange')
- ->willThrowException(new RedisException("Redis unavailable"));
+ ->willThrowException(new RedisException('Redis unavailable'));
$command = new GetQueuedMessagesCommand($this->redisMock);
$input = new ArrayInput([]);
diff --git a/test/Swoole/Command/StopCommandTest.php b/test/Swoole/Command/StopCommandTest.php
index dbdcca2..4e0dd12 100644
--- a/test/Swoole/Command/StopCommandTest.php
+++ b/test/Swoole/Command/StopCommandTest.php
@@ -49,9 +49,7 @@ public function testExecuteWhenServerStopsSuccessfully(): void
$command->method('isRunning')->willReturn(true);
- $command->killProcess = function (int $pid, ?int $signal = null): bool {
- return true;
- };
+ $command->killProcess = fn (): bool => true;
$tester = new CommandTester($command);
$exitCode = $tester->execute([]);
diff --git a/test/Swoole/Delegators/TCPServerDelegatorTest.php b/test/Swoole/Delegators/TCPServerDelegatorTest.php
index dd90db2..15d690e 100644
--- a/test/Swoole/Delegators/TCPServerDelegatorTest.php
+++ b/test/Swoole/Delegators/TCPServerDelegatorTest.php
@@ -10,12 +10,15 @@
use PHPUnit\Framework\TestCase;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\ContainerInterface;
+use Psr\Container\NotFoundExceptionInterface;
use Queue\App\Message\Message;
use Queue\Swoole\Command\GetProcessedMessagesCommand;
use Queue\Swoole\Delegators\TCPServerDelegator;
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\MessageBusInterface;
+use const PHP_EOL;
+
class TCPServerDelegatorTest extends TestCase
{
private Logger $logger;
@@ -43,6 +46,10 @@ protected function setUp(): void
$this->server = new DummySwooleServer();
}
+ /**
+ * @throws ContainerExceptionInterface
+ * @throws NotFoundExceptionInterface
+ */
public function testCallbacksAreRegistered(): void
{
$callback = fn() => $this->server;
@@ -56,15 +63,19 @@ public function testCallbacksAreRegistered(): void
$result = $delegator($this->container, 'tcp-server', $callback);
$this->assertSame($this->server, $result);
- $this->assertArrayHasKey('Connect', $this->server->callbacks);
+ $this->assertArrayHasKey('connect', $this->server->callbacks);
$this->assertArrayHasKey('receive', $this->server->callbacks);
- $this->assertArrayHasKey('Close', $this->server->callbacks);
+ $this->assertArrayHasKey('close', $this->server->callbacks);
- foreach (['Connect', 'receive', 'Close'] as $event) {
+ foreach (['connect', 'receive', 'close'] as $event) {
$this->assertIsCallable($this->server->callbacks[$event]);
}
}
+ /**
+ * @throws ContainerExceptionInterface
+ * @throws NotFoundExceptionInterface
+ */
public function testConnectOutputsExpectedString(): void
{
$callback = fn() => $this->server;
@@ -77,12 +88,16 @@ public function testConnectOutputsExpectedString(): void
$delegator = new TCPServerDelegator();
$delegator($this->container, 'tcp-server', $callback);
- $this->expectOutputString("Client: Connect.\n");
+ $this->expectOutputString('Client: Connect.' . PHP_EOL);
- $connectCb = $this->server->callbacks['Connect'];
+ $connectCb = $this->server->callbacks['connect'];
$connectCb($this->server, 1);
}
+ /**
+ * @throws ContainerExceptionInterface
+ * @throws NotFoundExceptionInterface
+ */
public function testCloseOutputsExpectedString(): void
{
$callback = fn() => $this->server;
@@ -95,12 +110,16 @@ public function testCloseOutputsExpectedString(): void
$delegator = new TCPServerDelegator();
$delegator($this->container, 'tcp-server', $callback);
- $this->expectOutputString("Client: Close.\n");
+ $this->expectOutputString('Client: Close.' . PHP_EOL);
- $closeCb = $this->server->callbacks['Close'];
+ $closeCb = $this->server->callbacks['close'];
$closeCb($this->server, 1);
}
+ /**
+ * @throws ContainerExceptionInterface
+ * @throws NotFoundExceptionInterface
+ */
public function testReceiveDispatchesMessagesAndLogsWhenUnknownCommand(): void
{
$callback = fn() => $this->server;
@@ -137,6 +156,10 @@ public function testReceiveDispatchesMessagesAndLogsWhenUnknownCommand(): void
$receiveCb($this->server, 42, 5, "hello");
}
+ /**
+ * @throws ContainerExceptionInterface
+ * @throws NotFoundExceptionInterface
+ */
public function testReceiveExecutesKnownCommandSuccessfully(): void
{
$callback = fn() => $this->server;
@@ -182,11 +205,14 @@ public function send($fd, $data, $serverSocket = -1): bool
$this->assertStringContainsString('processed output text', $this->server->sentData);
}
+ /**
+ * @throws ContainerExceptionInterface
+ * @throws NotFoundExceptionInterface
+ */
public function testReceiveParsesKnownOptions(): void
{
$callback = fn() => $this->server;
- $sentData = null;
$this->server = new class extends DummySwooleServer {
public ?string $sentData = null;
diff --git a/test/Swoole/Exception/InvalidStaticResourceMiddlewareExceptionTest.php b/test/Swoole/Exception/InvalidStaticResourceMiddlewareExceptionTest.php
index 461edda..35cf1f9 100644
--- a/test/Swoole/Exception/InvalidStaticResourceMiddlewareExceptionTest.php
+++ b/test/Swoole/Exception/InvalidStaticResourceMiddlewareExceptionTest.php
@@ -6,6 +6,7 @@
use PHPUnit\Framework\TestCase;
use Queue\Swoole\Exception\InvalidStaticResourceMiddlewareException;
+use stdClass;
use function get_debug_type;
use function sprintf;
@@ -14,7 +15,7 @@ class InvalidStaticResourceMiddlewareExceptionTest extends TestCase
{
public function testForMiddlewareAtPositionReturnsExpectedException(): void
{
- $middleware = new \stdClass();
+ $middleware = new stdClass();
$position = 2;
$exception = InvalidStaticResourceMiddlewareException::forMiddlewareAtPosition($middleware, $position);
diff --git a/test/Swoole/PidManagerFactoryTest.php b/test/Swoole/PidManagerFactoryTest.php
index b5e52db..39f78b4 100644
--- a/test/Swoole/PidManagerFactoryTest.php
+++ b/test/Swoole/PidManagerFactoryTest.php
@@ -4,17 +4,20 @@
namespace QueueTest\Swoole;
-use PHPUnit\Framework\MockObject\Exception;
use PHPUnit\Framework\TestCase;
+use Psr\Container\ContainerExceptionInterface;
use Psr\Container\ContainerInterface;
+use Psr\Container\NotFoundExceptionInterface;
use Queue\Swoole\PidManagerFactory;
+use ReflectionClass;
use ReflectionException;
final class PidManagerFactoryTest extends TestCase
{
/**
- * @throws Exception
+ * @throws ContainerExceptionInterface
* @throws ReflectionException
+ * @throws NotFoundExceptionInterface
*/
public function testCreatesPidManagerWithConfiguredPidFile(): void
{
@@ -47,7 +50,7 @@ public function testCreatesPidManagerWithConfiguredPidFile(): void
*/
private function getPrivateProperty(object $object): mixed
{
- $reflection = new \ReflectionClass($object);
+ $reflection = new ReflectionClass($object);
$property = $reflection->getProperty('pidFile');
return $property->getValue($object);
}
diff --git a/test/Swoole/ServerFactoryTest.php b/test/Swoole/ServerFactoryTest.php
index a1346f5..618c86c 100644
--- a/test/Swoole/ServerFactoryTest.php
+++ b/test/Swoole/ServerFactoryTest.php
@@ -7,9 +7,10 @@
use ErrorException;
use InvalidArgumentException;
use PHPUnit\Framework\Attributes\RunInSeparateProcess;
-use PHPUnit\Framework\MockObject\Exception;
use PHPUnit\Framework\TestCase;
+use Psr\Container\ContainerExceptionInterface;
use Psr\Container\ContainerInterface;
+use Psr\Container\NotFoundExceptionInterface;
use Queue\Swoole\ServerFactory;
use Swoole\Server;
@@ -28,7 +29,9 @@ protected function setUp(): void
}
/**
- * @throws Exception|ErrorException
+ * @throws ContainerExceptionInterface
+ * @throws NotFoundExceptionInterface
+ * @throws ErrorException
*/
#[RunInSeparateProcess]
public function testInvokeWithMinimalValidConfig(): void
@@ -48,7 +51,8 @@ public function testInvokeWithMinimalValidConfig(): void
}
/**
- * @throws Exception
+ * @throws ContainerExceptionInterface
+ * @throws NotFoundExceptionInterface
* @throws ErrorException
*/
#[RunInSeparateProcess]
@@ -78,7 +82,8 @@ public function testInvokeWithCustomValidConfig(): void
}
/**
- * @throws Exception
+ * @throws ContainerExceptionInterface
+ * @throws NotFoundExceptionInterface
* @throws ErrorException
*/
public function testThrowsOnInvalidPort(): void
@@ -101,7 +106,9 @@ public function testThrowsOnInvalidPort(): void
}
/**
- * @throws Exception|ErrorException
+ * @throws ContainerExceptionInterface
+ * @throws NotFoundExceptionInterface
+ * @throws ErrorException
*/
public function testThrowsOnInvalidMode(): void
{
@@ -123,7 +130,8 @@ public function testThrowsOnInvalidMode(): void
}
/**
- * @throws Exception
+ * @throws ContainerExceptionInterface
+ * @throws NotFoundExceptionInterface
* @throws ErrorException
*/
public function testThrowsOnInvalidProtocol(): void