diff --git a/src/Controller/Admin/QueueController.php b/src/Controller/Admin/QueueController.php index fd7bb282..10c201cf 100644 --- a/src/Controller/Admin/QueueController.php +++ b/src/Controller/Admin/QueueController.php @@ -9,6 +9,7 @@ use Queue\Queue\AddFromBackendInterface; use Queue\Queue\AddInterface; use Queue\Queue\TaskFinder; +use Queue\Queue\TaskMetadata; /** * @property \Queue\Model\Table\QueuedJobsTable $QueuedJobs @@ -66,9 +67,7 @@ public function index() { $taskDescriptions = []; foreach ($tasks as $task => $className) { - /** @var \Queue\Queue\Task $taskObject */ - $taskObject = new $className(); - $taskDescriptions[$task] = $taskObject->description(); + $taskDescriptions[$task] = TaskMetadata::fromClass($className)->description; } $servers = $QueueProcesses->serverList(); @@ -132,8 +131,8 @@ public function addJob() { throw new NotFoundException('Class not found for job `' . $job . '`'); } - $object = new $className(); - if ($object instanceof AddInterface) { + if (is_subclass_of($className, AddInterface::class)) { + $object = new $className(); $object->add(null); } else { $this->QueuedJobs->createJob($job); diff --git a/src/Queue/Config.php b/src/Queue/Config.php index 3bc3d32c..e72ec457 100644 --- a/src/Queue/Config.php +++ b/src/Queue/Config.php @@ -120,7 +120,7 @@ public static function ignoredTasks(): array { } /** - * @param array $tasks + * @param array> $tasks * * @throws \RuntimeException * @@ -134,13 +134,12 @@ public static function taskConfig(array $tasks): array { foreach ($tasks as $task => $className) { [$pluginName, $taskName] = pluginSplit($task); - /** @var \Queue\Queue\Task $taskObject */ - $taskObject = new $className(); + $taskMeta = TaskMetadata::fromClass($className); // Get task-specific config overrides from Configure $taskConfig = $taskOverrides[$task] ?? []; - $taskTimeout = $taskConfig['timeout'] ?? $taskObject->timeout ?? $defaultTimeout; + $taskTimeout = $taskConfig['timeout'] ?? $taskMeta->timeout ?? $defaultTimeout; // Auto-cap task timeout to defaultRequeueTimeout to prevent duplicate execution if ($taskTimeout > $defaultTimeout) { @@ -151,12 +150,10 @@ public static function taskConfig(array $tasks): array { $config[$task]['name'] = $taskName; $config[$task]['plugin'] = $pluginName; $config[$task]['timeout'] = $taskTimeout; - $config[$task]['retries'] = $taskConfig['retries'] ?? $taskObject->retries ?? static::defaultworkerretries(); - $config[$task]['rate'] = $taskConfig['rate'] ?? $taskObject->rate; - $config[$task]['costs'] = $taskConfig['costs'] ?? $taskObject->costs; - $config[$task]['unique'] = $taskConfig['unique'] ?? $taskObject->unique; - - unset($taskObject); + $config[$task]['retries'] = $taskConfig['retries'] ?? $taskMeta->retries ?? static::defaultworkerretries(); + $config[$task]['rate'] = $taskConfig['rate'] ?? $taskMeta->rate; + $config[$task]['costs'] = $taskConfig['costs'] ?? $taskMeta->costs; + $config[$task]['unique'] = $taskConfig['unique'] ?? $taskMeta->unique; } return $config; diff --git a/src/Queue/TaskMetadata.php b/src/Queue/TaskMetadata.php new file mode 100644 index 00000000..d0d87267 --- /dev/null +++ b/src/Queue/TaskMetadata.php @@ -0,0 +1,78 @@ + $className + * + * @param string $className + * + * @return self + */ + public static function fromClass(string $className): self { + $reflection = new ReflectionClass($className); + + $timeout = static::propertyDefault($reflection, 'timeout'); + $retries = static::propertyDefault($reflection, 'retries'); + $rate = static::propertyDefault($reflection, 'rate') ?? 0; + $costs = static::propertyDefault($reflection, 'costs') ?? 0; + $unique = static::propertyDefault($reflection, 'unique') ?? false; + + $description = null; + try { + /** @var \Queue\Queue\Task $instance */ + $instance = $reflection->newInstanceWithoutConstructor(); + $description = $instance->description(); + } catch (Throwable) { + } + + return new self($className, $timeout, $retries, $rate, $costs, $unique, $description); + } + + /** + * @param \ReflectionClass<\Queue\Queue\Task> $reflection + * @param string $property + * + * @return mixed + */ + protected static function propertyDefault(ReflectionClass $reflection, string $property): mixed { + if (!$reflection->hasProperty($property)) { + return null; + } + + $prop = $reflection->getProperty($property); + if (!$prop->hasDefaultValue()) { + return null; + } + + return $prop->getDefaultValue(); + } + +} diff --git a/tests/TestCase/Command/InfoCommandTest.php b/tests/TestCase/Command/InfoCommandTest.php index cf512562..b0e1bd0d 100644 --- a/tests/TestCase/Command/InfoCommandTest.php +++ b/tests/TestCase/Command/InfoCommandTest.php @@ -38,7 +38,7 @@ public function testExecute(): void { $this->exec('queue info'); $output = $this->_out->output(); - $this->assertStringContainsString('16 tasks available:', $output); + $this->assertStringContainsString('17 tasks available:', $output); $this->assertExitCode(0); } diff --git a/tests/TestCase/Queue/TaskMetadataTest.php b/tests/TestCase/Queue/TaskMetadataTest.php new file mode 100644 index 00000000..551456e1 --- /dev/null +++ b/tests/TestCase/Queue/TaskMetadataTest.php @@ -0,0 +1,57 @@ +assertSame(MetadataTask::class, $meta->class); + $this->assertSame(42, $meta->timeout); + $this->assertSame(3, $meta->retries); + $this->assertSame(5, $meta->rate); + $this->assertSame(75, $meta->costs); + $this->assertTrue($meta->unique); + } + + /** + * Test that fromClass reads description() from tasks that override it. + * + * @return void + */ + public function testFromClassReadsDescription(): void { + $meta = TaskMetadata::fromClass(MetadataTask::class); + + $this->assertSame('A task for testing metadata introspection', $meta->description); + } + + /** + * Test that fromClass works for tasks with required constructor DI params + * that cannot be instantiated with new $className(). + * + * @return void + */ + public function testFromClassWithDiConstructor(): void { + $meta = TaskMetadata::fromClass(InjectedTask::class); + + $this->assertSame(InjectedTask::class, $meta->class); + $this->assertSame(10, $meta->timeout); + $this->assertNull($meta->retries); + $this->assertSame(0, $meta->rate); + $this->assertSame(0, $meta->costs); + $this->assertFalse($meta->unique); + } + +} diff --git a/tests/test_app/src/Queue/Task/MetadataTask.php b/tests/test_app/src/Queue/Task/MetadataTask.php new file mode 100644 index 00000000..76afd5e4 --- /dev/null +++ b/tests/test_app/src/Queue/Task/MetadataTask.php @@ -0,0 +1,33 @@ +