Getting Started with Job Scheduling in PHP

Better Stack Team
Updated on October 21, 2022

Task scheduling is a commonly used technique for automating tasks based on a schedule. Such tasks may include backing up databases, processing a queue, or creating system usage reports, as they are required to be repeated regularly over time, or even indefinitely. It is better to schedule such tasks and monitor them so they can run predictably in a timely fashion.

Many programming languages offer their own task scheduling solution, and in this tutorial, we will discuss how to create scheduled jobs using PHP. We will also discuss a monitoring solution to help you keep tabs on your scheduled tasks and promptly notify you if something goes wrong.

Prerequisites

Before going through the rest of this article, ensure that you are using a Linux machine with the cron installed. You also need the latest versions of PHP and Composer installed.

Getting started with PHP Cron scheduler

In this tutorial, we will utilize the php-cron-scheduler package to implement task scheduling in PHP. It is a framework-agnostic package that can be integrated into any project or run as a standalone job scheduler.

You can go ahead and install this package in a new phpCronDemo directory using the commands below:

mkdir phpCronDemo && cd phpCronDemo
Copied!
composer require peppeocchi/php-cron-scheduler
Copied!
Output
Using version ^4.0 for peppeocchi/php-cron-scheduler
./composer.json has been created
Running composer update peppeocchi/php-cron-scheduler
. . .
 - Installing dragonmantank/cron-expression (v3.3.2): Extracting archive
- Installing peppeocchi/php-cron-scheduler (v4.0): Extracting archive
1 package suggestions were added by new dependencies, use `composer suggest` to see details. Generating autoload files 1 package you are using is looking for funding. Use the `composer fund` command to find out more!

Once the package is successfully installed, a vendor directory should be present in the working directory. This directory should contain an autoload.php file that will be used to import the installed packages into our program.

Create a scheduler.php file at the root of your working directory and open it in your text editor. It will serve as the starting point for all scheduled jobs in this tutorial.

nano scheduler.php
Copied!

Add the following code to the file:

scheduler.php
<?php require_once __DIR__ . '/vendor/autoload.php';

use GO\Scheduler;

// Create a new scheduler
$scheduler = new Scheduler();

// Schedule jobs

// Run the scheduler
$scheduler->run();
Copied!

First, you need to tell PHP where the autoload.php file is located and then import the Scheduler class from the installed package. Next, create a new instance of Scheduler and run it using the run() method. The scheduler configuration is omitted for now, but it will be added in the next section.

Before this scheduler can start working, you must activate it by setting up a Cron Job that executes the scheduler.php script every minute. Open your crontab with the following command:

crontab -e
Copied!

Add the following line to the end of the file:

* * * * * php <absolute_path>/scheduler.php
Copied!

This Cron Job activates the scheduler by executing the script every minute so that the scheduler can evaluate your scheduled tasks and run the ones that are due. In the next section, you will define your first scheduled task and see it in action.

Scheduling your first job

After you've created and activated your scheduler, you can start scheduling jobs. The php-cron-scheduler package offers three methods for this purpose, depending on what type of job you wish to schedule. For example, the raw() method is used to schedule the execution of a Linux command like this:

scheduler.php
. . .

$scheduler = new Scheduler();

$scheduler->raw(
"curl https://en.wikipedia.org/wiki/Main_Page -o wikipedia.html"
);
$scheduler->run();
Copied!

This particular example will schedule a curl command which downloads the Wikipedia homepage to an HTML file in the current directory.

At this point, you've defined the task to be scheduled. Next you need to define how often it should be executed. Let's make the job run every minute for example:

scheduler.php
. . .

$scheduler = new Scheduler();

$scheduler
  ->raw("curl https://en.wikipedia.org/wiki/Main_Page -o wikipedia.html")
->everyMinute();
$scheduler->run();
Copied!

Using the everyMinute() method ensures that the scheduled task is executed every minute. Once you save the scheduler.php file, wait for a minute, and you should observe the creation of the wikipedia.html file. View the contents of the file with the following command to confirm that it is working as expected:

cat wikipedia.html
Copied!
Output
<!DOCTYPE html>
<html class="client-nojs" lang="en" dir="ltr">
<head>
<meta charset="UTF-8"/>
<title>Wikipedia, the free encyclopedia</title>
. . .
</html>

Scheduling PHP scripts and functions

You can also schedule PHP functions or scripts using the php-cron-scheduler package as shown below. Note that functions always run in the foreground, while scripts run in the background by default.

Here's how to schedule a PHP function using the call() method:

$scheduler->call(
  function ($args) {
    return $args["user"];
  },
  [["user" => $user]],
  "myCustomIdentifier"
);
Copied!

The call() method takes three parameters:

  1. The function to be scheduled,
  2. The arguments to be passed to the function,
  3. An optional identifier.

On the other hand, the php() method is used to schedule PHP scripts as shown below:

$scheduler->php(
  "script.php", // The script to execute
  "php", // The path to the PHP binary that is used to execute the script
  [
    "username" => "jack",
    "verified" => true,
  ],
  "myCustomIdentifier"
);
Copied!

It takes four arguments, as shown in the list below, but only the first one is required:

  1. The path to the PHP script to execute.
  2. The path to the PHP binary. If you didn't add PHP to your environmental variables, you need to specify the absolute path instead. The location could differ depending on how you installed PHP.
  3. The arguments that should be passed to the PHP script. These arguments must be in in key/value pairs in an associative array.
  4. The script identifier.

Scheduling a script in this manner allows you to automate more complex tasks. For example, here is a script that retrieves the current weather information and saves it to a text file:

weather.php
<?php

$location = json_decode(file_get_contents("http://ipinfo.io/json"));

$coordinate = $location->loc;
$coordinate = explode(",", $coordinate);

$weather = json_decode(
  file_get_contents(
    "https://api.openweathermap.org/data/2.5/weather?lat={$coordinate[0]}&lon={$coordinate[1]}&appid=<api_key>"
  )
);

$text = "Todays weather is " . $weather->weather[0]->main;
$file = fopen("weather.txt", "w");
fwrite($file, $text);
fclose($file);
Copied!

This script uses the ipinfo.io API to retrieve your coordinates based on your IP address, then the coordinates are used to get the current weather forecast through the OpenWeatherMap API. Note that you need to register for a free OpenWeatherMap account and replace the <api_key> placeholder with your API key.

Once the weather information is extracted, it is saved to a weather.txt file. You can schedule this script to run every morning at 06:00 AM like this:

scheduler.php
<?php require_once __DIR__ . "/vendor/autoload.php";

use GO\Scheduler;

$scheduler->php("weather.php")->daily("06:00");
$scheduler->run();
Copied!

Setting up job execution times

Let's now discuss how you can set up an execution schedule for your tasks. The php-cron-scheduler package offers several other helpers besides the everyMinute() and daily() methods we already covered. These helpers are shown in the list below:

  • everyMinute(): This helper defaults to every minute, but you can pass a $minute parameter to make the task run at a different interval. For example, everyMinute(5) will execute the job every five minutes.
  • hourly(): Schedules the job to run once every hour at minute 0. You can pass a $minute parameter so that the job runs at minute $minute of the hour. For example, hourly(15) means the job will run at 00:15, 01:15, 02:15, and so on.
  • daily(): Runs once every day. You can pass two parameters ($hour and $minute) or a string (hour:minute) to determine the exact time this job is scheduled. If not set, the job will run at 00:00 every day.

There are also additional helpers for weekdays. Just like daily(), you can pass parameters $hour and $minute or a string to set the exact time, but it defaults to 00:00.

$scheduler->php("script.php")->sunday();
$scheduler->php("script.php")->monday();
$scheduler->php("script.php")->tuesday();
$scheduler->php("script.php")->wednesday();
$scheduler->php("script.php")->thursday();
$scheduler->php("script.php")->friday();
$scheduler->php("script.php")->saturday();

$scheduler->php("script.php")->saturday(10); # Saturday at 10:00
$scheduler->php("script.php")->saturday(11, 30); # Saturday at 11:30
$scheduler->php("script.php")->saturday("15:25"); # Saturday at 15:25
Copied!

There are helpers for months as well, and they default to running on the first day of the month at 00:00. You can pass three parameters $day, $hour and $minute to set the exact date and time if you wish. Note that these helper methods do not accept a string as input.

$scheduler->php("script.php")->january();
$scheduler->php("script.php")->february();
$scheduler->php("script.php")->march();
$scheduler->php("script.php")->april();
$scheduler->php("script.php")->may();
$scheduler->php("script.php")->june();
$scheduler->php("script.php")->july();
$scheduler->php("script.php")->august();
$scheduler->php("script.php")->september();
$scheduler->php("script.php")->october();
$scheduler->php("script.php")->november();
$scheduler->php("script.php")->december();

$scheduler->php("script.php")->december(25); # December 25th
$scheduler->php("script.php")->december(25, 12, 30); # December 25th at 12:30
Copied!

It is also possible for you to combine these helpers to create a more random schedule. For instance, if you want a job to run every Wednesday and Friday at 05:00 and 14:00, this is what you can do:

$scheduler
  ->php("script.php")
  ->wednesday("05:00")
  ->wednesday("14:00")
  ->friday("05:00")
  ->friday("14:00");
Copied!

If you are more comfortable using Cron expressions, you can use the at() method to create the same schedule as follows:

$scheduler->php("script.php")->at("0 5,14 * * 3,5");
Copied!

Lastly, a date() method is provided for scheduling a task to be executed exactly once on the specified date and time. The method takes a string or an instance of DateTime:

$scheduler->php("script.php")->date("2022-09-13 12:20");
$scheduler->php("script.php")->date(new DateTime("2022-09-13"));
Copied!

Defining conditions for job execution

One of the benefits of using PHP to schedule tasks instead of Cron is that you can harness the power of a programming language to define conditions other than a date and time for carrying out task execution.

The scheduler's when() method is provided for this purpose. It takes a callback function as its input, and the job only executes when the function returns true. For example, you can schedule a job that executes when PHP is using more than 1MB of memory:

$scheduler->php("script.php")->when(function () {
  if (memory_get_usage() >= 1048576) {
    return true;
  } else {
    return false;
  }
});
Copied!

Defining pre and post job execution behavior

The php-cron-scheduler package also provides some hooks for specifying any functions to run before and after a scheduled job is executed. These are the before() and then() methods shown below:

$scheduler
  ->php("weather.php")
->before(function () {
// execute this before evaluating `weather.php`
})
->then(function ($output) {
// execute this after evaluating `weather.php`
})
->everyMinute();
Copied!

Notice the sequence of this chain of methods. The before() and then() methods come after the php() method, followed by the scheduling method. The $output parameter in the then() method is automatically injected by the scheduler, containing the output of the weather.php script (if any).

Sending job output to files

In the weather report example, the fopen() function was used to write the weather information to a local file. However, there is an easier and more convenient way to deal with the output from scripts, commands, or functions through the php-cron-scheduler package.

First, you must ensure that your function, command, or script produces an output. For example, you can modify your weather.php file as follows:

weather.php
. . .
$text = "Todays weather is " . $weather->weather[0]->main;
echo $text;
Copied!

Next, go to your scheduler.php file, and add an output() method:

scheduler.php
. . .
$scheduler
  ->php("weather.php")
->everyMinute()
->output(["weather.txt"]);
$scheduler->run();
Copied!

For demonstration purposes, the schedule is changed to everyMinute(). Wait for the job to execute, then run the following command to see the output:

cat weather.txt
Copied!
Output
Todays weather is Clouds

The output() method takes an array as its input, allowing you to save the output to multiple files like this:

$scheduler
  ->php("weather.php")
  ->everyMinute()
  ->output(["weather.txt", "weather.log"]);
Copied!

Monitoring PHP scheduled tasks with Better Uptime

Better Uptime is a cloud-based monitoring tool that allows you to keep tabs on your scheduled jobs and get notified through if a task didn't execute as scheduled for any reason. This section will discuss how to configure Better Uptime to monitor your scheduled tasks.

First, you need to create a free Better Uptime account if you don't have one already. Once signed in, click Heartbeats on the top left of the dashboard and create a new heartbeat.

Choose an appropriate name for your monitor and select how often you expect this job to be repeated. In the On-call escalation section, pick how you wish to be notified when the job fails to execute. After you are done, click Save Changes.

You should see this page:

Notice the highlighted section in the middle. This URL is how Better Uptime monitors your scheduled task. Every time a job executes, you should ensure that a HEAD, GET, or POST request is made to this URL.

For example, head back to the scheduler.php file and add the following highlighted code:

scheduler.php
. . .
// Schedule jobs
$scheduler
  ->php("weather.php")
->then(function () {
file_get_contents("https://betteruptime.com/api/v1/heartbeat/<api_key>");
})
->everyMinute() ->output(["weather.txt"]); // Run the scheduler $scheduler->run();
Copied!

Here, the file_get_contents() function is used to make a GET request to the Better Uptime API each time the weather.php script is executed. Next, go to the Better Uptime monitor you just created and wait for the job to execute. Once Better Uptime starts receiving requests, the monitor will be marked as "Up", which means that the Cron Job is up and running.

You can simulate an incident by commenting out the file_get_contents() line. If Better Uptime does not receive a request within the configured time frame, the heartbeat will be marked as "Down", which means that an incident was detected.

You will also receive an alert in the configured channels:

Conclusion

In this tutorial, we discussed how to schedule tasks such as Linux commands, PHP functions and scripts using the PHP Cron Scheduler package. We also demonstrated how to specify conditional triggers for your tasks, and how to monitor scheduled task with Better Uptime.

Thanks for reading, and happy scheduling!

Check Uptime, Ping, Ports, SSL and more.
Get Slack, SMS and phone incident alerts.
Easy on-call duty scheduling.
Create free status page on your domain.
Got an article suggestion? Let us know
Licensed under CC-BY-NC-SA

This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.