Skip to content

Commit 333d2ef

Browse files
[10.x] Improve display of sub-minute tasks in schedule:list command. (#47720)
* Display next due time correctly for sub-minute events * Display the repeat expression for sub-minute events * formatting --------- Co-authored-by: Taylor Otwell <[email protected]>
1 parent a7244f4 commit 333d2ef

File tree

1 file changed

+119
-56
lines changed

1 file changed

+119
-56
lines changed

src/Illuminate/Console/Scheduling/ScheduleListCommand.php

Lines changed: 119 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -62,87 +62,130 @@ public function handle(Schedule $schedule)
6262

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

65+
$repeatExpressionSpacing = $this->getRepeatExpressionSpacing($events);
66+
6567
$timezone = new DateTimeZone($this->option('timezone') ?? config('app.timezone'));
6668

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

69-
$events = $events->map(function ($event) use ($terminalWidth, $expressionSpacing, $timezone) {
70-
$expression = $this->formatCronExpression($event->expression, $expressionSpacing);
71+
$events = $events->map(function ($event) use ($terminalWidth, $expressionSpacing, $repeatExpressionSpacing, $timezone) {
72+
return $this->listEvent($event, $terminalWidth, $expressionSpacing, $repeatExpressionSpacing, $timezone);
73+
});
7174

72-
$command = $event->command ?? '';
75+
$this->line(
76+
$events->flatten()->filter()->prepend('')->push('')->toArray()
77+
);
78+
}
7379

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

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

83-
if ($event instanceof CallbackEvent) {
84-
$command = $event->getSummaryForDisplay();
93+
/**
94+
* Get the spacing to be used on each event row.
95+
*
96+
* @param \Illuminate\Support\Collection $events
97+
* @return int
98+
*/
99+
private function getRepeatExpressionSpacing($events)
100+
{
101+
return $events->map(fn ($event) => mb_strlen($this->getRepeatExpression($event)))->max();
102+
}
85103

86-
if (in_array($command, ['Closure', 'Callback'])) {
87-
$command = 'Closure at: '.$this->getClosureLocation($event);
88-
}
89-
}
104+
/**
105+
* List the given even in the console.
106+
*
107+
* @param \Illuminate\Console\Scheduling\Event
108+
* @param int $terminalWidth
109+
* @param array $expressionSpacing
110+
* @param int $repeatExpressionSpacing
111+
* @param array $repeatExpressionSpacing
112+
* @param \DateTimeZone $timezone
113+
* @return \Illuminate\Support\DateTimeZone
114+
*/
115+
private function listEvent($event, $terminalWidth, $expressionSpacing, $repeatExpressionSpacing, $timezone)
116+
{
117+
$expression = $this->formatCronExpression($event->expression, $expressionSpacing);
90118

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

93-
$nextDueDateLabel = 'Next Due:';
121+
$command = $event->command ?? '';
94122

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

97-
$nextDueDate = $this->output->isVerbose()
98-
? $nextDueDate->format('Y-m-d H:i:s P')
99-
: $nextDueDate->diffForHumans();
125+
if (! $this->output->isVerbose()) {
126+
$command = str_replace([Application::phpBinary(), Application::artisanBinary()], [
127+
'php',
128+
preg_replace("#['\"]#", '', Application::artisanBinary()),
129+
], $command);
130+
}
100131

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

103-
$dots = str_repeat('.', max(
104-
$terminalWidth - mb_strlen($expression.$command.$nextDueDateLabel.$nextDueDate.$hasMutex) - 8, 0
105-
));
135+
if (in_array($command, ['Closure', 'Callback'])) {
136+
$command = 'Closure at: '.$this->getClosureLocation($event);
137+
}
138+
}
106139

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

110-
return [sprintf(
111-
' <fg=yellow>%s</> %s<fg=#6C7280>%s %s%s %s</>',
112-
$expression,
113-
$command,
114-
$dots,
115-
$hasMutex,
116-
$nextDueDateLabel,
117-
$nextDueDate
118-
), $this->output->isVerbose() && mb_strlen($description) > 1 ? sprintf(
119-
' <fg=#6C7280>%s%s %s</>',
120-
str_repeat(' ', mb_strlen($expression) + 2),
121-
'',
122-
$description
123-
) : ''];
124-
});
142+
$nextDueDateLabel = 'Next Due:';
125143

126-
$this->line(
127-
$events->flatten()->filter()->prepend('')->push('')->toArray()
128-
);
144+
$nextDueDate = $this->getNextDueDateForEvent($event, $timezone);
145+
146+
$nextDueDate = $this->output->isVerbose()
147+
? $nextDueDate->format('Y-m-d H:i:s P')
148+
: $nextDueDate->diffForHumans();
149+
150+
$hasMutex = $event->mutex->exists($event) ? 'Has Mutex › ' : '';
151+
152+
$dots = str_repeat('.', max(
153+
$terminalWidth - mb_strlen($expression.$repeatExpression.$command.$nextDueDateLabel.$nextDueDate.$hasMutex) - 8, 0
154+
));
155+
156+
// Highlight the parameters...
157+
$command = preg_replace("#(php artisan [\w\-:]+) (.+)#", '$1 <fg=yellow;options=bold>$2</>', $command);
158+
159+
return [sprintf(
160+
' <fg=yellow>%s</> <fg=#6C7280>%s</> %s<fg=#6C7280>%s %s%s %s</>',
161+
$expression,
162+
$repeatExpression,
163+
$command,
164+
$dots,
165+
$hasMutex,
166+
$nextDueDateLabel,
167+
$nextDueDate
168+
), $this->output->isVerbose() && mb_strlen($description) > 1 ? sprintf(
169+
' <fg=#6C7280>%s%s %s</>',
170+
str_repeat(' ', mb_strlen($expression) + 2),
171+
'',
172+
$description
173+
) : ''];
129174
}
130175

131176
/**
132-
* Gets the spacing to be used on each event row.
177+
* Get the repeat expression for an event.
133178
*
134-
* @param \Illuminate\Support\Collection $events
135-
* @return array<int, int>
179+
* @param \Illuminate\Console\Scheduling\Event $event
180+
* @return string
136181
*/
137-
private function getCronExpressionSpacing($events)
182+
private function getRepeatExpression($event)
138183
{
139-
$rows = $events->map(fn ($event) => array_map('mb_strlen', preg_split("/\s+/", $event->expression)));
140-
141-
return collect($rows[0] ?? [])->keys()->map(fn ($key) => $rows->max($key))->all();
184+
return $event->isRepeatable() ? "{$event->repeatSeconds}s " : '';
142185
}
143186

144187
/**
145-
* Sorts the events by due date if option set.
188+
* Sort the events by due date if option set.
146189
*
147190
* @param \Illuminate\Support\Collection $events
148191
* @param \DateTimeZone $timezone
@@ -164,15 +207,35 @@ private function sortEvents(\Illuminate\Support\Collection $events, DateTimeZone
164207
*/
165208
private function getNextDueDateForEvent($event, DateTimeZone $timezone)
166209
{
167-
return Carbon::instance(
210+
$nextDueDate = Carbon::instance(
168211
(new CronExpression($event->expression))
169212
->getNextRunDate(Carbon::now()->setTimezone($event->timezone))
170213
->setTimezone($timezone)
171214
);
215+
216+
if (! $event->isRepeatable()) {
217+
return $nextDueDate;
218+
}
219+
220+
$previousDueDate = Carbon::instance(
221+
(new CronExpression($event->expression))
222+
->getPreviousRunDate(Carbon::now()->setTimezone($event->timezone), allowCurrentDate: true)
223+
->setTimezone($timezone)
224+
);
225+
226+
$now = Carbon::now()->setTimezone($event->timezone);
227+
228+
if (! $now->copy()->startOfMinute()->eq($previousDueDate)) {
229+
return $nextDueDate;
230+
}
231+
232+
return $now
233+
->endOfSecond()
234+
->ceilSeconds($event->repeatSeconds);
172235
}
173236

174237
/**
175-
* Formats the cron expression based on the spacing provided.
238+
* Format the cron expression based on the spacing provided.
176239
*
177240
* @param string $expression
178241
* @param array<int, int> $spacing

0 commit comments

Comments
 (0)