tobento / service-schedule
一个特定间隔运行任务的调度系统。
Requires
- php: >=8.0
- butschster/cron-expression-generator: ^1.10
- dragonmantank/cron-expression: ^3.1
- psr/clock: ^1.0
- psr/container: ^2.0
- psr/event-dispatcher: ^1.0
- psr/simple-cache: 2 - 3
- tobento/service-autowire: ^1.0.9
- tobento/service-collection: ^1.0.5
- tobento/service-file-creator: ^1.0.1
Requires (Dev)
- guzzlehttp/guzzle: ^7.8
- mockery/mockery: ^1.6
- phpunit/phpunit: ^9.5
- symfony/process: ^4.4|^5.4|^6.0
- tobento/service-cache: ^1.0
- tobento/service-clock: ^1.0
- tobento/service-console: ^1.0.3
- tobento/service-container: ^1.0.6
- tobento/service-event: ^1.0
- tobento/service-mail: ^1.0
- vimeo/psalm: ^4.0
Suggests
- guzzlehttp/guzzle: Required to support the ping task and parameter
- symfony/process: Required to support the process task
- tobento/service-cache: May be used to support task without overlapping
- tobento/service-console: To support command tasks
- tobento/service-event: May be used to support schedule and task events
- tobento/service-mail: May be used to support sending mails
README
一个特定间隔运行任务的调度系统。
目录
入门
使用以下命令添加运行此命令的调度服务项目的最新版本。
composer require tobento/service-schedule
要求
- PHP 8.0 或更高版本
亮点
- 框架无关,可与任何项目一起使用
- 解耦设计
文档
用法
调度任务
要调度任务,您可以使用默认的 调度 或创建符合您需求的调度。
运行计划任务
运行调度处理器的最常见方法是运行每分钟运行的调度:run的cron作业。以下内容应添加到您的生产服务器的crontab中
* * * * * cd /path-to-your-project && php console.php schedule:run >> /dev/null 2>&1
console.php的示例
composer require tobento/service-console
composer require tobento/service-clock
composer require tobento/service-container
use Tobento\Service\Clock\SystemClock; use Tobento\Service\Container\Container; use Tobento\Service\Console\Console; use Tobento\Service\Console\Command; use Tobento\Service\Console\InteractorInterface; use Tobento\Service\Console\Symfony; use Tobento\Service\Schedule\Task; use Tobento\Service\Schedule\Parameter; use Tobento\Service\Schedule\TaskProcessor; use Tobento\Service\Schedule\TaskProcessorInterface; use Tobento\Service\Schedule\ScheduleProcessor; use Tobento\Service\Schedule\ScheduleProcessorInterface; use Tobento\Service\Schedule\Schedule; use Tobento\Service\Schedule\ScheduleInterface; use Psr\Container\ContainerInterface; use Psr\Clock\ClockInterface; // Container bindings: $container = new Container(); $container->set(ClockInterface::class, new SystemClock()); $container->set(TaskProcessorInterface::class, TaskProcessor::class); $container->set(ScheduleProcessorInterface::class, ScheduleProcessor::class); // Schedule: $container->set(ScheduleInterface::class, function() { $schedule = new Schedule(name: 'default'); $schedule->task( (new Task\CallableTask( callable: static function (): string { // do something: return 'task output'; }, ))->name('demo') ); return $schedule; }); // Console: $console = new Symfony\Console( name: 'app', container: $container, ); $console->addCommand(\Tobento\Service\Schedule\Console\ScheduleRunCommand::class); $console->addCommand(\Tobento\Service\Schedule\Console\ScheduleListCommand::class); $console->run();
任务
可调用任务
use Tobento\Service\Schedule\Task\CallableTask; use Tobento\Service\Schedule\Task\TaskInterface; $task = new CallableTask( callable: static function (SomeService $service, $option): string { // do something // you may return the task output or nothing at all (void): return 'task output'; }, // you may set data passed to the function: params: ['option' => 'value'], ); var_dump($task instanceof TaskInterface); // bool(true) // Specific task methods: $callable = $task->getCallable(); $params = $task->getParams();
可调用对象
您可以考虑使用包含 __invoke
方法的可调用对象
use Tobento\Service\Schedule\Task\CallableTask; class SampleInvokable { public function __invoke(SomeService $service, $option): string { // do something // you may return the task output or nothing at all (void): return 'task output'; } } $task = new CallableTask( callable: new SampleInvokable(), // you may set data passed to the __invoke method: params: ['option' => 'value'], );
命令任务
命令任务可以用于使用 控制台服务 运行提供的命令。
use Tobento\Service\Schedule\Task\CommandTask; use Tobento\Service\Schedule\Task\TaskInterface; use Tobento\Service\Console\ConsoleInterface; use Tobento\Service\Console\ExecutedInterface; $task = new CommandTask( command: 'command:name', // you may set command input data: input: [ // passing arguments: 'username' => 'Tom', // with array value: 'username' => ['Tom', 'Tim'], // passing options: '--some-option' => 'value', // with array value: '--some-option' => ['value'], ], ); var_dump($task instanceof TaskInterface); // bool(true) // Specific task methods: $command = $task->getCommand(); $input = $task->getInput(); // Returning null if task is not processed yet, otherwise the console. $console = $task->getConsole(); // null|ConsoleInterface // Returning null if task is not processed yet, otherwise the executed. $executed = $task->getExecuted(); // null|ExecutedInterface
要求
此命令任务需要 控制台服务
composer require tobento/service-console
可调用任务
您可以通过扩展 InvokableTask::class
来创建可调用任务。当任务正在处理时,将调用 __invoke
方法。
use Tobento\Service\Schedule\Task\InvokableTask; use Tobento\Service\Schedule\Task\Schedule\CronExpression; class SampleTask extends InvokableTask { public function __construct() { // you may set default parameters: $this->id('A unique id'); $this->name('A task name'); $this->description('A task description'); // you may set a default schedule: $this->schedule(new CronExpression('* * * * *')); // or $this->cron('* * * * *'); } public function __invoke(SomeService $service): string { // do something // you may return the task output or nothing at all (void): return 'task output'; } } $task = (new SampleTask())->cron('* * * * *');
Ping任务
Ping任务可以用于ping提供的URI。
use Tobento\Service\Schedule\Task\PingTask; use Tobento\Service\Schedule\Task\TaskInterface; use Psr\Http\Message\ResponseInterface; $task = new PingTask( uri: 'https://example.com/ping', method: 'GET', // default options: [], ); var_dump($task instanceof TaskInterface); // bool(true) // Specific task methods: $uri = $task->getUri(); $method = $task->getMethod(); $options = $task->getOptions(); // Returning null if task is not processed yet, otherwise the response. $response = $task->getResponse(); // null|ResponseInterface
要求
此ping任务需要 Guzzle,PHP HTTP客户端
composer require guzzlehttp/guzzle
进程任务
进程任务可以用于使用 Symfony Process Component 执行shell命令。
use Tobento\Service\Schedule\Task\ProcessTask; use Tobento\Service\Schedule\Task\TaskInterface; use Symfony\Component\Process\Process; $task = new ProcessTask( process: '/bin/script', ); // or with a process instance: $task = new ProcessTask( process: Process::fromShellCommandline('/bin/script') ->setTimeout(20), ); var_dump($task instanceof TaskInterface); // bool(true) // Specific task methods: $process = $task->getProcess();
要求
此进程任务需要 Symfony Process Component
composer require symfony/process
任务方法
所有支持以下方法的可用任务。
通用任务方法
// You may set a unique task id: $task->id('taskId'); // You may set a task name: $task->name('A task name'); // You may set a description: $task->description('A task description');
调度任务方法
use Tobento\Service\Schedule\TaskScheduleInterface; use Tobento\Service\Schedule\Task\Schedule\CronExpression; // you may set the schedule implementing TaskScheduleInterface $task->schedule(new CronExpression( expression: '* * * * *', // you may specify a timezone: timezone: 'Europe/Berlin', // string|\DateTimeZone )); // or you may use the cron method: $task->cron( expression: '* * * * *', // you may specify a timezone: timezone: 'Europe/Berlin', // string|\DateTimeZone );
查看“任务计划”部分以获取可用的任务计划。
您可以考虑使用已经可用的cron生成器。
use Butschster\CronExpression\Generator; $task->cron(Generator::create()->daily());
查看Cron表达式生成器以获取更多示例。
任务调度
Cron表达式
您可以使用CronExpression::class
来定义希望任务运行的间隔。
use Tobento\Service\Schedule\Task\Schedule\CronExpression; use Tobento\Service\Schedule\TaskScheduleInterface; $cron = new CronExpression( expression: '* * * * *', // you may specify a timezone: timezone: 'Europe/Berlin', // string|\DateTimeZone ); var_dump($cron instanceof TaskScheduleInterface); // bool(true)
您可以考虑使用已经可用的cron生成器。
use Tobento\Service\Schedule\Task\Schedule\CronExpression; use Butschster\CronExpression\Generator; $cron = new CronExpression( expression: Generator::create()->daily(), );
查看Cron表达式生成器以获取更多示例。
日期
您可以使用Dates::class
在每年的特定日期运行任务。
use Tobento\Service\Schedule\Task\Schedule\Dates; use Tobento\Service\Schedule\TaskScheduleInterface; $dates = new Dates( // Every year yyyy-05-12 15:38 new \DateTime('2023-05-12 15:38:45'), // Every year yyyy-08-24 10:15 new \DateTimeImmutable('2023-08-24 10:15:33'), ); var_dump($dates instanceof TaskScheduleInterface); // bool(true)
任务参数
您可以使用提供的参数,这些参数为任务提供了基本功能,或者创建自定义参数以添加新功能或自定义现有功能以满足您的需求。
所有支持以下参数及其辅助方法的所有任务。
之后参数
在任务处理完毕后,执行指定的处理程序。
TaskResultInterface::class
将传递给您的处理程序。此外,您还可以请求由传递给任务处理器的容器解析(自动装配)的任何服务。
use Tobento\Service\Schedule\Task\CommandTask; use Tobento\Service\Schedule\Parameter; use Tobento\Service\Schedule\TaskResultInterface; // any callable handler: $task = (new CommandTask('command:name')) ->parameter(new Parameter\After(static function(TaskResultInterface $result, SomeService $service): void { // executes after the task is processed })) // or using the helper method: ->after(static function (TaskResultInterface $result, SomeService $service): void { // executes after the task is processed }); // or a handler implementing Parameter\AfterTaskHandler $task = (new CommandTask('command:name')) ->parameter(new Parameter\After(handler: $handler)) // or using the helper method: ->after(handler: $handler);
之前参数
在任务处理之前执行指定的处理程序。
TaskInterface::class
将传递给您的处理程序。此外,您还可以请求由传递给任务处理器的容器解析(自动装配)的任何服务。
use Tobento\Service\Schedule\Task\CommandTask; use Tobento\Service\Schedule\Parameter; use Tobento\Service\Schedule\TaskInterface; // any callable handler: $task = (new CommandTask('command:name')) ->parameter(new Parameter\Before(static function(TaskInterface $task, SomeService $service): void { // executes before the task is processed })) // or using the helper method: ->before(static function (TaskInterface $task, SomeService $service): void { // executes before the task is processed }); // or a handler implementing Parameter\BeforeTaskHandler $task = (new CommandTask('command:name')) ->parameter(new Parameter\Before(handler: $handler)) // or using the helper method: ->before(handler: $handler);
跳过任务
如果您想阻止任务被处理但不导致失败,您可以抛出TaskSkipException::class
。
use Tobento\Service\Schedule\Task\CommandTask; use Tobento\Service\Schedule\TaskSkipException; // any callable handler: $task = (new CommandTask('command:name')) ->before(static function (): void { throw new TaskSkipException('Skipped because ...'); });
失败参数
如果任务失败,则执行指定的处理程序。
TaskResultInterface::class
将传递给您的处理程序。此外,您还可以请求由传递给任务处理器的容器解析(自动装配)的任何服务。
use Tobento\Service\Schedule\Task\CommandTask; use Tobento\Service\Schedule\Parameter; use Tobento\Service\Schedule\TaskResultInterface; // any callable handler: $task = (new CommandTask('command:name')) ->parameter(new Parameter\Failed(static function(TaskResultInterface $result, SomeService $service): void { // executes if the task failed })) // or using the helper method: ->failed(static function (TaskResultInterface $result, SomeService $service): void { // executes if the task failed }); // or a handler implementing Parameter\FailedTaskHandler $task = (new CommandTask('command:name')) ->parameter(new Parameter\Failed(handler: $handler)) // or using the helper method: ->failed(handler: $handler);
邮件参数
可以使用mail参数在任务处理之前和/或之后或任务失败时使用邮件服务发送邮件。
use Tobento\Service\Schedule\Task\CommandTask; use Tobento\Service\Schedule\Parameter; use Tobento\Service\Mail\Message; $task = (new CommandTask('command:name')) ->parameter(new Parameter\Mail( message: (new Message())->to('admin@example.com'), handle: ['before', 'after', 'failed'], // default // send mail only if task failed: // handle: ['failed'], ));
此外,您还可以使用before
、after
和failed
辅助方法。
use Tobento\Service\Schedule\Task\CommandTask; use Tobento\Service\Schedule\Parameter; use Tobento\Service\Mail\Message; $task = (new CommandTask('command:name')) ->before(new Parameter\Mail( message: (new Message())->to('admin@example.com'), )) ->after(new Parameter\Mail( message: (new Message())->to('admin@example.com'), )) ->failed(new Parameter\Mail( message: (new Message())->to('admin@example.com'), ));
要求
此参数需要邮件服务
首先,安装邮件服务
composer require tobento/service-mail
最后,它需要将MailerInterface::class
绑定到传递给任务处理器的容器。
使用容器服务的示例
use Tobento\Service\Mail\MailerInterface; use Tobento\Service\Schedule\TaskProcessor; use Tobento\Service\Container\Container; $container = new Container(); $container->set(MailerInterface::class, function() { // create mailer: return $mailer; }); $taskProcessor = new TaskProcessor($container);
发件人地址
如果您没有定义默认的发件人地址,则需要设置它
use Tobento\Service\Mail\Message; $message = (new Message()) ->from('from@example.com') ->to('admin@example.com');
邮件内容
您可以设置一个消息主题,否则将使用任务名称
use Tobento\Service\Mail\Message; $message = (new Message()) ->subject('Some task subject') ->to('admin@example.com');
发送的邮件如下所示
Task Status: Failed Task ID: task-id Task Name: Task's name Task Description: Task's description (if any) Task Output: Failed task's output (if any) Task Exception: Failed task's exception stack trace (if any)
监控参数
可以使用monitor参数来监控任务过程,例如启动时间、运行时间和内存使用情况。
use Tobento\Service\Schedule\Task\CommandTask; use Tobento\Service\Schedule\Parameter; $task = (new CommandTask('command:name')) ->parameter(new Parameter\Monitor()) // or using the helper method: ->monitor();
Ping参数
可以使用ping参数ping提供的URI。
use Tobento\Service\Schedule\Task\CommandTask; use Tobento\Service\Schedule\Parameter; $task = (new CommandTask('command:name')) ->parameter(new Parameter\Ping( uri: 'https://example.com/task', method: 'GET', // default options: [], handle: ['before', 'after', 'failed'], // default // pings only if task failed: // handle: ['failed'], ));
将根据其处理状态添加一个X-Task-Status
头,其值可以是“Starting”、“Success”或“Failed”。
此外,您还可以使用before
、after
和failed
辅助方法。
use Tobento\Service\Schedule\Task\CommandTask; use Tobento\Service\Schedule\Parameter; $task = (new CommandTask('command:name')) ->before(new Parameter\Ping( uri: 'https://example.com/task-before', )) ->after(new Parameter\Ping( uri: 'https://example.com/task-after', )) ->failed(new Parameter\Ping( uri: 'https://example.com/task-failed', ));
要求
此参数需要Guzzle,PHP HTTP客户端
composer require guzzlehttp/guzzle
发送结果到参数
可以使用send result to参数将任务结果发送到指定的文件。
use Tobento\Service\Schedule\Task\CommandTask; use Tobento\Service\Schedule\Parameter; use Tobento\Service\Schedule\TaskInterface; $task = (new CommandTask('command:name')) ->parameter(new Parameter\SendResultTo( file: 'dir/to/file.log', // if true sends only output, otherwise the whole result: onlyOutput: false, // default // if true appends result to file otherwise overwrites file: append: true, // default handle: ['after', 'failed'], // default // send result only after task is processed: // handle: ['after'], ));
此外,您还可以使用after
和failed
辅助方法。
use Tobento\Service\Schedule\Task\CommandTask; use Tobento\Service\Schedule\Parameter; $task = (new CommandTask('command:name')) ->after(new Parameter\SendResultTo( file: 'dir/to/file-success.log', )) ->failed(new Parameter\SendResultTo( file: 'dir/to/file-failed.log', ));
跳过参数
跳过参数可用于在 skip
参数评估为 true
时跳过任务。该参数的默认优先级为 100000
,这意味着它在任何低优先级任务参数之前处理。
use Tobento\Service\Schedule\Task\CommandTask; use Tobento\Service\Schedule\Parameter; use Tobento\Service\Schedule\TaskInterface; // using a boolean: $task = (new CommandTask('command:name')) ->parameter(new Parameter\Skip( skip: true, // You may specify a reason for skipping: reason: 'Because of ...' )); // using a callable: $task = (new CommandTask('command:name')) ->parameter(new Parameter\Skip( skip: static function(TaskInterface $task, SomeService $service) // skips if return value is true return true; }, // You may specify a reason for skipping: reason: 'Because of ...' )); // or using the helper method: $task = (new CommandTask('command:name')) ->skip(skip: $skip, reason: 'Because of ...');
如果使用可调用对象,将把 TaskInterface::class
传递给您的回调函数。此外,您还可以请求通过传递给任务处理器的容器解析(自动装配)的任何服务。
无重叠参数
可以使用非重叠参数来防止任务重叠。
use Tobento\Service\Schedule\Task\CommandTask; use Tobento\Service\Schedule\Parameter; $task = (new CommandTask('command:name')) ->parameter(new Parameter\WithoutOverlapping( // You may set a unique id. If null it uses the task id. id: 'unique-task-id', // null|string // You may set a maximum expected lock duration in seconds: ttl: 86400, // default )) // or using the helper method: ->withoutOverlapping() // with id and ttl: ->withoutOverlapping(id: 'unique-task-id', ttl: 86400);
要求
此参数需要将 CacheInterface::class
绑定到传递给任务处理器的容器。
use Psr\SimpleCache\CacheInterface; use Tobento\Service\Schedule\TaskProcessor; use Tobento\Service\Container\Container; use Tobento\Service\Cache\Simple\Psr6Cache; use Tobento\Service\Cache\ArrayCacheItemPool; use Tobento\Service\Clock\SystemClock; $container = new Container(); $container->set(CacheInterface::class, function() { // create cache: return new Psr6Cache( pool: new ArrayCacheItemPool( clock: new SystemClock(), ), namespace: 'default', ttl: null, ); }); $taskProcessor = new TaskProcessor($container);
任务处理器
TaskProcessor::class
负责处理任务。
use Tobento\Service\Schedule\TaskProcessor; use Tobento\Service\Schedule\TaskProcessorInterface; use Psr\Container\ContainerInterface; $taskProcessor = new TaskProcessor( container: $container // ContainerInterface ); var_dump($taskProcessor instanceof TaskProcessorInterface); // bool(true)
进程任务
use Tobento\Service\Schedule\TaskInterface; use Tobento\Service\Schedule\TaskResultInterface; $result = $taskProcessor->processTask($task); // TaskInterface var_dump($result instanceof TaskResultInterface); // bool(true)
查看任务结果了解更多信息。
任务结果
use Tobento\Service\Schedule\TaskResult; use Tobento\Service\Schedule\TaskResultInterface; use Tobento\Service\Schedule\TaskInterface; $result = new TaskResult( task: $task, output: 'task output', exception: null, // null|\Throwable ); var_dump($result instanceof TaskResultInterface); // bool(true) // Get the task: $task = $result->task(); // TaskInterface // Get the output: $output = $result->output(); // string // Get the exception: $output = $result->exception(); // null|\Throwable var_dump($result->isSuccessful()); // bool(true) var_dump($result->isFailure()); // bool(false) var_dump($result->isSkipped()); // bool(false)
任务结果
use Tobento\Service\Schedule\TaskResults; use Tobento\Service\Schedule\TaskResultsInterface; use Tobento\Service\Schedule\TaskResultInterface; $results = new TaskResults(); var_dump($results instanceof TaskResultsInterface); // bool(true) // Add a task result: $results->add($result); // TaskResultInterface // Get all task results: $taskResults = $results->all(); // iterable<int, TaskResultInterface> // or just: foreach($taskResults as $taskResult) {} // Count all task results: $number = $results->count(); // int(0) // Filter all successful task results returning a new instance: $taskResults = $results->successful(); // TaskResultsInterface // Filter all failed task results returning a new instance: $taskResults = $results->failed(); // TaskResultsInterface // Filter all skipped task results returning a new instance: $taskResults = $results->skipped(); // TaskResultsInterface // You may use the filter method for filtering task results // returning a new instance: $taskResults = $results->filter(fn(TaskResultInterface $r): bool => is_null($r->exception())); // TaskResultsInterface
调度
use Tobento\Service\Schedule\Schedule; use Tobento\Service\Schedule\ScheduleInterface; use Tobento\Service\Schedule\TaskInterface; $schedule = new Schedule(name: 'default'); var_dump($schedule instanceof ScheduleInterface); // bool(true) // Schedule any task implementing TaskInterface $schedule->task($task); // You may get a task by its id returning null if task is not found: $task = $schedule->getTask(id: 'taskId'); // null|TaskInterface // You may get all tasks: $tasks = $schedule->all(); // iterable<int, TaskInterface>
添加任务的示例
use Tobento\Service\Schedule\Task; use Tobento\Service\Schedule\Parameter; use Butschster\CronExpression\Generator; $schedule->task( (new Task\CommandTask( command: 'command:name', )) // schedule task: ->cron(Generator::create()->everyTenMinutes()) // adding parameters: ->parameter(Parameter\SendResultTo( file: 'dir/to/file.log', )) // or using helper methods: ->withoutOverlapping() ); $schedule->task( (new Task\CallableTask( callable: static function (SomeService $service, $option): string { // do something // you may return the task output or nothing at all (void): return 'task output'; }, // you may set data passed to the function: params: ['option' => 'value'], )) ->name('Some name') ->description('Some description') // schedule task: ->cron(Generator::create()->everyTenMinutes()) // adding parameters: ->parameter(Parameter\SendResultTo( file: 'dir/to/file.log', )) // or using helper methods: ->withoutOverlapping() );
查看任务部分以获取有关各个任务的更多信息。
调度处理器
调度处理器负责处理到期的计划任务。
use Tobento\Service\Schedule\ScheduleProcessor; use Tobento\Service\Schedule\ScheduleProcessorInterface; use Tobento\Service\Schedule\TaskProcessorInterface; use Psr\EventDispatcher\EventDispatcherInterface; $scheduleProcessor = new ScheduleProcessor( taskProcessor: $taskProcessor, // TaskProcessorInterface // you may set an event dispatcher if you want to support events: eventDispatcher: null, // null|EventDispatcherInterface ); var_dump($scheduleProcessor instanceof ScheduleProcessorInterface); // bool(true)
处理计划任务
use Tobento\Service\Schedule\ScheduleInterface; use Tobento\Service\Schedule\TaskResultsInterface; $results = $scheduleProcessor->processSchedule( schedule: $schedule, // ScheduleInterface now: new \DateTime(), // \DateTimeInterface ); var_dump($results instanceof TaskResultsInterface); // bool(true)
要处理到期的任务,应无限期地每分钟调用一次 processSchedule
方法。
尽管如此,您也可以创建一个符合您需求的自定义调度处理器。
查看任务结果了解更多。
控制台
您可以使用控制台服务执行以下命令。
要快速开始,请考虑使用以下两个应用程序包
否则,您需要安装控制台服务并自行设置控制台。查看运行计划任务部分以查看可能的实现。
运行命令
运行到期的任务
php app schedule:run
按其ID运行特定任务
php app schedule:run --id=taskId --id=anotherTaskId
列表命令
列出所有任务。
php app schedule:list
事件
可用事件
use Tobento\Service\Schedule\Event;
只需确保将事件分派器传递给调度处理器!
了解更多
在没有控制台的情况下运行计划任务
运行调度处理器的另一种方法是Cron作业,每分钟运行您的schedule.php脚本。以下内容应添加到生产服务器的crontab中
* * * * * cd /path-to-your-project && php schedule.php >> /dev/null 2>&1
schedule.php的示例
使用容器服务的示例
composer require tobento/service-container
use Tobento\Service\Container\Container; use Tobento\Service\Schedule\Task; use Tobento\Service\Schedule\Parameter; use Tobento\Service\Schedule\TaskProcessor; use Tobento\Service\Schedule\TaskProcessorInterface; use Tobento\Service\Schedule\ScheduleProcessor; use Tobento\Service\Schedule\ScheduleProcessorInterface; use Tobento\Service\Schedule\Schedule; use Tobento\Service\Schedule\ScheduleInterface; use Psr\Container\ContainerInterface; // Container bindings: $container = new Container(); $container->set(TaskProcessorInterface::class, TaskProcessor::class); $container->set(ScheduleProcessorInterface::class, ScheduleProcessor::class); // Schedule tasks: $schedule = new Schedule(name: 'default'); $schedule->task( (new Task\CallableTask( callable: static function (): string { // do something: return 'task output'; }, ))->name('demo') ); // Process the schedule: $container->get(ScheduleProcessorInterface::class)->processSchedule( schedule: $schedule, // ScheduleInterface now: new \DateTime(), // \DateTimeInterface );