@@ -62,87 +62,130 @@ public function handle(Schedule $schedule)
62
62
63
63
$ expressionSpacing = $ this ->getCronExpressionSpacing ($ events );
64
64
65
+ $ repeatExpressionSpacing = $ this ->getRepeatExpressionSpacing ($ events );
66
+
65
67
$ timezone = new DateTimeZone ($ this ->option ('timezone ' ) ?? config ('app.timezone ' ));
66
68
67
69
$ events = $ this ->sortEvents ($ events , $ timezone );
68
70
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
+ });
71
74
72
- $ command = $ event ->command ?? '' ;
75
+ $ this ->line (
76
+ $ events ->flatten ()->filter ()->prepend ('' )->push ('' )->toArray ()
77
+ );
78
+ }
73
79
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 )));
75
89
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
+ }
82
92
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
+ }
85
103
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 );
90
118
91
- $ command = mb_strlen ( $ command ) > 1 ? "{ $ command } " : '' ;
119
+ $ repeatExpression = str_pad ( $ this -> getRepeatExpression ( $ event ), $ repeatExpressionSpacing ) ;
92
120
93
- $ nextDueDateLabel = ' Next Due: ' ;
121
+ $ command = $ event -> command ?? ' ' ;
94
122
95
- $ nextDueDate = $ this -> getNextDueDateForEvent ( $ event , $ timezone ) ;
123
+ $ description = $ event -> description ?? '' ;
96
124
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
+ }
100
131
101
- $ hasMutex = $ event ->mutex ->exists ($ event ) ? 'Has Mutex › ' : '' ;
132
+ if ($ event instanceof CallbackEvent) {
133
+ $ command = $ event ->getSummaryForDisplay ();
102
134
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
+ }
106
139
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 } " : '' ;
109
141
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: ' ;
125
143
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
+ ) : '' ];
129
174
}
130
175
131
176
/**
132
- * Gets the spacing to be used on each event row .
177
+ * Get the repeat expression for an event.
133
178
*
134
- * @param \Illuminate\Support\Collection $events
135
- * @return array<int, int>
179
+ * @param \Illuminate\Console\Scheduling\Event $event
180
+ * @return string
136
181
*/
137
- private function getCronExpressionSpacing ( $ events )
182
+ private function getRepeatExpression ( $ event )
138
183
{
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 " : '' ;
142
185
}
143
186
144
187
/**
145
- * Sorts the events by due date if option set.
188
+ * Sort the events by due date if option set.
146
189
*
147
190
* @param \Illuminate\Support\Collection $events
148
191
* @param \DateTimeZone $timezone
@@ -164,15 +207,35 @@ private function sortEvents(\Illuminate\Support\Collection $events, DateTimeZone
164
207
*/
165
208
private function getNextDueDateForEvent ($ event , DateTimeZone $ timezone )
166
209
{
167
- return Carbon::instance (
210
+ $ nextDueDate = Carbon::instance (
168
211
(new CronExpression ($ event ->expression ))
169
212
->getNextRunDate (Carbon::now ()->setTimezone ($ event ->timezone ))
170
213
->setTimezone ($ timezone )
171
214
);
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 );
172
235
}
173
236
174
237
/**
175
- * Formats the cron expression based on the spacing provided.
238
+ * Format the cron expression based on the spacing provided.
176
239
*
177
240
* @param string $expression
178
241
* @param array<int, int> $spacing
0 commit comments