Basic scheduler completed
This commit is contained in:
@@ -2,7 +2,13 @@
|
|||||||
"name": "avsdev/sprinkle-scheduler",
|
"name": "avsdev/sprinkle-scheduler",
|
||||||
"type": "userfrosting-sprinkle",
|
"type": "userfrosting-sprinkle",
|
||||||
"description": "A sprinkle to add scheduled events.",
|
"description": "A sprinkle to add scheduled events.",
|
||||||
|
"require": {
|
||||||
|
"dragonmantank/cron-expression": "*"
|
||||||
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
|
"files" : [
|
||||||
|
"defines.php"
|
||||||
|
],
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
"UserFrosting\\Sprinkle\\Scheduler\\": "src/"
|
"UserFrosting\\Sprinkle\\Scheduler\\": "src/"
|
||||||
}
|
}
|
||||||
|
|||||||
13
defines.php
Normal file
13
defines.php
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AVSDev UF Scheduler (https://avsdev.uk)
|
||||||
|
*
|
||||||
|
* @link https://git.avsdev.uk/avsdev/sprinkle-scheduler
|
||||||
|
* @license https://git.avsdev.uk/avsdev/sprinkle-scheduler/blob/master/LICENSE.md (LGPL-3.0 License)
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace UserFrosting;
|
||||||
|
|
||||||
|
// Relative path from within sprinkle directory
|
||||||
|
define('UserFrosting\SCHEDULED_TASKS_DIR', SRC_DIR_NAME . DS . 'Scheduler' . DS . 'Tasks');
|
||||||
139
src/Bakery/ScheduleCommand.php
Normal file
139
src/Bakery/ScheduleCommand.php
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AVSDev UF Scheduler (https://avsdev.uk)
|
||||||
|
*
|
||||||
|
* @link https://git.avsdev.uk/avsdev/sprinkle-scheduler
|
||||||
|
* @license https://git.avsdev.uk/avsdev/sprinkle-scheduler/blob/master/LICENSE.md (LGPL-3.0 License)
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace UserFrosting\Sprinkle\Scheduler\Bakery;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
|
use Symfony\Component\Console\Input\InputOption;
|
||||||
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
use UserFrosting\Sprinkle\Core\Bakery\Helper\ConfirmableTrait;
|
||||||
|
use UserFrosting\Sprinkle\Core\Database\Tasker\Tasker;
|
||||||
|
use UserFrosting\System\Bakery\BaseCommand;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* scheduler Bakery Command
|
||||||
|
* Run a scheduled task.
|
||||||
|
*
|
||||||
|
* @author Craig Williams (https://avsdev.uk)
|
||||||
|
*/
|
||||||
|
class ScheduleCommand extends BaseCommand
|
||||||
|
{
|
||||||
|
use ConfirmableTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Tasker
|
||||||
|
*/
|
||||||
|
protected $scheduler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function configure()
|
||||||
|
{
|
||||||
|
$this->setName('schedule')
|
||||||
|
->setDescription('Run the schedule')
|
||||||
|
->setHelp('This command runs the schedule, checking if any tasks are due and executing them.');
|
||||||
|
#->addOption('quiet', null, InputOption::VALUE_NONE, 'Do not output any status messages.');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function execute(InputInterface $input, OutputInterface $output)
|
||||||
|
{
|
||||||
|
// Get options
|
||||||
|
$quiet = $input->getOption('quiet');
|
||||||
|
|
||||||
|
if (!$quiet) {
|
||||||
|
$this->io->title("UserFrosting's Scheduler");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare task locator
|
||||||
|
$scheduler = $this->ci->scheduler;
|
||||||
|
|
||||||
|
|
||||||
|
if (!$quiet) {
|
||||||
|
$this->io->writeln('<comment>Checking for tasks to run...</comment>');
|
||||||
|
}
|
||||||
|
|
||||||
|
$tasks = [];
|
||||||
|
|
||||||
|
// Start by getting tasks
|
||||||
|
foreach ($scheduler->getTasks() as $task) {
|
||||||
|
if ($task['instance']->isDue()) {
|
||||||
|
// Add task class to list
|
||||||
|
$tasks[] = $task;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count($tasks) == 0) {
|
||||||
|
if (!$quiet) {
|
||||||
|
$this->io->success('Nothing to do');
|
||||||
|
}
|
||||||
|
return self::SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$quiet) {
|
||||||
|
$this->io->writeln('<info>Found ' . count($tasks) . ' task(s) to run</>');
|
||||||
|
$this->io->writeln('');
|
||||||
|
$this->io->writeln('<comment>Running tasks...</comment>');
|
||||||
|
}
|
||||||
|
|
||||||
|
$hasFailure = false;
|
||||||
|
|
||||||
|
// Run tasks
|
||||||
|
foreach ($tasks as $task) {
|
||||||
|
// Display the class we are going to use as info
|
||||||
|
$this->io->write('<info>Running task `' . $task['class'] . '`...</>');
|
||||||
|
$start = hrtime(true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
if (!($taskSuccess = $task['instance']->run())) {
|
||||||
|
throw new Exception('Task returned failure');
|
||||||
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$taskSuccess = false;
|
||||||
|
$hasFailure = true;
|
||||||
|
if ($quiet) {
|
||||||
|
$this->io->write('<info>Running task `' . $task['class'] . '`...</>');
|
||||||
|
}
|
||||||
|
$this->io->error('<error> [ERROR] ' . $e->getMessage() . ' </>');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$quiet && $taskSuccess) {
|
||||||
|
$end = hrtime(true);
|
||||||
|
|
||||||
|
$tdiff = round(($end-$start) / 1e+6);
|
||||||
|
$tunit = 'ms';
|
||||||
|
if ($tdiff > 1000) {
|
||||||
|
$tdiff = round($tdiff / 1000);
|
||||||
|
$tunit = 's';
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->io->writeln($tdiff . $tunit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Success
|
||||||
|
if (!$hasFailure) {
|
||||||
|
if (!$quiet) {
|
||||||
|
$this->io->success('Schedule success !');
|
||||||
|
}
|
||||||
|
return self::SUCCESS;
|
||||||
|
} else {
|
||||||
|
if (!$quiet) {
|
||||||
|
$this->io->error('Schedule failed !');
|
||||||
|
}
|
||||||
|
return self::FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
49
src/Bakery/ScheduleListCommand.php
Normal file
49
src/Bakery/ScheduleListCommand.php
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AVSDev UF Scheduler (https://avsdev.uk)
|
||||||
|
*
|
||||||
|
* @link https://git.avsdev.uk/avsdev/sprinkle-scheduler
|
||||||
|
* @license https://git.avsdev.uk/avsdev/sprinkle-scheduler/blob/master/LICENSE.md (LGPL-3.0 License)
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace UserFrosting\Sprinkle\Scheduler\Bakery;
|
||||||
|
|
||||||
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
use UserFrosting\System\Bakery\BaseCommand;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* schedule Bakery Command
|
||||||
|
* List scheduled tasks.
|
||||||
|
*
|
||||||
|
* @author Craig Williams (https://avsdev.uk)
|
||||||
|
*/
|
||||||
|
class ScheduleListCommand extends BaseCommand
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function configure()
|
||||||
|
{
|
||||||
|
$this->setName('schedule:list')
|
||||||
|
->setDescription('List all scheduled tasks available')
|
||||||
|
->setHelp('This command returns a list of scheduled tasks that can be called using the `schedule:run` command.');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function execute(InputInterface $input, OutputInterface $output)
|
||||||
|
{
|
||||||
|
$this->io->title('Scheduled Tasks List');
|
||||||
|
$tasks = $this->ci->scheduler->getTasks();
|
||||||
|
$tasks = array_map(function($task) {
|
||||||
|
unset($task['instance']);
|
||||||
|
return $task;
|
||||||
|
}, $tasks);
|
||||||
|
$this->io->table(['Schedule', 'Sprinkle', 'Name', 'Namespace'], $tasks);
|
||||||
|
|
||||||
|
return self::SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
92
src/Bakery/ScheduleRunCommand.php
Normal file
92
src/Bakery/ScheduleRunCommand.php
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AVSDev UF Scheduler (https://avsdev.uk)
|
||||||
|
*
|
||||||
|
* @link https://git.avsdev.uk/avsdev/sprinkle-scheduler
|
||||||
|
* @license https://git.avsdev.uk/avsdev/sprinkle-scheduler/blob/master/LICENSE.md (LGPL-3.0 License)
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace UserFrosting\Sprinkle\Scheduler\Bakery;
|
||||||
|
|
||||||
|
use Symfony\Component\Console\Input\InputArgument;
|
||||||
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
use UserFrosting\System\Bakery\BaseCommand;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* schedule:run Bakery Command
|
||||||
|
* Run scheduled tasks.
|
||||||
|
*
|
||||||
|
* @author Craig Williams (https://avsdev.uk)
|
||||||
|
*/
|
||||||
|
class ScheduleRunCommand extends BaseCommand
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function configure()
|
||||||
|
{
|
||||||
|
$this->setName('schedule:run')
|
||||||
|
->setDescription('Run a scheduled task now')
|
||||||
|
->setHelp('This command runs a task immediately, ignoring all schedule constraints.')
|
||||||
|
->addArgument('name', InputArgument::IS_ARRAY | InputArgument::REQUIRED, 'The name of the task(s). Separate multiple tasks with a space.');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function execute(InputInterface $input, OutputInterface $output)
|
||||||
|
{
|
||||||
|
$this->io->title('Running Task(s)');
|
||||||
|
|
||||||
|
$scheduler = $this->ci->scheduler;
|
||||||
|
|
||||||
|
// Get options
|
||||||
|
$names = $input->getArgument('name');
|
||||||
|
|
||||||
|
$hasFailure = false;
|
||||||
|
|
||||||
|
// Start by getting seeds
|
||||||
|
foreach ($names as $taskName) {
|
||||||
|
|
||||||
|
// Get task class and
|
||||||
|
try {
|
||||||
|
$taskClass = $scheduler->getTaskClass($taskName);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->io->error($e->getMessage());
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display the class we are going to use as info
|
||||||
|
$this->io->write('<info>Running task `' . get_class($taskClass) . '`...</>');
|
||||||
|
|
||||||
|
$start = hrtime(true);
|
||||||
|
try {
|
||||||
|
$scheduler->runTask($taskClass);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->io->writeln('<error>failed</>');
|
||||||
|
$this->io->error($e->getMessage());
|
||||||
|
$hasFailure = true;
|
||||||
|
}
|
||||||
|
$end = hrtime(true);
|
||||||
|
|
||||||
|
$tdiff = round(($end-$start) / 1e+6);
|
||||||
|
$tunit = 'ms';
|
||||||
|
if ($tdiff > 1000) {
|
||||||
|
$tdiff = round($tdiff / 1000);
|
||||||
|
$tunit = 's';
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->io->writeln($tdiff . $tunit);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$hasFailure) {
|
||||||
|
$this->io->success('Run task success !');
|
||||||
|
} else {
|
||||||
|
$this->io->error('Run task failed !');
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,4 +18,23 @@ use UserFrosting\System\Sprinkle\Sprinkle;
|
|||||||
*/
|
*/
|
||||||
class Scheduler extends Sprinkle
|
class Scheduler extends Sprinkle
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* Set static references to DI container in necessary classes.
|
||||||
|
*/
|
||||||
|
public function onSprinklesInitialized()
|
||||||
|
{
|
||||||
|
$this->registerStreams();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register Scheduler sprinkle locator streams.
|
||||||
|
*/
|
||||||
|
protected function registerStreams()
|
||||||
|
{
|
||||||
|
/** @var \UserFrosting\UniformResourceLocator\ResourceLocator $locator */
|
||||||
|
$locator = $this->ci->locator;
|
||||||
|
|
||||||
|
// Register scheduler sprinkle class streams
|
||||||
|
$locator->registerStream('tasks', '', \UserFrosting\SCHEDULED_TASKS_DIR);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
111
src/Scheduler/BaseTask.php
Normal file
111
src/Scheduler/BaseTask.php
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AVSDev UF Scheduler (https://avsdev.uk)
|
||||||
|
*
|
||||||
|
* @link https://git.avsdev.uk/avsdev/sprinkle-scheduler
|
||||||
|
* @license https://git.avsdev.uk/avsdev/sprinkle-scheduler/blob/master/LICENSE.md (LGPL-3.0 License)
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace UserFrosting\Sprinkle\Scheduler\Scheduler;
|
||||||
|
|
||||||
|
use Cron\CronExpression;
|
||||||
|
use Psr\Container\ContainerInterface;
|
||||||
|
use Illuminate\Console\Scheduling\ManagesFrequencies;
|
||||||
|
use Illuminate\Support\Facades\Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Task Class
|
||||||
|
* Base class for scheduled tasks.
|
||||||
|
*
|
||||||
|
* @author Craig Williams (https://avsdev.uk)
|
||||||
|
*/
|
||||||
|
abstract class BaseTask implements TaskInterface
|
||||||
|
{
|
||||||
|
use ManagesFrequencies;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The cron expression representing the event's frequency.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public $expression = '* * * * *';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The timezone the date should be evaluated on.
|
||||||
|
*
|
||||||
|
* @var \DateTimeZone|string
|
||||||
|
*/
|
||||||
|
public $timezone;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The human readable description of the task.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public $description;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ContainerInterface
|
||||||
|
*/
|
||||||
|
protected $ci;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param ContainerInterface $ci
|
||||||
|
*/
|
||||||
|
public function __construct(ContainerInterface $ci)
|
||||||
|
{
|
||||||
|
$this->ci = $ci;
|
||||||
|
$this->schedule();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function used to retrieve the cron expression for the task.
|
||||||
|
*/
|
||||||
|
public function cronExpression()
|
||||||
|
{
|
||||||
|
return $this->expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the given event should run based on the Cron expression.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isDue()
|
||||||
|
{
|
||||||
|
$date = Date::now();
|
||||||
|
|
||||||
|
if ($this->timezone) {
|
||||||
|
$date = $date->setTimezone($this->timezone);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (new CronExpression($this->expression))->isDue($date->toDateTimeString());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine the next due date for an event.
|
||||||
|
*
|
||||||
|
* @param \DateTimeInterface|string $currentTime
|
||||||
|
* @param int $nth
|
||||||
|
* @param bool $allowCurrentDate
|
||||||
|
* @return \Illuminate\Support\Carbon
|
||||||
|
*/
|
||||||
|
public function nextRunDate($currentTime = 'now', $nth = 0, $allowCurrentDate = false)
|
||||||
|
{
|
||||||
|
return Date::instance((new CronExpression($this->getExpression()))
|
||||||
|
->getNextRunDate($currentTime, $nth, $allowCurrentDate, $this->timezone));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function used to specify the schedule for the task.
|
||||||
|
*/
|
||||||
|
abstract public function schedule();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function used to specify what the task does.
|
||||||
|
*/
|
||||||
|
abstract public function run();
|
||||||
|
}
|
||||||
215
src/Scheduler/Scheduler.php
Normal file
215
src/Scheduler/Scheduler.php
Normal file
@@ -0,0 +1,215 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AVSDev UF Scheduler (https://avsdev.uk)
|
||||||
|
*
|
||||||
|
* @link https://git.avsdev.uk/avsdev/sprinkle-scheduler
|
||||||
|
* @license https://git.avsdev.uk/avsdev/sprinkle-scheduler/blob/master/LICENSE.md (LGPL-3.0 License)
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace UserFrosting\Sprinkle\Scheduler\Scheduler;
|
||||||
|
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
use Psr\Container\ContainerInterface;
|
||||||
|
use UserFrosting\UniformResourceLocator\Resource as ResourceInstance;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scheduler Class.
|
||||||
|
*
|
||||||
|
* Finds all tasks class across sprinkles
|
||||||
|
*
|
||||||
|
* @author Craig Williams (https://avsdev.uk)
|
||||||
|
*/
|
||||||
|
class Scheduler
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var ContainerInterface
|
||||||
|
*/
|
||||||
|
protected $ci;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string The resource locator scheme
|
||||||
|
*/
|
||||||
|
protected $scheme = 'tasks://';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Constructor.
|
||||||
|
*
|
||||||
|
* @param ContainerInterface $ci
|
||||||
|
*/
|
||||||
|
public function __construct(ContainerInterface $ci)
|
||||||
|
{
|
||||||
|
$this->ci = $ci;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loop all the available sprinkles and return a list of their tasks.
|
||||||
|
*
|
||||||
|
* @return array An array of all the task classes found for every sprinkle
|
||||||
|
*/
|
||||||
|
public function getTasks()
|
||||||
|
{
|
||||||
|
$tasks = $this->ci->locator->listResources($this->scheme, false, false);
|
||||||
|
|
||||||
|
return $this->loadTasks($tasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a single task info.
|
||||||
|
*
|
||||||
|
* @param string $name The task name
|
||||||
|
*
|
||||||
|
* @throws \Exception If task not found
|
||||||
|
*
|
||||||
|
* @return array The details about a task file [name, class, sprinkle]
|
||||||
|
*/
|
||||||
|
public function getTask($name)
|
||||||
|
{
|
||||||
|
// Get task resource
|
||||||
|
$taskResource = $this->ci->locator->getResource($this->scheme . $name . '.php');
|
||||||
|
|
||||||
|
// Make sure we found something
|
||||||
|
if (!$taskResource) {
|
||||||
|
throw new \Exception("Task $name not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the task info
|
||||||
|
return $this->getTaskDetails($taskResource);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the class instance of a task.
|
||||||
|
*
|
||||||
|
* @param string $name The task name
|
||||||
|
*
|
||||||
|
* @throws \Exception If class doesn't exist or is not right interface
|
||||||
|
*
|
||||||
|
* @return TaskInterface The task class instance
|
||||||
|
*/
|
||||||
|
public function getTaskClass($name)
|
||||||
|
{
|
||||||
|
// Try to get task info
|
||||||
|
$task = $this->getTask($name);
|
||||||
|
|
||||||
|
// Make sure class exist
|
||||||
|
$classPath = $task['class'];
|
||||||
|
if (!class_exists($classPath)) {
|
||||||
|
throw new \Exception("Task class `$classPath` not found. Make sure the class has the correct namespace.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new class instance
|
||||||
|
$taskClass = new $classPath($this->ci);
|
||||||
|
|
||||||
|
// Class must be an instance of `TaskerInterface`
|
||||||
|
if (!$taskClass instanceof TaskInterface) {
|
||||||
|
throw new \Exception('Task class must be an instance of `TaskerInterface`');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $taskClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run a task class.
|
||||||
|
*
|
||||||
|
* @param TaskInterface $task The task to run
|
||||||
|
*/
|
||||||
|
public function runTask(TaskInterface $task)
|
||||||
|
{
|
||||||
|
$task->run();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run a task based on it's name.
|
||||||
|
*
|
||||||
|
* @param string $taskName
|
||||||
|
*/
|
||||||
|
public function run($taskName)
|
||||||
|
{
|
||||||
|
$task = $this->getTaskClass($taskName);
|
||||||
|
$this->runTask($task);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a list of tasks due to be run
|
||||||
|
*/
|
||||||
|
public function dueTasks()
|
||||||
|
{
|
||||||
|
$tasks = $this->getTasks();
|
||||||
|
|
||||||
|
return array_filter($tasks, function($task) {
|
||||||
|
// Get the task's class
|
||||||
|
$classPath = $task['class'];
|
||||||
|
|
||||||
|
// Create a new class instance
|
||||||
|
$taskClass = new $classPath($this->ci);
|
||||||
|
|
||||||
|
// Class must be an instance of `TaskerInterface`
|
||||||
|
if (!$taskClass instanceof TaskInterface) {
|
||||||
|
throw new \Exception('Task class must be an instance of `TaskerInterface`');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $taskClass->isDue();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process tasks Resource into info.
|
||||||
|
*
|
||||||
|
* @param array $taskFiles List of tasks files
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function loadTasks(array $taskFiles)
|
||||||
|
{
|
||||||
|
$tasks = [];
|
||||||
|
|
||||||
|
foreach ($taskFiles as $taskFile) {
|
||||||
|
$tasks[] = $this->getTaskDetails($taskFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $tasks;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an array of task details including the class name and the sprinkle name.
|
||||||
|
*
|
||||||
|
* @param ResourceInstance $file The task file
|
||||||
|
*
|
||||||
|
* @return array The details about a task file [name, class, sprinkle]
|
||||||
|
*/
|
||||||
|
protected function getTaskDetails(ResourceInstance $file)
|
||||||
|
{
|
||||||
|
// Format the sprinkle name for the namespace
|
||||||
|
$sprinkleName = $file->getLocation()->getName();
|
||||||
|
$sprinkleName = Str::studly($sprinkleName);
|
||||||
|
|
||||||
|
// Getting base path, name and class name
|
||||||
|
$basePath = str_replace($file->getBasename(), '', $file->getBasePath());
|
||||||
|
$name = $basePath . $file->getFilename();
|
||||||
|
$className = str_replace('/', '\\', $basePath) . $file->getFilename();
|
||||||
|
$classPath = "\\UserFrosting\\Sprinkle\\$sprinkleName\\Scheduler\\Tasks\\$className";
|
||||||
|
|
||||||
|
if (!class_exists($classPath)) {
|
||||||
|
throw new \Exception("Task class `$className` not found. Make sure the class has the correct namespace.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new class instance
|
||||||
|
$taskClass = new $classPath($this->ci);
|
||||||
|
|
||||||
|
// Class must be an instance of `TaskerInterface`
|
||||||
|
if (!$taskClass instanceof TaskInterface) {
|
||||||
|
throw new \Exception('Task class must be an instance of `TaskerInterface`');
|
||||||
|
}
|
||||||
|
|
||||||
|
$schedule = $taskClass->cronExpression();
|
||||||
|
|
||||||
|
// Build the class name and namespace
|
||||||
|
return [
|
||||||
|
'schedule' => $schedule,
|
||||||
|
'sprinkle' => $sprinkleName,
|
||||||
|
'name' => $name,
|
||||||
|
'class' => $classPath,
|
||||||
|
'instance' => $taskClass,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
51
src/Scheduler/TaskInterface.php
Normal file
51
src/Scheduler/TaskInterface.php
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AVSDev UF Scheduler (https://avsdev.uk)
|
||||||
|
*
|
||||||
|
* @link https://git.avsdev.uk/avsdev/sprinkle-scheduler
|
||||||
|
* @license https://git.avsdev.uk/avsdev/sprinkle-scheduler/blob/master/LICENSE.md (LGPL-3.0 License)
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace UserFrosting\Sprinkle\Scheduler\Scheduler;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All tasks must implement this interface.
|
||||||
|
*
|
||||||
|
* @author Craig Williams (https://avsdev.uk)
|
||||||
|
*/
|
||||||
|
interface TaskInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Function used to retrieve the cron expression for the task.
|
||||||
|
*/
|
||||||
|
public function cronExpression();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the given event should run based on the Cron expression.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isDue();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine the next due date for an event.
|
||||||
|
*
|
||||||
|
* @param \DateTimeInterface|string $currentTime
|
||||||
|
* @param int $nth
|
||||||
|
* @param bool $allowCurrentDate
|
||||||
|
* @return \Illuminate\Support\Carbon
|
||||||
|
*/
|
||||||
|
public function nextRunDate($currentTime = 'now', $nth = 0, $allowCurrentDate = false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function used to specify the schedule for the task.
|
||||||
|
*/
|
||||||
|
public function schedule();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function used to specify what the task does.
|
||||||
|
*/
|
||||||
|
public function run();
|
||||||
|
}
|
||||||
42
src/ServicesProvider/ServicesProvider.php
Normal file
42
src/ServicesProvider/ServicesProvider.php
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AVSDev UF Scheduler (https://avsdev.uk)
|
||||||
|
*
|
||||||
|
* @link https://git.avsdev.uk/avsdev/sprinkle-scheduler
|
||||||
|
* @license https://git.avsdev.uk/avsdev/sprinkle-scheduler/blob/master/LICENSE.md (LGPL-3.0 License)
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace UserFrosting\Sprinkle\Scheduler\ServicesProvider;
|
||||||
|
|
||||||
|
use Illuminate\Container\Container;
|
||||||
|
use Psr\Container\ContainerInterface;
|
||||||
|
use UserFrosting\Sprinkle\Scheduler\Scheduler\Scheduler;
|
||||||
|
use UserFrosting\Sprinkle\Core\ServicesProvider\BaseServicesProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sheduler services provider.
|
||||||
|
*
|
||||||
|
* Registers core services for the Sheduler sprinkle.
|
||||||
|
*
|
||||||
|
* @author Craig Williams (https://avsdev.uk)
|
||||||
|
*/
|
||||||
|
class ServicesProvider
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Register Sheduler's services.
|
||||||
|
*
|
||||||
|
* @param ContainerInterface $container A DI container implementing ArrayAccess and psr-container.
|
||||||
|
*/
|
||||||
|
public function register(ContainerInterface $container)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Return an instance of the task scheduler
|
||||||
|
*
|
||||||
|
* @return \UserFrosting\Sprinkle\Scheduler\Scheduler\Scheduler
|
||||||
|
*/
|
||||||
|
$container['scheduler'] = function ($c) {
|
||||||
|
return new Scheduler($c);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user