Skip to content

Commit

Permalink
[10.x] Improve display of sub-minute tasks in schedule:list command. (
Browse files Browse the repository at this point in the history
#47720)

* Display next due time correctly for sub-minute events

* Display the repeat expression for sub-minute events

* formatting

---------

Co-authored-by: Taylor Otwell <taylor@laravel.com>
  • Loading branch information
jessarcher and taylorotwell committed Jul 12, 2023
1 parent a7244f4 commit 333d2ef
Showing 1 changed file with 119 additions and 56 deletions.
175 changes: 119 additions & 56 deletions src/Illuminate/Console/Scheduling/ScheduleListCommand.php
Expand Up @@ -62,87 +62,130 @@ public function handle(Schedule $schedule)

$expressionSpacing = $this->getCronExpressionSpacing($events);

$repeatExpressionSpacing = $this->getRepeatExpressionSpacing($events);

$timezone = new DateTimeZone($this->option('timezone') ?? config('app.timezone'));

$events = $this->sortEvents($events, $timezone);

$events = $events->map(function ($event) use ($terminalWidth, $expressionSpacing, $timezone) {
$expression = $this->formatCronExpression($event->expression, $expressionSpacing);
$events = $events->map(function ($event) use ($terminalWidth, $expressionSpacing, $repeatExpressionSpacing, $timezone) {
return $this->listEvent($event, $terminalWidth, $expressionSpacing, $repeatExpressionSpacing, $timezone);
});

$command = $event->command ?? '';
$this->line(
$events->flatten()->filter()->prepend('')->push('')->toArray()
);
}

$description = $event->description ?? '';
/**
* Get the spacing to be used on each event row.
*
* @param \Illuminate\Support\Collection $events
* @return array<int, int>
*/
private function getCronExpressionSpacing($events)
{
$rows = $events->map(fn ($event) => array_map('mb_strlen', preg_split("/\s+/", $event->expression)));

if (! $this->output->isVerbose()) {
$command = str_replace([Application::phpBinary(), Application::artisanBinary()], [
'php',
preg_replace("#['\"]#", '', Application::artisanBinary()),
], $command);
}
return collect($rows[0] ?? [])->keys()->map(fn ($key) => $rows->max($key))->all();
}

if ($event instanceof CallbackEvent) {
$command = $event->getSummaryForDisplay();
/**
* Get the spacing to be used on each event row.
*
* @param \Illuminate\Support\Collection $events
* @return int
*/
private function getRepeatExpressionSpacing($events)
{
return $events->map(fn ($event) => mb_strlen($this->getRepeatExpression($event)))->max();
}

if (in_array($command, ['Closure', 'Callback'])) {
$command = 'Closure at: '.$this->getClosureLocation($event);
}
}
/**
* List the given even in the console.
*
* @param \Illuminate\Console\Scheduling\Event
* @param int $terminalWidth
* @param array $expressionSpacing
* @param int $repeatExpressionSpacing
* @param array $repeatExpressionSpacing
* @param \DateTimeZone $timezone
* @return \Illuminate\Support\DateTimeZone
*/
private function listEvent($event, $terminalWidth, $expressionSpacing, $repeatExpressionSpacing, $timezone)
{
$expression = $this->formatCronExpression($event->expression, $expressionSpacing);

$command = mb_strlen($command) > 1 ? "{$command} " : '';
$repeatExpression = str_pad($this->getRepeatExpression($event), $repeatExpressionSpacing);

$nextDueDateLabel = 'Next Due:';
$command = $event->command ?? '';

$nextDueDate = $this->getNextDueDateForEvent($event, $timezone);
$description = $event->description ?? '';

$nextDueDate = $this->output->isVerbose()
? $nextDueDate->format('Y-m-d H:i:s P')
: $nextDueDate->diffForHumans();
if (! $this->output->isVerbose()) {
$command = str_replace([Application::phpBinary(), Application::artisanBinary()], [
'php',
preg_replace("#['\"]#", '', Application::artisanBinary()),
], $command);
}

$hasMutex = $event->mutex->exists($event) ? 'Has Mutex › ' : '';
if ($event instanceof CallbackEvent) {
$command = $event->getSummaryForDisplay();

$dots = str_repeat('.', max(
$terminalWidth - mb_strlen($expression.$command.$nextDueDateLabel.$nextDueDate.$hasMutex) - 8, 0
));
if (in_array($command, ['Closure', 'Callback'])) {
$command = 'Closure at: '.$this->getClosureLocation($event);
}
}

// Highlight the parameters...
$command = preg_replace("#(php artisan [\w\-:]+) (.+)#", '$1 <fg=yellow;options=bold>$2</>', $command);
$command = mb_strlen($command) > 1 ? "{$command} " : '';

return [sprintf(
' <fg=yellow>%s</> %s<fg=#6C7280>%s %s%s %s</>',
$expression,
$command,
$dots,
$hasMutex,
$nextDueDateLabel,
$nextDueDate
), $this->output->isVerbose() && mb_strlen($description) > 1 ? sprintf(
' <fg=#6C7280>%s%s %s</>',
str_repeat(' ', mb_strlen($expression) + 2),
'⇁',
$description
) : ''];
});
$nextDueDateLabel = 'Next Due:';

$this->line(
$events->flatten()->filter()->prepend('')->push('')->toArray()
);
$nextDueDate = $this->getNextDueDateForEvent($event, $timezone);

$nextDueDate = $this->output->isVerbose()
? $nextDueDate->format('Y-m-d H:i:s P')
: $nextDueDate->diffForHumans();

$hasMutex = $event->mutex->exists($event) ? 'Has Mutex › ' : '';

$dots = str_repeat('.', max(
$terminalWidth - mb_strlen($expression.$repeatExpression.$command.$nextDueDateLabel.$nextDueDate.$hasMutex) - 8, 0
));

// Highlight the parameters...
$command = preg_replace("#(php artisan [\w\-:]+) (.+)#", '$1 <fg=yellow;options=bold>$2</>', $command);

return [sprintf(
' <fg=yellow>%s</> <fg=#6C7280>%s</> %s<fg=#6C7280>%s %s%s %s</>',
$expression,
$repeatExpression,
$command,
$dots,
$hasMutex,
$nextDueDateLabel,
$nextDueDate
), $this->output->isVerbose() && mb_strlen($description) > 1 ? sprintf(
' <fg=#6C7280>%s%s %s</>',
str_repeat(' ', mb_strlen($expression) + 2),
'⇁',
$description
) : ''];
}

/**
* Gets the spacing to be used on each event row.
* Get the repeat expression for an event.
*
* @param \Illuminate\Support\Collection $events
* @return array<int, int>
* @param \Illuminate\Console\Scheduling\Event $event
* @return string
*/
private function getCronExpressionSpacing($events)
private function getRepeatExpression($event)
{
$rows = $events->map(fn ($event) => array_map('mb_strlen', preg_split("/\s+/", $event->expression)));

return collect($rows[0] ?? [])->keys()->map(fn ($key) => $rows->max($key))->all();
return $event->isRepeatable() ? "{$event->repeatSeconds}s " : '';
}

/**
* Sorts the events by due date if option set.
* Sort the events by due date if option set.
*
* @param \Illuminate\Support\Collection $events
* @param \DateTimeZone $timezone
Expand All @@ -164,15 +207,35 @@ private function sortEvents(\Illuminate\Support\Collection $events, DateTimeZone
*/
private function getNextDueDateForEvent($event, DateTimeZone $timezone)
{
return Carbon::instance(
$nextDueDate = Carbon::instance(
(new CronExpression($event->expression))
->getNextRunDate(Carbon::now()->setTimezone($event->timezone))
->setTimezone($timezone)
);

if (! $event->isRepeatable()) {
return $nextDueDate;
}

$previousDueDate = Carbon::instance(
(new CronExpression($event->expression))
->getPreviousRunDate(Carbon::now()->setTimezone($event->timezone), allowCurrentDate: true)
->setTimezone($timezone)
);

$now = Carbon::now()->setTimezone($event->timezone);

if (! $now->copy()->startOfMinute()->eq($previousDueDate)) {
return $nextDueDate;
}

return $now
->endOfSecond()
->ceilSeconds($event->repeatSeconds);
}

/**
* Formats the cron expression based on the spacing provided.
* Format the cron expression based on the spacing provided.
*
* @param string $expression
* @param array<int, int> $spacing
Expand Down

0 comments on commit 333d2ef

Please sign in to comment.