evgeek / scheduler
Simple scheduler
Requires
- php: >=7.2
- ext-json: *
- doctrine/dbal: ^3.2
- nesbot/carbon: ^2.54
- psr/log: ^1.1
Requires (Dev)
- roave/security-advisories: dev-latest
README
Supports various types of tasks, task batches, customizable logging with any PSR-3 logger or stdout/stderr, full launch log in the database (powered by Doctrine DBAL).
Installation
Requires PHP version 7.2 or higher
$ composer require evgeek/scheduler
Basic usage
Create php file (scheduler.php
for example) with scheduler setup code:
<?php use Doctrine\DBAL\DriverManager; use Evgeek\Scheduler\Scheduler; use Evgeek\Scheduler\Handler\DatabaseLogging; require_once '/path/to/vendor/autoload.php'; //Create DBAL connector $uri = 'mysql://user:secret@localhost/mydb'; $conn = \Doctrine\DBAL\DriverManager::getConnection(['url' => $uri]); //Create new instance of the scheduler $handler = new DatabaseLogging($conn); $scheduler = new Scheduler($handler); //Create, add to scheduler and setup new task $scheduler->task(function() { echo 'Hello world!'; }) ->schedule() ->every(1); //Run the scheduler $scheduler->run();
And add new line to crontab to run your scheduler file every minute:
* * * * * /usr/bin/php /path/to/your/scheduler.php
Supported task types
All tasks are created using the $scheduler->task()
method. The task type is recognized automatically.
Closure
$scheduler->task(function() {echo 'Hello world!';});
Bash command
$scheduler->task('ls -la');
Php file
$scheduler->task('/path/to/your/file.php');
Job interface
The scheduler can work with any class that implements a simple interface Evgeek\Scheduler\JobInterface
(single
required method dispatch()
must run the task).
$scheduler->task(new Job());
Bunch
You can combine several tasks of different types into a batch and manage their launch in the same way as a single task.
$scheduler->task([ $scheduler->task(function() {echo 'Hello world!';}), $scheduler->task('ls -la'), $scheduler->task('/path/to/your/file.php'), $scheduler->task(new Job()) ]);
Setting up a task
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.
Work mode methods
- repetitive - using the
every()
(every X minutes) ordelay()
(X minutes after previous launch finished) methods. - interval - using the
addInterval()
,daysOfWeek()
,daysOfMonth()
,months()
andyears()
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.
Examples
- Every hour
$scheduler->task(new Job()) ->schedule() ->every(60);
- Every night all night with 5 minutes delay
$scheduler->task(new Job()) ->schedule() ->addInterval('00:00', '06:00') ->delay(5);
- Every Sunday, once from 03:00 to 04:00 and once from 15:00 to 16:00
$scheduler->task(new Job()) ->schedule() ->addInterval('03:00', '04:00') ->addInterval('15:00', '16:00') ->daysOfWeek([7]);
- Once on 1st January and 1st December, and every Monday and Wednesday in January and December
$scheduler->task(new Job()) ->schedule() ->daysOfWeek(['Mon', 'wednesday']) ->daysOfMonth([1]) ->months(['Jan']) ->months(['Dec']);
- Every minute January 1, 2022
$scheduler->task(new Job()) ->schedule() ->every(1) ->daysOfMonth([1]) ->months(['Jan']) ->years([2022]);
Setup methods
$scheduler->task(new Job()) ->schedule() ->every(1) ->name('Job') ->description('A very useful task') ->tries(3);
name()
- task name for the log.description()
- task description for the log.preventOverlapping()
(defaultfalse
) - if true, the task cannot start if another instance of this task is currently running.lockResetTimeout()
(default360
) - how long (in minutes) the task lock should be recognized as frozen and reset.tries()
(default1
) - how many attempts to complete the task should be made in case of an error.tryDelay()
(default0
) - how long (in minutes) to wait before retrying the failed task.
Helper methods
getSettings()
- returns array with task settings.logDebug()
- send a message to the debug channel immediately.logError()
- send a message to the error channel immediately.
Scheduler setup
You can configure scheduler with handler object implements \Evgeek\Scheduler\Handler\LockHandlerInterface
and
config object Evgeek\Scheduler\Config
. Example:
//Creates and setup handler $uri = 'mysql://user:secret@localhost/mydb'; $conn = \Doctrine\DBAL\DriverManager::getConnection(['url' => $uri]); $handler = new \Evgeek\Scheduler\Handler\DatabaseLogging( $conn, 'scheduler_tasks', 'scheduler_launches' ); //Creates and setup config $config = new \Evgeek\Scheduler\Config(new Logger(), true); //Creates scheduler with handler and (optional) config $scheduler = new Scheduler($handler, $config);
Handlers
Lock handler, implements \Evgeek\Scheduler\Handler\LockHandlerInterface
. So far, only one is available.
\Evgeek\Scheduler\Handler\DatabaseLogging
Stores locks in the database with tasks information in one table and a full launch log in another. Needs configured Doctrine DBAL object as first parameter of constructor. You can pass custom task table name to the second optional parameter, and table name to the third.
Config
Allows you to configure other scheduling options. You can do this using Config
constructor
parameters (Config
object is immutable). Constructor parameters are of the following types:
Logger
The scheduler has two log channels: debug
for getting detailed launch information and error
for task errors.
Methods for configure:
$logger
(defaultnull
) - set PSR-3 compatible logger. If passednull
, log will be sent toSTDOUT
/STDERR
.$debugLog
(defaultfalse
) - ifnull
/false
- debug messages from the scheduler will be disabled. Iftrue
- enabled, with PSR-3 logger messages will be sent to theDEBUG
level, without - toSTDOUT
. And you can pass custom log level (like$debugLog = Logger::INFO
) to change the default PSR-3 channel.$errorLog
(defaulttrue
) - ifnull
/false
- error messages from the scheduler and tasks will be disabled. Iftrue
- enabled, with PSR-3 logger messages will be sent to theERROR
level, without - toSTDERR
. And you can pass custom log level (like$debugLog = Logger::WARNING
) to change the default PSR-3 channel.$logUncaughtErrors
(defaultfalse
) - registers shutdown function for log uncaught errors such as PHP errors or incorrect task settings.$logWarningsToError
(defaultfalse
) - ff true, PHP non-fatal errors will be sent to the error channel, otherwise - to the debug channel.$logMessageFormat
(defaultnull
) - formatting template for task logger. Passnull
for set default formatting ("[{{task_id}}. {{TASK_TYPE}} '{{task_name}}']: {{message}}"
). Available variables:{{task_id}}
{{task_type}}
{{TASK_TYPE}}
{{task_name}}
{{TASK_NAME}}
{{message}}
{{MESSAGE}}
{{task_description}}
{{TASK_DESCRIPTION}}
Lowercase for regular case, uppercase - for forced uppercase. Debug log message example with default formatting ( don't forgot createConfig
with enabled$debugLog
):
/* ... */ $config = new \Evgeek\Scheduler\Config(null, true); /* ... */ $scheduler->task('ls -la') ->schedule() ->delay(0) ->tries(3);
[0. COMMAND 'ls -la']: Checking if it's time to start
[0. COMMAND 'ls -la']: Launched (try 1/3)
[0. COMMAND 'ls -la']: Completed in 00s
-
$maxLogMsgLength
(defaultnull
) - the maximum length of the log message (the longer one will be truncated) . Set null for disable truncation. Useful if some logging channel does not accept too long messages. -
$exceptionLogMatching
(default[]
) - for more complex exceptions handling. You can pass an array of mapping to select the logging level for a specific exception:
$exceptionLogMatching = [ SoftException::class => Logger::NOTICE, FatalException::class => Logger::CRITICAL, AbstractException::class => Logger::WARNING, ];
First, the mapping goes according to a specific class, then - according to the parents, from top to bottom. Therefore, place more abstract errors below.
-
$logExceptionFormat
(defaultnull
) - formatting template for logging exceptions ({{message}}
part of the$logMessageFormat
parameter). Passnull
for set default formatting ("{{header}}\n[code]: {{code}}\n[exception]: {{class}}\n[message]: {{message}}\n[stacktrace]:\n{{stacktrace}}"
). Available variables:{{header}}
- accompanying message{{code}}
-$e->getCode()
{{class}}
- class of exception{{message}}
{{stacktrace}}
-
$maxExceptionMsgLength
(defaultnull
) - the maximum length of the exception message (the longer one will be truncated) . Set null for disable truncation. Useful with$maxLogMsgLength
if your exceptions messages are too long, and you don't want to truncate the stacktrace. -
$commandOutput
(defaultfalse
) - enable/disable shell output forbash command
tasks.
Default task options
Some options for setting default task options. The parameters specified in the task overwrite the default values.
$defaultPreventOverlapping
(defaultfalse
) - if true, the task cannot start if another instance of this task is currently running.$defaultLockResetTimeout
(default360
) - how long (in minutes) the task lock should be recognized as frozen and reset.$defaultTries
(default1
) - how many attempts to complete the task should be made in case of an error.$defaultTryDelay
(default0
) - how long (in minutes) to wait before retrying the failed task.
Others
$restartAfterFail
(defaultfalse
) - If true, the failed task will be restarted without delay for Every/Delay modes, and at the same interval for Single mode (but only at the correct interval).$minimumIntervalLength
(default30
) - Minimum interval size in minutes (for task methodaddInterval()
). 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.
Future plans
- Asynchronous task launch.
- Tests.
- More lock handlers, first - file lock handler as default behavior.
- More scheduling options, including launch at exact time.
- Managing the scheduler using console commands - list of task, force start all or specific task etc.