Skip to content

Commit

Permalink
[FEATURE] Use attributes to schedule task executions
Browse files Browse the repository at this point in the history
  • Loading branch information
vertexvaar committed Oct 14, 2023
1 parent 376a0a7 commit 274525e
Show file tree
Hide file tree
Showing 16 changed files with 198 additions and 136 deletions.
14 changes: 0 additions & 14 deletions config/tasks.php

This file was deleted.

4 changes: 2 additions & 2 deletions packages/bluesprints/bin/bluesprints
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ declare(strict_types=1);

use Symfony\Component\Dotenv\Dotenv;
use VerteXVaaR\BlueContainer\DI;
use VerteXVaaR\BlueSprints\Task\CliRequest;
use VerteXVaaR\BlueSprints\Task\Scheduler;
use VerteXVaaR\BlueSprints\Scheduler\CliRequest;
use VerteXVaaR\BlueSprints\Scheduler\Scheduler;

$root = getenv('VXVR_BS_ROOT');
if (!$root) {
Expand Down
5 changes: 5 additions & 0 deletions packages/bluesprints/config/services.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
use VerteXVaaR\BlueSprints\Mvcr\Controller\Controller;
use VerteXVaaR\BlueSprints\Mvcr\DependencyInjection\PublicServicePass;
use VerteXVaaR\BlueSprints\Routing\DependencyInjection\RouteCollectorCompilerPass;
use VerteXVaaR\BlueSprints\Scheduler\DependencyInjection\SchedulerTaskCompilerPass;
use VerteXVaaR\BlueSprints\Scheduler\Task\Task;
use VerteXVaaR\BlueSprints\Template\DependencyInjection\TemplateRendererCompilerPass;

return static function (ContainerBuilder $container): void {
Expand All @@ -17,8 +19,11 @@
$container->addCompilerPass(new MiddlewareCompilerPass());
$container->addCompilerPass(new RouteCollectorCompilerPass());
$container->addCompilerPass(new TemplateRendererCompilerPass());
$container->addCompilerPass(new SchedulerTaskCompilerPass());

$container->registerForAutoconfiguration(Controller::class)
->addTag('vertexvaar.bluesprints.controller');
$container->addCompilerPass(new PublicServicePass('vertexvaar.bluesprints.controller'));
$container->registerForAutoconfiguration(Task::class)
->addTag('vertexvaar.bluesprints.scheduler.scheduled_task');
};
2 changes: 1 addition & 1 deletion packages/bluesprints/config/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ services:
VerteXVaaR\BlueSprints\Http\HttpResponseEmitter:
public: true

VerteXVaaR\BlueSprints\Task\Scheduler:
VerteXVaaR\BlueSprints\Scheduler\Scheduler:
public: true

VerteXVaaR\BlueSprints\Environment\Environment:
Expand Down
16 changes: 16 additions & 0 deletions packages/bluesprints/src/Scheduler/Attribute/ScheduledTask.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace VerteXVaaR\BlueSprints\Scheduler\Attribute;

use Attribute;

#[Attribute(Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)]
final readonly class ScheduledTask
{
public function __construct(public string $identifier, public int $interval, public array $config)
{
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

declare(strict_types=1);

namespace VerteXVaaR\BlueSprints\Task;
namespace VerteXVaaR\BlueSprints\Scheduler;

class CliRequest
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

declare(strict_types=1);

namespace VerteXVaaR\BlueSprints\Scheduler\DependencyInjection;

use ReflectionClass;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;

use Symfony\Component\DependencyInjection\Reference;
use VerteXVaaR\BlueSprints\Scheduler\Attribute\ScheduledTask;

use VerteXVaaR\BlueSprints\Scheduler\SchedulerTaskRegistry;

use function array_keys;

class SchedulerTaskCompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
$scheduledTasks = [];

$tasks = $container->findTaggedServiceIds('vertexvaar.bluesprints.scheduler.scheduled_task');
foreach (array_keys($tasks) as $task) {
$taskDefinition = $container->getDefinition($task);
$taskClass = $taskDefinition->getClass();
$taskReflection = new ReflectionClass($taskClass);
$attributes = $taskReflection->getAttributes(ScheduledTask::class);
foreach ($attributes as $attribute) {
/** @var ScheduledTask $scheduledTask */
$scheduledTask = $attribute->newInstance();
$scheduledTasks[$taskClass][$scheduledTask->identifier] = [
'service' => new Reference($taskClass),
'config' => $scheduledTask->config,
'interval' => $scheduledTask->interval,
];
}
}

$schedulerTaskRegistry = $container->getDefinition(SchedulerTaskRegistry::class);
$schedulerTaskRegistry->setArgument('$tasks', $scheduledTasks);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

declare(strict_types=1);

namespace VerteXVaaR\BlueSprints\Task;
namespace VerteXVaaR\BlueSprints\Scheduler\Model;

use VerteXVaaR\BlueSprints\Mvcr\Model\Entity;

Expand Down
57 changes: 57 additions & 0 deletions packages/bluesprints/src/Scheduler/Scheduler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php

declare(strict_types=1);

namespace VerteXVaaR\BlueSprints\Scheduler;

use VerteXVaaR\BlueSprints\Mvcr\Repository\Repository;
use VerteXVaaR\BlueSprints\Scheduler\Model\TaskExecution;
use VerteXVaaR\BlueSprints\Scheduler\Task\AbstractTask;

use function strtr;
use function time;

use const PHP_INT_MAX;

readonly class Scheduler
{

public function __construct(
private SchedulerTaskRegistry $schedulerTaskRegistry,
private Repository $repository,
) {
}

/**
* Starts all the scheduled tasks. They are not executed in parallel.
*/
public function run(CliRequest $cliRequest): void
{
$now = time();

foreach ($this->schedulerTaskRegistry->tasks as $taskClass => $identifiers) {
foreach ($identifiers as $identifier => $taskConfiguration) {
$taskExecution = $this->getTaskExecution($taskClass . '->' . $identifier);
if (($now - $taskExecution->lastExecution) >= $taskConfiguration['interval']) {
/** @var AbstractTask $task */
$task = $taskConfiguration['service'];
$task->run($identifier, $cliRequest, $taskConfiguration['config'] ?? []);
$taskExecution->lastExecution = $now;
$this->repository->persist($taskExecution);
}
}
}
}

public function getTaskExecution(string $taskName): TaskExecution
{
$identifier = strtr($taskName, '\\', '_');
$taskExecution = $this->repository->findByIdentifier(TaskExecution::class, $identifier);
if (null === $taskExecution) {
$taskExecution = new TaskExecution($identifier);
$taskExecution->lastExecution = -PHP_INT_MAX;
$this->repository->persist($taskExecution);
}
return $taskExecution;
}
}
12 changes: 12 additions & 0 deletions packages/bluesprints/src/Scheduler/SchedulerTaskRegistry.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace VerteXVaaR\BlueSprints\Scheduler;

readonly class SchedulerTaskRegistry
{
public function __construct(public array $tasks)
{
}
}
20 changes: 20 additions & 0 deletions packages/bluesprints/src/Scheduler/Task/AbstractTask.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

declare(strict_types=1);

namespace VerteXVaaR\BlueSprints\Scheduler\Task;

use VerteXVaaR\BlueSprints\Scheduler\CliRequest;

abstract class AbstractTask implements Task
{
/**
* Does not return anything. Write with ->printLine() instead.
*/
abstract public function run(string $identifier, CliRequest $cliRequest, array $config): void;

final protected function printLine(string $line): void
{
echo $line . PHP_EOL;
}
}
10 changes: 10 additions & 0 deletions packages/bluesprints/src/Scheduler/Task/Task.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

declare(strict_types=1);

namespace VerteXVaaR\BlueSprints\Scheduler\Task;

interface Task
{

}
25 changes: 0 additions & 25 deletions packages/bluesprints/src/Task/AbstractTask.php

This file was deleted.

68 changes: 0 additions & 68 deletions packages/bluesprints/src/Task/Scheduler.php

This file was deleted.

29 changes: 29 additions & 0 deletions src/Scheduler/ExampleScheduledTask.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

declare(strict_types=1);

namespace VerteXVaaR\BlueDist\Scheduler;

use VerteXVaaR\BlueSprints\Scheduler\Attribute\ScheduledTask;
use VerteXVaaR\BlueSprints\Scheduler\CliRequest;
use VerteXVaaR\BlueSprints\Scheduler\Task\AbstractTask;

use function var_export;

#[ScheduledTask(identifier: 'First schedule of example task', interval: 10, config: ['first', 'second'])]
#[ScheduledTask(identifier: 'Second schedule of example task', interval: 5, config: ['foo', 'bar'])]
class ExampleScheduledTask extends AbstractTask
{
public function run(string $identifier, CliRequest $cliRequest, array $config): void
{
$verbose = $cliRequest->flagExists('--verbose');
$this->printLine(sprintf("Running %s with options: %s", $identifier, var_export($config, true)));
$verbose ? $this->printLine('Checking if arg "string" exists') : null;
if ($cliRequest->hasArgument('string')) {
$verbose ? $this->printLine('arg "string" exists, printing "string" value') : null;
$this->printLine($cliRequest->getArgument('string'));
} else {
$verbose ? $this->printLine('arg "string" does not exist') : null;
}
}
}
24 changes: 0 additions & 24 deletions src/Task/ExampleTask.php

This file was deleted.

0 comments on commit 274525e

Please sign in to comment.