Laravel Task Scheduler: An Introduction

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.

#Defining Task Schedules

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 $schedule
11 * @return void
12 */
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.

#Scheduling Callbacks

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// ...

#Scheduling Tasks in Maintenance Mode

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// ...

#Setting the Command User

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// ...

#Preventing Concurrent Task Execution

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// ...

#Running the Task in the Background

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// ...

#Complex Scheduling Intervals

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.

#Date Methods

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.

#Time Methods

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// ...

#Manually Setting the Cron Expression

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// ...

#Executing Code Before or After Task Execution

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 Mailer
12 // instance. This instance will automatically be
13 // 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// ...

#Pinging URLs Before or After Task Execution

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.

Some absolutely amazing
people

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.