December 7, 2016 —John Koster
Laravel provides a manageable system for scheduling tasks and commands out of the box, the command scheduler. It is a replacement for systems like cron or Windows Task Scheduler in the sense that the actual scheduling of commands is done using Laravel's task scheduler, not with third party systems (the third party systems would then be required to call the Laravel task scheduler, however). The benefit to this is that all the scheduled tasks are managed through your applications code and can be distributed as part of the application itself. Another added benefit is that since the tasks are maintained via application code, they can be tracked using source control systems such as git.
The scheduling system itself is fairly straightforward. There is a schedule
(Illuminate\Console\Scheduling\Schedule
) that is responsible for maintaining a list of the tasks that need to be ran and command events
(Illuminate\Console\Scheduling\Event
and Illuminate\Console\Scheduling\CallbackEvent
) that are responsible for providing the details on the actual tasks that should be performed.
Following this there is a schedule:run
(Illuminate\Console\Scheduling\ScheduleRunCommand
) that is called by third-party task systems (like Cron or Windows Task Scheduler) that runs the actual events. All components of the scheduling system have access to the same instance of Schedule
and tasks are registered, or added, in the App\Console\Kernel
class via the schedule
method.
As stated earlier, tasks are added to the App\Console\Kernel
class via the schedule
method. The schedule method provides access to the shared Schedule
instance (provided by the Illuminate\Foundation\Console\Kernel
base class):
1<?php 2 3namespace App\Console; 4 5// ... 6 7/** 8 * Define the application's command schedule. 9 *10 * @param \Illuminate\Console\Scheduling\Schedule $schedule11 * @return void12 */13protected function schedule(Schedule $schedule)14{15 // ...16}17 18// ...
The Schedule
class exposes many methods that help register the tasks that should be run. Most of the methods are a convenient wrapper around the cron expressions; these methods make it very easy and expressive to schedule commands for particular days and for specific intervals.
The following would schedule an Artisan command to run daily:
1<?php 2 3// ... 4 5protected function schedule(Schedule $schedule) 6{ 7 $schedule->command('command:name --options')->daily(); 8} 9 10// ...
It is also easy to schedule an arbitrary command to the scheduler (such as calling operating system processes or interoping with external systems). To do this, simply schedule the command using the exec
method with the command name and any arguments:
1<?php 2 3// ... 4 5protected function schedule(Schedule $schedule) 6{ 7 // Schedule composer to update project dependencies daily. 8 $schedule->exec('composer update')->daily(); 9}10 11// ...
When using the exec
method it is important to pay attention to the paths for both the command name and any configurable directory it may run in.
It is possible to schedule a function to execute at a given interval using the schedule's call
method:
1<?php 2 3// ... 4 5protected function schedule(Schedule $schedule) 6{ 7 $schedule->call(function() { 8 // Some task to execute daily. 9 })->daily();10}11 12// ...
Internally, the scheduling system will create a new instance of
Illuminate\Console\Scheduling\CallbackEvent
to represent the task within the scheduling system. The CallbackEvent
will check that the callback is actually callable using PHP's is_callable
function. Because of this, anything that will satisfy the is_callable
method can be supplied as the callback to the call
method. The following example creates a simple function and then uses the name of the function as the callback value:
1<?php 2 3// A function available to the console kernel. 4 5function exampleFunction() 6{ 7 // Function implementation. 8} 9 10// Within the `App\Console\Kernel` class.11 12protected function schedule(Schedule $schedule)13{14 $schedule->call('exampleFunction')->everyMinute();15}16 17// ...
The signature for the call
method is call($callback, array $parameters = [])
. We have already looked at the $callback
parameter; optional parameters can be supplied to the callback functions by supplying an argument for the $parameters
parameter. The following example expands on the previous example and creates a function that requires arguments:
1<?php 2 3// A function available to the console kernel. 4 5function exampleCommand($firstParameter) 6{ 7 // Function implementation. 8} 9 10// Within the `App\Console\Kernel` class.11 12protected function schedule(Schedule $schedule)13{14 $schedule->call('exampleFunction', [15 'firstParameterValue'16 ])->everyMinute();17}18 19// ...
By default all the scheduled tasks and events will not run when the application is in maintenance mode. This can be changed by calling the evenInMaintenanceMode()
method:
1<?php 2 3// ... 4 5 6protected function schedule(Schedule $schedule) 7{ 8 // Run the command in maintenance mode. 9 $schedule->command('command:name --options')10 ->daily()11 ->evenInMaintenanceMode();12}13 14// ...
Given an existing event instance, it is possible to determine if the event is configured to run in maintenance mode by using the runsInMaintenanceMode
method. This method returns either true
or false
indicating if the event should run in maintenance mode.
The following code example demonstrates how to call this method. It assumes that the $event
variable has been initially set to an event instance:
1<?php 2 3// ... 4 5protected function schedule(Schedule $schedule) 6{ 7 $event = $schedule->command('command:name --options') 8 ->daily() 9 ->evenInMaintenanceMode();10 11 // Returns true.12 $runsInMaintenanceMode = $event->runsInMaintenanceMode();13}14 15// ...
On Unix-like systems the user the command will run under can be customized by calling user
method. The user
method accepts only the name of the user and does not perform any validation on it. For example, the following would schedule a command to run as a user named www
:
1<?php 2 3// ... 4 5protected function schedule(Schedule $schedule) 6{ 7 // Schedule a task to run as `www`. 8 $schedule->exec('command to call')->user('www'); 9}10 11// ...
By default, the task scheduler allows the same task to run concurrently (at the same time). This means that if a task has started and has not completed by the time it has been scheduled to start again, there will be two processes running the same task. This can be an issue if the task is performing maintenance or administrative tasks. This can be preventing by calling the withoutOverlapping
method on the event:
1<?php 2 3// ... 4 5protected function schedule(Schedule $schedule) 6{ 7 // Schedule a task, but do not allow concurrent execution. 8 $schedule->command('command:name --options')->withoutOverlapping(); 9}10 11// ...
This method changes a public property named $withoutOverlapping
. This property can be directly accessed and changed if needed:
1<?php 2 3// ... 4 5protected function schedule(Schedule $schedule) 6{ 7 // Schedule a task, but do not allow concurrent execution. 8 $event = $schedule->command('command:name --options') 9 ->withoutOverlapping();10 11 // Access the `$withoutOverlapping` public variable.12 $event->withoutOverlapping = false;13}14 15// ...
By default, the Laravel task scheduler will run all tasks as a foreground process. When the task is running in the foreground, the before and after callbacks will be called (this is not the case when the task is executing in the background). This behavior can be changed by calling the runInBackground
method. This method accepts no arguments:
1<?php 2 3// ... 4 5protected function schedule(Schedule $schedule) 6{ 7 // Schedule the command to run in the background. 8 $schedule->command('command:name --options') 9 ->daily()10 ->runInBackground();11}12 13// ...
This method changes a public property named $runInBackground
. This property can be accessed directly to be changed if needed:
1<?php 2 3// ... 4 5protected function schedule(Schedule $schedule) 6{ 7 // Schedule the command to run in the background. 8 $event = $schedule->command('command:name --options') 9 ->daily()10 ->runInBackground();11 12 // Access the $runInBackground public property.13 $event->runInBackground = false;14}15 16// ...
The various methods that the event classes expose can be combined to specify more complicated scheduling intervals. The following tables lists each of these methods and a description of what they do (these methods are exposed via the Illuminate\Console\Scheduling\Event
base class). Any methods that accept a time value are expecting a string value formatted based on the 24-hour clock.
Method | Description |
---|---|
timezone($timezone) |
Sets the timezone the date should be evaluated on. |
days($days) |
Sets the days of the week the command should run on. Can either specify each day as its own argument or supply an array of days. |
daily |
Schedules the event to run daily. |
dailyAt($time) |
Schedule the event to run daily at a given time. |
twiceDaily($first = 1, $second = 13) |
Schedule the event daily. $first refers to the time the event should be run the first time, and $second refers to the second time the event should run. |
weekdays |
Schedule the event to run only on weekdays. |
mondays |
Schedule the event to only run on Mondays. Equivalent to calling days(1) . |
tuesdays |
Schedule the event to only run on Tuesdays. Equivalent to calling days(2) . |
wednesdays |
Schedule the event to only run on Wednesdays. Equivalent to calling days(3) . |
thursdays |
Schedule the event to only run on Thursdays. Equivalent to calling days(4) . |
fridays |
Schedule the event to only run on Fridays. Equivalent to calling days(5) . |
saturdays |
Schedules the event to only run on Saturdays. Equivalent to calling days(6) . |
sundays |
Schedule the event to only run on Sundays. Equivalent to calling days(0) . |
weekly |
Schedule the event to run weekly. |
weeklyOn($day, $time = '0:0') |
Schedule the event to run weekly on a given day and time. |
monthly |
Schedule the event to run monthly. |
monthlyOn($day = 1, $time = '0:0') |
Schedule the event to run monthly on a given day and time. |
quarterly |
Schedule the event to run quarterly. |
yearly |
Schedule the event to run yearly. |
Method | Description |
---|---|
hourly |
Schedule the event to run hourly. |
at($time) |
Set the time that the command should run at. |
everyMinute |
Schedule the event to run every minute. |
everyFiveMinutes |
Schedule the event to run every five minutes. |
everyTenMinutes |
Schedule the event to run every ten minutes. |
everyThirtyMinutes |
Schedule the event to run every thirty minutes. |
The methods can be combined to construct more complex scheduling intervals. For example, the following would run a event (command, etc) to run every Saturday at 12:00 PM:
1<?php 2 3// ... 4 5protected function schedule(Schedule $schedule) 6{ 7 $schedule->command('command:name')->saturdays()->at('14:00'); 8} 9 10// ...
Or even:
1<?php 2 3// ... 4 5protected function schedule(Schedule $schedule) 6{ 7 $schedule->call(function() { 8 // Some callback implementation. 9 })->weekdays()->hourly()->when(function () {10 11 })12}13 14// ...
Some of the methods cannot be easily chained, particularly the day methods like fridays
and saturdays
. The following would not work as expected; it would schedule the task to execute on Saturdays only:
1<?php 2 3// ... 4 5protected function schedule(Schedule $schedule) 6{ 7 $schedule->call(function() { 8 // Some callback implementation. 9 })->fridays()->saturdays();10}11 12// ...
The resulting Cron expression would be * * * * 6 *
, which is not correct. The correct way to schedule tasks for days like this is to use the days
method, which can accept an array of days. The following table lists the numeric value for each of the days:
Day | Numeric Value |
---|---|
Monday | 1 |
Tuesday | 2 |
Wednesday | 3 |
Thursday | 4 |
Friday | 5 |
Saturday | 6 |
Sunday | 0 |
Using the days
method is quite simple:
1<?php 2 3// ... 4 5protected function schedule(Schedule $schedule) 6{ 7 // Schedule the task to run on Fridays and Saturdays. 8 $schedule->call(function() { 9 // Some callback implementation.10 })->days([5, 6]);11}12 13// ...
If you already have a Cron expression and would just like to use the expression instead of the methods on the Event
classes, you may set the expression using the event's cron
method:
1<?php 2 3// ... 4 5protected function schedule(Schedule $schedule) 6{ 7 // Schedule a command to run daily. Equivalent 8 // to calling the `daily` event method. 9 $schedule->command('command:name --options')->cron('0 0 * * * *');10}11 12// ...
The tasks scheduling system allows you to define anonymous functions that can be executed before or after the execution of a given task. These additional tasks are defined for each individual event and not the scheduling system as a whole. Specifying code to run before or after a task has executed is done using the before(Closure $callback)
and after(Closure $callback)
(the after
method is just an alias of the then
method; both methods have the same signature) methods, respectively.
The following code example demonstrates what this might look like:
1<?php 2 3// ... 4 5protected function schedule(Schedule $schedule) 6{ 7 $schedule->command('command:name --options') 8 ->daily() 9 ->before(function () {10 // Code to execute before the task runs.11 })12 ->after(function () {13 // Code to execute after the task has completed.14 });15}16 17// ...
Internally, the task scheduler will use the Service Container to execute the anonymous functions supplied to the before
and after
methods. This allows us to use dependency injection when defining our functions.
In the following example, Laravel will automatically inject a Mailer
instance into our anonymous function:
1<?php 2 3use Illuminate\Contracts\Mail\Mailer; 4 5// ... 6 7protected function schedule(Schedule $schedule) 8{ 9 $schedule->command('command:name --options')10 ->before(function (Mailer $mailer) {11 // Our function would now have access to a Mailer12 // instance. This instance will automatically be13 // injected by Laravel's Service Container.14 });15}16 17// ...
Multiple functions can also be defined for both the before
and after
methods. They will be called in the order that they were defined. This can be useful for separating the concerns of code that doesn't necessarily make sense to be grouped together. Adding multiple functions to the before
or after
callback lists looks like this:
1<?php 2 3// ... 4 5protected function schedule(Schedule $schedule) 6{ 7 $schedule->command('command:name --options') 8 ->before(function () { 9 // First function to call before the task executes.10 })11 ->before(function () {12 // Second function to call before the task executes.13 });14}15 16// ...
The task scheduler also provides convenient methods for pinging URLs before or after a task has executed. The methods to do so are pingBefore($url)
and pingAfter($url)
. These methods internally use the Guzzle HTTP library. This library is not included with Laravel by default, so it must be included beforehand in order to use the pingBefore
and pingAfter
methods. This can done by adding the following to require
section of your project's composer.json
file:
1"guzzlehttp/guzzle": "~5.3|~6.0"
After adding this line, you must run composer update
to have Composer pull down the Guzzle library and add it to your project's files.
If you have installed the Guzzle HTTP library and still receive a fatal exception stating something similar to "Class 'GuzzleHttp\Client' not found", you may have to regenerate the autoloader's class map file by issuing the composer dump-autoload
or composer dump-autoload -o
command.
Once Guzzle has been installed, we can then use the pingBefore
and pingAfter
methods. Each method accepts the URL to ping. These methods issue a GET request to the specified URL. The following code examples demonstrate how to use these methods:
1<?php 2 3// ... 4 5protected function schedule(Schedule $schedule) 6{ 7 // Schedule a command to run daily; ping the supplied 8 // URLs before and after the task has completed. 9 $schedule->command('command:name --options')10 ->daily()11 ->pingBefore('http://www.stillat.com/')12 ->pingAfter('http://www.stillat.com/?someParameter=someValue');13}14 15// ...
Similarly to the before
and after
methods, multiple URLs can be specified by simply calling the pingBefore
and pingAfter
methods multiple times:
1<?php 2 3// ... 4 5protected function schedule(Schedule $schedule) 6{ 7 $schedule->command('command:name --options') 8 ->pingBefore($url) 9 ->pingBefore($anotherUrl);10}11 12// ...
This article is continued in the Limiting the Execution of Commands and Tasks With Laravel Artisan Scheduling article.
∎
The following amazing people help support this site and my open source projects ♥️
If you're interesting in supporting my work and want to show up on this list, check out my GitHub Sponsors Profile.