You've already forked Php-Template
158 lines
4.5 KiB
PHP
158 lines
4.5 KiB
PHP
<?php
|
|
|
|
declare(ticks=1);
|
|
|
|
namespace Siteworxpro\App\Async;
|
|
|
|
use Siteworxpro\App\Annotations\Async\HandlesMessage;
|
|
use Siteworxpro\App\Async\Queues\Queue;
|
|
use Siteworxpro\App\Services\Facades\Logger;
|
|
|
|
class Consumer
|
|
{
|
|
private static bool $shutDown = false;
|
|
|
|
private const array QUEUES = [
|
|
'default' => Queues\DefaultQueue::class,
|
|
];
|
|
|
|
private array $queues = [];
|
|
|
|
private array $handlers = [];
|
|
|
|
private const string HANDLER_NAMESPACE = 'Siteworxpro\\App\\Async\\Handlers\\';
|
|
|
|
public function __construct(array $queues = [])
|
|
{
|
|
if ($queues === []) {
|
|
$queues = self::QUEUES;
|
|
} else {
|
|
$mappedQueues = [];
|
|
foreach ($queues as $queueName) {
|
|
if (isset(self::QUEUES[$queueName])) {
|
|
$mappedQueues[] = self::QUEUES[$queueName];
|
|
} else {
|
|
throw new \InvalidArgumentException("Queue '$queueName' is not defined.");
|
|
}
|
|
}
|
|
$queues = $mappedQueues;
|
|
}
|
|
|
|
|
|
foreach ($queues as $queueClass) {
|
|
$this->queues[] = new $queueClass();
|
|
}
|
|
|
|
$this->registerHandlers();
|
|
}
|
|
|
|
private function registerHandlers(): void
|
|
{
|
|
$recursiveIterator = new \RecursiveIteratorIterator(
|
|
new \RecursiveDirectoryIterator(__DIR__ . '/Handlers/')
|
|
);
|
|
|
|
foreach ($recursiveIterator as $file) {
|
|
if ($file->isFile() && $file->getExtension() === 'php') {
|
|
$relativePath = str_replace(__DIR__ . '/Handlers/', '', $file->getPathname());
|
|
$className = self::HANDLER_NAMESPACE . str_replace('/', '\\', substr($relativePath, 0, -4));
|
|
|
|
|
|
if (class_exists($className)) {
|
|
$reflection = new \ReflectionClass($className);
|
|
$attributes = $reflection->getAttributes(HandlesMessage::class);
|
|
foreach ($attributes as $attribute) {
|
|
$instance = $attribute->newInstance();
|
|
$messageClass = $instance->getMessageClass();
|
|
$this->handlers[$messageClass][] = $className;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param $signal
|
|
*/
|
|
public static function handleSignal($signal): void
|
|
{
|
|
switch ($signal) {
|
|
// Graceful
|
|
case SIGINT:
|
|
case SIGTERM:
|
|
case SIGHUP:
|
|
self::$shutDown = true;
|
|
|
|
break;
|
|
|
|
// Not Graceful
|
|
case SIGKILL:
|
|
exit(9);
|
|
}
|
|
}
|
|
|
|
private function shouldShutDown(): bool
|
|
{
|
|
return self::$shutDown;
|
|
}
|
|
|
|
public function start(): void
|
|
{
|
|
if (!\function_exists('pcntl_signal')) {
|
|
throw new \RuntimeException('The pcntl extension is required to handle signals.');
|
|
}
|
|
|
|
Logger::info('Starting queue consumer...');
|
|
|
|
\pcntl_signal(SIGINT, [self::class, 'handleSignal']);
|
|
\pcntl_signal(SIGTERM, [self::class, 'handleSignal']);
|
|
\pcntl_signal(SIGHUP, [self::class, 'handleSignal']);
|
|
|
|
while (true) {
|
|
if ($this->shouldShutDown()) {
|
|
Logger::info('Shutting down queue consumer...');
|
|
break;
|
|
}
|
|
|
|
/** @var Queue $queue */
|
|
foreach ($this->queues as $queue) {
|
|
Logger::info('Listening to queue: ' . $queue->queueName());
|
|
$message = $queue->pop();
|
|
if ($message) {
|
|
Logger::info('Processing message of type: ' . get_class($message));
|
|
|
|
$handlers = $this->getHandlerForMessage($message);
|
|
|
|
foreach ($handlers as $handler) {
|
|
$handler($message);
|
|
}
|
|
}
|
|
}
|
|
|
|
sleep(1);
|
|
}
|
|
}
|
|
|
|
private function getHandlerForMessage($message): array
|
|
{
|
|
$callables = [];
|
|
|
|
$messageClass = get_class($message);
|
|
if (isset($this->handlers[$messageClass])) {
|
|
$handlerClasses = $this->handlers[$messageClass];
|
|
|
|
foreach ($handlerClasses as $handlerClass) {
|
|
if (class_exists($handlerClass)) {
|
|
$handlerInstance = new $handlerClass();
|
|
|
|
$callables[] = $handlerInstance;
|
|
}
|
|
}
|
|
|
|
return $callables;
|
|
}
|
|
|
|
throw new \RuntimeException("No handler found for message class: $messageClass");
|
|
}
|
|
}
|