PHP cron job scheduler
This is a framework agnostic cron jobs scheduler that can be easily integrated with your project or run as a standalone command scheduler. The idea was originally inspired by the Laravel Task Scheduling.
The recommended way is to install the php-cron-scheduler is through Composer. Please refer to Getting Started on how to download and install Composer.
After you have downloaded/installed Composer, run
php composer.phar require peppeocchi/php-cron-scheduler
or add the package to your composer.json
{
"require": {
"peppeocchi/php-cron-scheduler": "4.*"
}
}
Scheduler V4 requires php >= 7.3, please use the v3 branch for php versions 7.1 or the v2 branch for php versions run();
Then add a new entry to your crontab to run `scheduler.php` every minute.
That's it! Your scheduler is up and running, now you can add your jobs without worring anymore about the crontab.
## Scheduling jobs
By default all your jobs will try to run in background.
PHP scripts and raw commands will run in background by default, while functions will always run in foreground.
You can force a command to run in foreground by calling the `inForeground()` method.
**Jobs that have to send the output to email, will run foreground**.
### Schedule a php script
```php
$scheduler->php('path/to/my/script.php');
```
The `php` method accepts 4 arguments:
- The path to your php script
- The PHP binary to use
- Arguments to be passed to the script (**NOTE**: You need to have **register_argc_argv** enable in your php.ini for this to work ([ref](https://github.com/peppeocchi/php-cron-scheduler/issues/88)). Don't worry it's enabled by default, so unless you've intentionally disabled it or your host has it disabled by default, you can ignore it.)
- Identifier
```php
$scheduler->php(
'path/to/my/script.php', // The script to execute
'path/to/my/custom/bin/php', // The PHP bin
[
'-c' => 'ignore',
'--merge' => null,
],
'myCustomIdentifier'
);
```
### Schedule a raw command
```php
$scheduler->raw('ps aux | grep httpd');
```
The `raw` method accepts 3 arguments:
- Your command
- Arguments to be passed to the command
- Identifier
```php
$scheduler->raw(
'mycommand | myOtherCommand',
[
'-v' => '6',
'--silent' => null,
],
'myCustomIdentifier'
);
```
### Schedule a function
```php
$scheduler->call(function () {
return true;
});
```
The `call` method accepts 3 arguments:
- Your function
- Arguments to be passed to the function
- Identifier
```php
$scheduler->call(
function ($args) {
return $args['user'];
},
[
['user' => $user],
],
'myCustomIdentifier'
);
```
All of the arguments you pass in the array will be injected to your function.
For example
```php
$scheduler->call(
function ($firstName, $lastName) {
return implode(' ', [$firstName, $lastName]);
},
[
'John',
'last_name' => 'Doe', // The keys are being ignored
],
'myCustomIdentifier'
);
```
If you want to pass a key => value pair, please pass an array within the arguments array
```php
$scheduler->call(
function ($user, $role) {
return implode(' ', [$user['first_name'], $user['last_name']]) . " has role: '{$role}'";
},
[
[
'first_name' => 'John',
'last_name' => 'Doe',
],
'Admin'
],
'myCustomIdentifier'
);
```
### Schedules execution time
There are a few methods to help you set the execution time of your schedules.
If you don't call any of this method, the job will run every minute (* * * * *).
- `at` - This method accepts any expression supported by [dragonmantank/cron-expression](https://github.com/dragonmantank/cron-expression)
```php
$scheduler->php('script.php')->at('* * * * *');
```
- `everyMinute` - Run every minute. You can optionally pass a `$minute` to specify the job runs every `$minute` minutes.
```php
$scheduler->php('script.php')->everyMinute();
$scheduler->php('script.php')->everyMinute(5);
```
- `hourly` - Run once per hour. You can optionally pass the `$minute` you want to run, by default it will run every hour at minute '00'.
```php
$scheduler->php('script.php')->hourly();
$scheduler->php('script.php')->hourly(53);
```
- `daily` - Run once per day. You can optionally pass `$hour` and `$minute` to have more granular control (or a string `hour:minute`)
```php
$scheduler->php('script.php')->daily();
$scheduler->php('script.php')->daily(22, 03);
$scheduler->php('script.php')->daily('22:03');
```
There are additional helpers for weekdays (all accepting optionals hour and minute - defaulted at 00:00)
- `sunday`
- `monday`
- `tuesday`
- `wednesday`
- `thursday`
- `friday`
- `saturday`
```php
$scheduler->php('script.php')->saturday();
$scheduler->php('script.php')->friday(18);
$scheduler->php('script.php')->sunday(12, 30);
```
And additional helpers for months (all accepting optionals day, hour and minute - defaulted to the 1st of the month at 00:00)
- `january`
- `february`
- `march`
- `april`
- `may`
- `june`
- `july`
- `august`
- `september`
- `october`
- `november`
- `december`
```php
$scheduler->php('script.php')->january();
$scheduler->php('script.php')->december(25);
$scheduler->php('script.php')->august(15, 20, 30);
```
…
```
php
$scheduler->php('script.php')->date('2018-01-01 12:20');
$scheduler->php('script.php')->date(new DateTime('2018-01-01'));
$scheduler->php('script.php')->date(DateTime::createFromFormat('!d/m Y', '01/01 2018'));
```
### Send output to file/s
You can define one or multiple files where you want the output of your script/command/function execution to be sent to.
```php
$scheduler->php('script.php')->output([
'my_file1.log', 'my_file2.log'
]);
// The scheduler catches both stdout and function return and send
// those values to the output file
$scheduler->call(function () {
echo "Hello";
return " world!";
})->output('my_file.log');
```
### Send output to email/s
You can define one or multiple email addresses where you want the output of your script/command/function execution to be sent to.
In order for the email to be sent, the output of the job needs to be sent first to a file.
In fact, the files will be attached to your email address.
In order for this to work, you need to install [swiftmailer/swiftmailer](https://github.com/swiftmailer/swiftmailer)
```php
$scheduler->php('script.php')->output([
// If you specify multiple files, both will be attached to the email
'my_file1.log', 'my_file2.log'
])->email([
'[email protected]' => 'My custom name',
'[email protected]'
]);
```
…
```
php
$scheduler = new Scheduler([
'email' => [
'subject' => 'Visitors count',
'from' => '[email protected]',
'body' => 'This is the daily visitors count',
'transport' => Swift_SmtpTransport::newInstance('smtp.gmail.com', 465, 'ssl')
->setUsername('username')
->setPassword('password'),
'ignore_empty_output' => false,
]
]);
```
Or can be set on a job per job basis.
```php
$scheduler = new Scheduler();
$scheduler->php('myscript.php')->configure([
'email' => [
'subject' => 'Visitors count',
]
]);
$scheduler->php('my_other_script.php')->configure([
'email' => [
'subject' => 'Page views count',
]
]);
```
### Schedule conditional execution
Sometimes you might want to execute a schedule not only when the execution is due, but also depending on some other condition.
You can delegate the execution of a cronjob to a truthful test with the method `when`.
```php
$scheduler->php('script.php')->when(function () {
// The job will run (if due) only when
// this function returns true
return true;
});
```
### Schedules execution order
The jobs that are due to run are being ordered by their execution: jobs that can run in **background** will be executed **first**.
### Schedules overlapping
To prevent the execution of a schedule while the previous execution is still in progress, use the method `onlyOne`. To avoid overlapping, the Scheduler needs to create **lock files**.
By default it will be used the directory path used for temporary files.
You can specify a custom directory path globally, when creating a new Scheduler instance.
```php
$scheduler = new Scheduler([
'tempDir' => 'path/to/my/tmp/dir'
]);
$scheduler->php('script.php')->onlyOne();
```
Or you can define the directory path on a job per job basis.
```php
$scheduler = new Scheduler();
// This will use the default directory path
$scheduler->php('script.php')->onlyOne();
$scheduler->php('script.php')->onlyOne('path/to/my/tmp/dir');
$scheduler->php('other_script.php')->onlyOne('path/to/my/other/tmp/dir');
```
In some cases you might want to run the job also if it's overlapping.
For example if the last execution was more that 5 minutes ago.
You can pass a function as a second parameter, the last execution time will be injected.
The job will not run until this function returns `false`. If it returns `true`, the job will run if overlapping.
```php
$scheduler->php('script.php')->onlyOne(null, function ($lastExecutionTime) {
return (time() - $lastExecutionTime) > (60 * 5);
});
```
### Before job execution
In some cases you might want to run some code, if the job is due to run, before it's being executed.
For example you might want to add a log entry, ping a url or anything else.
To do so, you can call the `before` like the example below.
```php
// $logger here is your own implementation
$scheduler->php('script.php')->before(function () use ($logger) {
$logger->info("script.php started at " . time());
});
```
### After job execution
Sometime you might wish to do something after a job runs. The `then` methods provides you the flexibility to do anything you want after the job execution. The output of the job will be injected to this function.
For example you might want to add an entry to you logs, ping a url etc...
By default, the job will be forced to run in foreground (because the output is injected to the function), if you don't need the output, you can pass `true` as a second parameter to allow the execution in background (in this case `$output` will be empty).
```php
// $logger and $messenger here are your own implementation
$scheduler->php('script.php')->then(function ($output) use ($logger, $messenger) {
$logger->info($output);
$messenger->ping('myurl.com', $output);
});
$scheduler->php('script.php')->then(function ($output) use ($logger) {
$logger->info('Job executed!');
}, true);
```
#### Using "before" and "then" together
```php
// $logger here is your own implementation
$scheduler->php('script.php')
->before(function () use ($logger) {
$logger->info("script.php started at " . time());
})
->then(function ($output) use ($logger) {
$logger->info("script.php completed at " . time(), [
'output' => $output,
]);
});
```
### Multiple scheduler runs
In some cases you might need to run the scheduler multiple times in the same script.
Although this is not a common case, the following methods will allow you to re-use the same instance of the scheduler.
```php
# some code
$scheduler->r
No open issues yet, or sync has not completed.