Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Calculate next run date, get last run date, run single task #60

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 48 additions & 22 deletions src/Cronner/Cronner.php
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,8 @@ public function addTasks($tasks) : self

/**
* Runs all cron tasks.
*
* @param DateTime $now
*/
public function run(DateTime $now = NULL)
{
Expand All @@ -190,28 +192,7 @@ public function run(DateTime $now = NULL)
}

foreach ($this->tasks as $task) {
try {
$name = $task->getName();
if ($task->shouldBeRun($now)) {
if ($this->criticalSection->enter($name)) {
$this->onTaskBegin($this, $task);
$task($now);
$this->onTaskFinished($this, $task);
$this->criticalSection->leave($name);
}
}
} catch (Exception $e) {
$this->onTaskError($this, $e, $task);
$name = $task->getName();
if ($this->criticalSection->isEntered($name)) {
$this->criticalSection->leave($name);
}
if ($e instanceof RuntimeException) {
throw $e; // Throw exception if it is Cronner Runtime exception
} elseif ($this->skipFailedTask === FALSE) {
throw $e; // Throw exception if failed task should not be skipped
}
}
$this->executeTask($task, $now);
}
}

Expand Down Expand Up @@ -242,4 +223,49 @@ private function createIdFromObject($tasks) : string
return sha1(get_class($tasks));
}

/**
* @param Task $task
* @param DateTime $now
* @param bool $forceRun
* @throws Exception
*/
private function executeTask(Task $task, DateTime $now, $forceRun = FALSE)
{
try {
$name = $task->getName();
if ($task->shouldBeRun($now) || $forceRun) {
if ($this->criticalSection->enter($name)) {
$this->onTaskBegin($this, $task);
$task($now);
$this->onTaskFinished($this, $task);
$this->criticalSection->leave($name);
}
}
} catch (Exception $e) {
$this->onTaskError($this, $e, $task);
$name = $task->getName();
if ($this->criticalSection->isEntered($name)) {
$this->criticalSection->leave($name);
}
if ($e instanceof RuntimeException) {
throw $e; // Throw exception if it is Cronner Runtime exception
} elseif ($this->skipFailedTask === FALSE) {
throw $e; // Throw exception if failed task should not be skipped
}
}
}

/**
* @param Task $task
* @param bool $forceRun
*/
public function runTask(Task $task, $forceRun = FALSE)
{
if ($this->maxExecutionTime !== NULL) {
set_time_limit($this->maxExecutionTime);
}

$this->executeTask($task, new DateTime(), $forceRun);
}

}
92 changes: 92 additions & 0 deletions src/Cronner/Tasks/Task.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Nette\Reflection\Method;
use ReflectionClass;
use stekycz\Cronner\ITimestampStorage;
use stekycz\Cronner\Tasks\Parameters;

final class Task
{
Expand Down Expand Up @@ -110,4 +111,95 @@ private function getParameters() : Parameters
return $this->parameters;
}

/**
* @return DateTime|null
*/
public function getLastRun()
{
$this->timestampStorage->setTaskName($this->getName());
return $this->timestampStorage->loadLastRunTime();
}

/**
* @param DateTime|null $startDate
* @return DateTime
*/
public function getNextRun(DateTime $startDate = NULL) : DateTime
{
$startDate = is_null($startDate) ? new DateTime() : clone $startDate;

$days = [
1 => 'Mon',
2 => 'Tue',
3 => 'Wed',
4 => 'Thu',
5 => 'Fri',
6 => 'Sat',
7 => 'Sun',
];

$lastRun = $this->getLastRun();

$parameters = Parameters::parseParameters($this->getMethodReflection());

if (is_null($parameters[Parameters::PERIOD]) && is_null($parameters[Parameters::DAYS]) && is_null($parameters[Parameters::TIME])) {
return $startDate;
}

if(is_null($parameters[Parameters::DAYS])) {
$parameters[Parameters::DAYS] = $days;
}

if(is_null($parameters[Parameters::TIME])) {
$parameters[Parameters::TIME][] = [
'from' => '00:00',
'to' => NULL,
];
}

$nextRun = $startDate;

if (!is_null($parameters[Parameters::PERIOD]) && !is_null($lastRun)) {
$nextRun = $lastRun->modify(sprintf('+ %s', $parameters[Parameters::PERIOD]));
$nextRun = $nextRun < $startDate ? $startDate : $nextRun;
}

$time = $nextRun->format('H:i');
$day = $nextRun->format('N');
$seconds = $nextRun->format('s');

$nextTimes = array_filter(
$parameters[Parameters::TIME],
function ($definedTimes) use ($nextRun) {
return $definedTimes['to'] === NULL || sprintf('%s:00', $definedTimes['to']) >= $nextRun->format('H:i:s');
});

if (in_array($nextRun->format('D'), $parameters[Parameters::DAYS]) && !empty($nextTimes)) {
$nextTime = reset($nextTimes)['from'];

if($nextTime <= $time) {
$nextTime = $time;
} else {
$seconds = 0;
}

$timeParts = explode(':', $nextTime);
$nextRun->setTime((int) $timeParts[0], (int) $timeParts[1], (int) $seconds);

} else {
$day++;
$day = $day > 7 ? 1 : $day;

while(!in_array($days[$day], $parameters[Parameters::DAYS])) {
$day = ($day + 1) > 7 ? 0 : $day;
$day++;
}

$nextRun->modify(sprintf('next %s', $days[$day]));
$timeParts = explode(':', reset($parameters[Parameters::TIME])['from']);
$nextRun->setTime((int) $timeParts[0], (int) $timeParts[1], 0);
}

return $nextRun;
}
}
93 changes: 93 additions & 0 deletions tests/CronnerTests/Tasks/Task.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,99 @@ class TaskTest extends \TestCase
Assert::true($task->shouldBeRun(new Nette\Utils\DateTime('2014-08-15 09:17:00')));
}

/**
* @dataProvider dataProviderNextRun
* @param string $methodName
* @param string $now
* @param string|null $lastRunTime
* @param string $nextRunTime
*/
public function testNextRun(string $methodName, string $now, string $lastRunTime = NULL, string $nextRunTime)
{
$now = new \DateTime($now);
$nextRunTime = new \DateTime($nextRunTime);

$method = (new \Nette\Reflection\ClassType($this->object))->getMethod($methodName);

$timestampStorage = Mockery::mock(ITimestampStorage::class);
$timestampStorage->shouldReceive("loadLastRunTime")->atLeast(1)->andReturnUsing(function() use ($lastRunTime) {
return $lastRunTime ? \DateTime::createFromFormat('Y-m-d H:i:s', $lastRunTime) : NULL;
});

$timestampStorage->shouldReceive("setTaskName")->atLeast(1);

$task = new Task($this->object, $method, $timestampStorage);

Assert::equal($nextRunTime, $task->getNextRun($now));
Assert::same(TRUE, $task->shouldBeRun($nextRunTime));
}

public function dataProviderNextRun() : array
{
return [
// Test 01
['test01', '2013-02-01 12:00:00', NULL, '2013-02-01 12:00:00'],
['test01', '2013-02-01 12:00:00', '2013-02-01 11:59:59', '2013-02-01 12:04:59'],
['test01', '2013-02-01 12:00:00', '2013-02-01 12:00:00', '2013-02-01 12:05:00'],
['test01', '2013-02-01 12:10:00', '2013-02-01 12:06:55', '2013-02-01 12:11:55'],
['test01', '2013-02-02 15:20:23', '2012-02-01 12:06:55', '2013-02-02 15:20:23'],
// Test 02
['test02', '2013-02-01 12:00:00', NULL, '2013-02-01 15:00:00'],
['test02', '2013-02-01 09:00:00', NULL, '2013-02-01 09:00:00'],
['test02', '2013-02-01 09:00:38', '2013-02-01 09:00:00', '2013-02-01 10:00:00'],
['test02', '2013-02-01 09:01:38', '2013-02-01 09:00:01', '2013-02-01 15:00:00'],
['test02', '2013-02-01 14:01:38', NULL, '2013-02-01 15:00:00'],
['test02', '2013-02-01 14:01:38', '2013-02-01 09:00:01', '2013-02-01 15:00:00'],
['test02', '2013-02-01 15:01:38', '2013-02-01 09:00:01', '2013-02-01 15:01:38'],
['test02', '2013-02-01 15:01:38', '2013-02-01 15:00:00', '2013-02-01 16:00:00'],
['test02', '2013-02-01 15:01:38', '2013-02-01 15:00:01', '2013-02-04 09:00:00'],
['test02', '2013-02-04 14:01:38', '2013-02-04 09:00:01', '2013-02-04 15:00:00'],
['test02', '2013-02-04 15:01:38', '2013-02-04 09:00:01', '2013-02-04 15:01:38'],
['test02', '2013-02-04 15:01:38', '2013-02-04 15:00:00', '2013-02-04 16:00:00'],
['test02', '2013-02-04 15:01:38', '2013-02-04 15:00:01', '2013-02-06 09:00:00'],
['test02', '2013-02-04 15:01:38', NULL, '2013-02-04 15:01:38'],
['test02', '2013-02-05 14:01:38', '2013-02-04 09:00:01', '2013-02-06 09:00:00'],
['test02', '2013-02-05 06:01:38', '2013-02-04 09:00:01', '2013-02-06 09:00:00'],
['test02', '2013-02-05 9:00:00', '2013-02-04 15:00:00', '2013-02-06 09:00:00'],
['test02', '2013-02-05 15:01:38', '2013-02-04 15:00:01', '2013-02-06 09:00:00'],
['test02', '2013-02-05 15:01:38', NULL, '2013-02-06 09:00:00'],
['test02', '2013-02-05 9:00:00', NULL, '2013-02-06 09:00:00'],
// Test 03
['test03', '2013-02-01 09:00:00', NULL, '2013-02-01 09:00:00'],
['test03', '2013-02-01 09:00:38', '2013-02-01 09:00:00', '2013-02-01 09:17:00'],
['test03', '2013-02-01 09:01:38', '2013-02-01 09:00:01', '2013-02-01 09:17:01'],
['test03', '2013-02-01 10:44:38', '2013-02-01 09:00:01', '2013-02-01 10:44:38'],
['test03', '2013-02-01 10:45:00', '2013-02-01 09:00:01', '2013-02-01 10:45:00'],
['test03', '2013-02-01 10:45:01', '2013-02-01 09:00:01', '2013-02-04 09:00:00'],
['test03', '2013-02-01 12:00:00', NULL, '2013-02-04 09:00:00'],
['test03', '2013-02-01 12:00:00', '2013-02-01 09:00:01', '2013-02-04 09:00:00'],
['test03', '2013-02-03 09:00:00', '2013-02-01 09:00:01', '2013-02-04 09:00:00'],
['test03', '2013-02-05 09:01:38', NULL, '2013-02-05 09:01:38'],
['test03', '2013-02-05 09:01:38', '2013-02-01 09:00:01', '2013-02-05 09:01:38'],
['test03', '2013-02-05 09:18:38', '2013-02-05 09:00:00', '2013-02-05 09:18:38'],
['test03', '2013-02-05 10:45:00', NULL, '2013-02-05 10:45:00'],
['test03', '2013-02-05 10:45:01', NULL, '2013-02-06 09:00:00'],
['test03', '2013-02-05 10:45:00', '2013-02-05 10:28:59', '2013-02-06 09:00:00'],
['test03', '2013-02-05 10:45:00', '2013-02-05 10:28:00', '2013-02-05 10:45:00'],
['test03', '2013-02-05 10:45:00', '2013-02-05 10:28:05', '2013-02-06 09:00:00'],
// Test 04
['test04', '2013-02-01 09:00:00', NULL, '2013-02-02 00:00:00'],
['test04', '2013-02-01 09:00:38', '2013-02-01 09:00:00', '2013-02-02 09:00:00'],
['test04', '2013-02-01 09:00:38', '2013-02-01 09:00:38', '2013-02-02 09:00:38'],
['test04', '2013-02-01 09:00:38', '2012-02-01 09:00:38', '2013-02-02 00:00:00'],
['test04', '2013-02-02 09:01:38', NULL, '2013-02-02 09:01:38'],
['test04', '2013-02-02 09:01:38', '2013-02-01 00:01:38', '2013-02-02 09:01:38'],
['test04', '2013-02-02 00:01:38', '2013-02-01 00:02:38', '2013-02-02 00:02:38'],
['test04', '2013-02-02 09:01:38', '2013-02-02 00:01:38', '2013-02-03 00:01:38'],
['test04', '2013-02-03 00:01:38', '2013-02-02 00:01:38', '2013-02-03 00:01:38'],
['test04', '2013-02-03 00:01:37', '2013-02-02 00:01:38', '2013-02-03 00:01:38'],
['test04', '2013-02-03 10:44:38', '2013-02-03 00:00:01', '2013-02-09 00:00:00'],
['test04', '2013-02-03 10:44:38', '2013-02-03 00:26:01', '2013-02-09 00:00:00'],
['test04', '2013-02-04 00:00:00', NULL, '2013-02-09 00:00:00'],
['test04', '2013-02-06 23:46:27', NULL, '2013-02-09 00:00:00'],
];
}

}

run(new TaskTest());