|
| 1 | +# Simple PHP Scheduler |
| 2 | +Supports various types of tasks, task batches, customizable logging with any [PSR-3](https://www.php-fig.org/psr/psr-3/) logger or stdout/stderr, full launch log in the database (powered by Doctrine DBAL). |
| 3 | +## Installation |
| 4 | +Requires PHP version 7.2 or higher |
| 5 | +```bash |
| 6 | +$ composer require evgeek/scheduler |
| 7 | +``` |
| 8 | +## Basic usage |
| 9 | +Create php file (```scheduler.php``` for example) with scheduler setup code: |
| 10 | +```php |
| 11 | +<?php |
| 12 | + |
| 13 | +use Doctrine\DBAL\DriverManager; |
| 14 | +use Evgeek\Scheduler\Scheduler; |
| 15 | +use Evgeek\Scheduler\Handler\DatabaseLogging; |
| 16 | + |
| 17 | +require_once __DIR__ . '/path/to/vendor/autoload.php'; |
| 18 | + |
| 19 | +//Create DBAL connector |
| 20 | +$uri = 'mysql://user:secret@localhost/mydb'; |
| 21 | +$conn = \Doctrine\DBAL\DriverManager::getConnection(['url' => $uri]); |
| 22 | + |
| 23 | +//Create new instance of the scheduler |
| 24 | +$handler = new DatabaseLogging($conn); |
| 25 | +$scheduler = new Scheduler($handler); |
| 26 | + |
| 27 | +//Create, add to scheduler and setup new task |
| 28 | +$scheduler->task(function() { |
| 29 | + echo 'Hello world!'; |
| 30 | +}) |
| 31 | + ->schedule() |
| 32 | + ->every(1); |
| 33 | + |
| 34 | +//Run the scheduler |
| 35 | +$scheduler->run(); |
| 36 | +``` |
| 37 | +And add new line to crontab to run your scheduler file every minute: |
| 38 | +``` |
| 39 | +* * * * * /usr/bin/php /path/to/your/scheduler.php |
| 40 | +``` |
| 41 | +## Supported task types |
| 42 | +All tasks are created using the ```$scheduler->task()``` method. The task type is recognized automatically. |
| 43 | +### Closure |
| 44 | +```php |
| 45 | +$scheduler->task(function() {echo 'Hello world!';}); |
| 46 | +``` |
| 47 | +### Bash command |
| 48 | +```php |
| 49 | +$scheduler->task('ls -la'); |
| 50 | +``` |
| 51 | +### Php file |
| 52 | +```php |
| 53 | +$scheduler->task('/path/to/your/file.php'); |
| 54 | +``` |
| 55 | +### Job interface |
| 56 | +The scheduler can work with any class that implements a simple interface ```Evgeek\Scheduler\JobInterface``` (single required method ```dispatch()``` must run the task). |
| 57 | +```php |
| 58 | +$scheduler->task(new Job()); |
| 59 | +``` |
| 60 | +### Bunch |
| 61 | +You can combine several tasks of different types into a batch and manage their launch in the same way as a single task. |
| 62 | +```php |
| 63 | +$scheduler->task([ |
| 64 | + $scheduler->task(function() {echo 'Hello world!';}), |
| 65 | + $scheduler->task('ls -la'), |
| 66 | + $scheduler->task('/path/to/your/file.php'), |
| 67 | + $scheduler->task(new Job()) |
| 68 | +]); |
| 69 | +``` |
| 70 | +## Setting up a task |
| 71 | +After creating a task using the ```task()``` method, you can add it to the scheduler using the ```schedule()``` method. After that, various methods of setting up the task launch become available. |
| 72 | +### Work mode methods |
| 73 | +* **_repetitive_** - using the ```every()``` (every X minutes) or ```delay()``` (X minutes after previous launch finished) methods. |
| 74 | +* **_interval_** - using the ```addInterval()```, ```daysOfWeek()```, ```daysOfMonth()```, ```months()``` and ```years()``` methods. If interval mode is used with repetitive mode, the task will be launched repetitive at the specified intervals, otherwise the task will be launched once per interval. |
| 75 | +#### Examples |
| 76 | +* Every hour |
| 77 | +```php |
| 78 | +$scheduler->task(new Job()) |
| 79 | + ->schedule() |
| 80 | + ->every(60); |
| 81 | +``` |
| 82 | +* Every night all night with 5 minutes delay |
| 83 | +```php |
| 84 | +$scheduler->task(new Job()) |
| 85 | + ->schedule() |
| 86 | + ->addInterval('00:00', '06:00') |
| 87 | + ->delay(5); |
| 88 | +``` |
| 89 | +* Every Sunday, once from 03:00 to 04:00 and once from 15:00 to 16:00 |
| 90 | +```php |
| 91 | +$scheduler->task(new Job()) |
| 92 | + ->schedule() |
| 93 | + ->addInterval('03:00', '04:00') |
| 94 | + ->addInterval('15:00', '16:00') |
| 95 | + ->daysOfWeek([7]); |
| 96 | +``` |
| 97 | +* Once on 1st January and December and every Monday and Wednesday in January and December |
| 98 | +```php |
| 99 | +$scheduler->task(new Job()) |
| 100 | + ->schedule() |
| 101 | + ->daysOfWeek(['Mon', 'wednesday']) |
| 102 | + ->daysOfMonth([1]) |
| 103 | + ->months(['Jan']) |
| 104 | + ->months(['Dec']); |
| 105 | +``` |
| 106 | +* Every minute January 1, 2022 |
| 107 | +```php |
| 108 | +$scheduler->task(new Job()) |
| 109 | + ->schedule() |
| 110 | + ->every(1) |
| 111 | + ->daysOfMonth([1]) |
| 112 | + ->months(['Jan']) |
| 113 | + ->years([2022]); |
| 114 | +``` |
| 115 | +### Setup methods |
| 116 | +```php |
| 117 | +$scheduler->task(new Job()) |
| 118 | + ->schedule() |
| 119 | + ->every(1) |
| 120 | + ->name('Job') |
| 121 | + ->description('A very useful task') |
| 122 | + ->tries(3); |
| 123 | +``` |
| 124 | +* ```name()``` - task name for the log. |
| 125 | +* ```description()``` - task description for the log. |
| 126 | +* ```preventOverlapping()``` (default ```false```) - if true, the task cannot start if another instance of this task is currently running. |
| 127 | +* ```lockResetTimeout()``` (default ```360```) - how long (in minutes) the task lock should be recognized as frozen and reset. |
| 128 | +* ```tries()``` (default ```1```) - how many attempts to complete the task should be made in case of an error. |
| 129 | +* ```tryDelay()``` (default ```0```) - how long (in minutes) to wait before retrying the failed task. |
| 130 | +### Helper methods |
| 131 | +* ```getSettings()``` - returns array with task settings. |
| 132 | +* ```logDebug()``` - send a message to the debug channel immediately. |
| 133 | +* ```logError()``` - send a message to the error channel immediately. |
| 134 | +## Scheduler setup |
| 135 | +You can configure scheduler with handler object implements ```\Evgeek\Scheduler\Handler\LockHandlerInterface``` and config object ```Evgeek\Scheduler\Config```. Example: |
| 136 | +```php |
| 137 | +//Creates and setup handler |
| 138 | +$uri = 'mysql://user:secret@localhost/mydb'; |
| 139 | +$conn = \Doctrine\DBAL\DriverManager::getConnection(['url' => $uri]); |
| 140 | +$handler = new \Evgeek\Scheduler\Handler\DatabaseLogging( |
| 141 | + $conn, |
| 142 | + 'scheduler_tasks', |
| 143 | + 'scheduler_launches' |
| 144 | +); |
| 145 | + |
| 146 | +//Creates and setup config |
| 147 | +$config = new \Evgeek\Scheduler\Config(); |
| 148 | +$config |
| 149 | + ->setDebugLogging(true) |
| 150 | + ->setDefaultTries(3); |
| 151 | + |
| 152 | +//Creates scheduler with handler and (optional) config |
| 153 | +$scheduler = new Scheduler($handler, $config); |
| 154 | +``` |
| 155 | +### Handlers |
| 156 | +Lock handler, implements ```\Evgeek\Scheduler\Handler\LockHandlerInterface```. So far, only one is available. |
| 157 | +* ```\Evgeek\Scheduler\Handler\DatabaseLogging```\ |
| 158 | +Stores locks in the database with tasks information in one table and a full launch log in another. Needs configured [Doctrine DBAL](https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/introduction.html) object as first parameter of constructor. You can pass custom task table name to the second optional parameter, and table name to the third. |
| 159 | +### Config |
| 160 | +Allows you to configure other scheduling options. You can do this using ```Config``` constructor parameters or using ```$config``` methods: |
| 161 | +#### Logger |
| 162 | +The scheduler has two log channels: ```debug``` for getting detailed launch information and ```error``` for task errors. Methods for configure: |
| 163 | +* ```setDebugLogging()``` (default ```false```) - enable/disable ```debug``` channel. |
| 164 | +* ```setErrorLogging()``` (default ```true```) - enable/disable ```error``` channel. |
| 165 | +* ```setLogger()``` (default ```null```) - set [PSR-3](https://www.php-fig.org/psr/psr-3/) compatible logger. If passed ```null```, log will be sent to ```STDOUT```/```STDERR```. |
| 166 | +* ```setDebugLogLevel()``` (default ```null```) - sets custom log level for the PSR-3 logger for ```debug``` messages. By default, this is ```debug```. |
| 167 | +* ```setDErrorLogLevel()``` (default ```null```) - sets custom log level for the PSR-3 logger for ```error``` messages. By default, this is ```error```. |
| 168 | +* ```setLogUncaughtErrors()``` (default ```false```) - registers shutdown function for log uncaught exceptions such as PHP fatal errors or incorrect task settings. |
| 169 | +* ```setLogMessageFormat()``` (default ```"[{{task_id}}. {{TASK_TYPE}} '{{task_name}}']: {{message}}"```) - formatting template for task logger. Available variables: |
| 170 | + * ```{{task_id}}``` |
| 171 | + * ```{{task_type}}``` |
| 172 | + * ```{{TASK_TYPE}}``` |
| 173 | + * ```{{task_name}}``` |
| 174 | + * ```{{TASK_NAME}}``` |
| 175 | + * ```{{message}}``` |
| 176 | + * ```{{MESSAGE}}``` |
| 177 | + * ```{{task_description}}``` |
| 178 | + * ```{{TASK_DESCRIPTION}}``` |
| 179 | + |
| 180 | +Lowercase for regular case, uppercase - for forced uppercase. Log message example with default formatting: |
| 181 | +```php |
| 182 | +/* ... */ |
| 183 | +$config->setDebugLogging(true); |
| 184 | +/* ... */ |
| 185 | +$scheduler->task('ls -la') |
| 186 | + ->schedule() |
| 187 | + ->delay(0) |
| 188 | + ->tries(3); |
| 189 | +``` |
| 190 | +``` |
| 191 | +[0. COMMAND 'ls -la']: Checking if it's time to start |
| 192 | +[0. COMMAND 'ls -la']: Launched (try 1/3) |
| 193 | +[0. COMMAND 'ls -la']: Completed in 00s |
| 194 | +``` |
| 195 | +* ```setCommandOutput()``` (default ```false```) - enable/disable shell output for `bash command` tasks. |
| 196 | +#### Default task options |
| 197 | +Some options for setting default task options. The parameters specified in the task overwrite the default values. |
| 198 | +* ```setDefaultPreventOverlapping()``` (default ```false```) - if true, the task cannot start if another instance of this task is currently running. |
| 199 | +* ```setDefaultLockResetTimeout()``` (default ```360```) - how long (in minutes) the task lock should be recognized as frozen and reset. |
| 200 | +* ```setDefaultTries()``` (default ```1```) - how many attempts to complete the task should be made in case of an error. |
| 201 | +* ```setDefaultTryDelay()``` (default ```0```) - how long (in minutes) to wait before retrying the failed task. |
| 202 | +#### Others |
| 203 | +* ```setMinimumIntervalLength()``` (default ```30```) - Minimum interval size in minutes (for task method ```addInterval()```). Currently, tasks are started sequentially and synchronously, so the scheduler cannot guarantee the exact time when the task will start. Because of this, I had to limit the minimum size of the interval to make sure that the task will not be missed because the interval is too small. This is not a good decision. In future updates, task launching will be implemented asynchronously, and the interval limitation will be removed. |
| 204 | +## Future plans |
| 205 | +* Asynchronous task launch. |
| 206 | +* Tests. |
| 207 | +* More lock handlers, first - file lock handler as default behavior. |
| 208 | +* More scheduling options, including launch at exact time. |
| 209 | +* Managing the scheduler using console commands - list of task, force start all or specific task etc. |
0 commit comments