Added Kohana Cron https://github.com/cbandy/kohana-cron
This commit is contained in:
parent
c1b7196f41
commit
91c51fdb83
86
includes/kohana/modules/cron/README.markdown
Normal file
86
includes/kohana/modules/cron/README.markdown
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
# Kohana-Cron
|
||||||
|
|
||||||
|
This module provides a way to schedule tasks (jobs) within your Kohana application.
|
||||||
|
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Step 1: Download the module into your modules subdirectory.
|
||||||
|
|
||||||
|
Step 2: Enable the module in your bootstrap file:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable modules. Modules are referenced by a relative or absolute path.
|
||||||
|
*/
|
||||||
|
Kohana::modules(array(
|
||||||
|
'cron' => MODPATH.'cron',
|
||||||
|
// 'auth' => MODPATH.'auth', // Basic authentication
|
||||||
|
// 'codebench' => MODPATH.'codebench', // Benchmarking tool
|
||||||
|
// 'database' => MODPATH.'database', // Database access
|
||||||
|
// 'image' => MODPATH.'image', // Image manipulation
|
||||||
|
// 'orm' => MODPATH.'orm', // Object Relationship Mapping
|
||||||
|
// 'pagination' => MODPATH.'pagination', // Paging of results
|
||||||
|
// 'userguide' => MODPATH.'userguide', // User guide and API documentation
|
||||||
|
));
|
||||||
|
|
||||||
|
|
||||||
|
Step 3: Make sure the settings in `config/cron.php` are correct for your environment.
|
||||||
|
If not, copy the file to `application/config/cron.php` and change the values accordingly.
|
||||||
|
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
In its simplest form, a task is a [PHP callback][1] and times at which it should run.
|
||||||
|
To configure a task call `Cron::set($name, array($frequency, $callback))` where
|
||||||
|
`$frequency` is a string of date and time fields identical to those found in [crontab][2].
|
||||||
|
For example,
|
||||||
|
|
||||||
|
Cron::set('reindex_catalog', array('@daily', 'Catalog::regenerate_index'));
|
||||||
|
Cron::set('calendar_notifications', array('*/5 * * * *', 'Calendar::send_emails'));
|
||||||
|
|
||||||
|
Configured tasks are run with their appropriate frequency by calling `Cron::run()`. Call
|
||||||
|
this method in your bootstrap file, and you're done!
|
||||||
|
|
||||||
|
|
||||||
|
## Advanced Usage
|
||||||
|
|
||||||
|
A task can also be an instance of `Cron` that extends `next()` and/or `execute()` as
|
||||||
|
needed. Such a task is configured by calling `Cron::set($name, $instance)`.
|
||||||
|
|
||||||
|
If you have access to the system crontab, you can run Cron less (or more) than once
|
||||||
|
every request. You will need to modify the lines where the request is handled in your
|
||||||
|
bootstrap file to prevent extraneous output. The default is:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the main request. A source of the URI can be passed, eg: $_SERVER['PATH_INFO'].
|
||||||
|
* If no source is specified, the URI will be automatically detected.
|
||||||
|
*/
|
||||||
|
echo Request::instance()
|
||||||
|
->execute()
|
||||||
|
->send_headers()
|
||||||
|
->response;
|
||||||
|
|
||||||
|
Change it to:
|
||||||
|
|
||||||
|
if ( ! defined('SUPPRESS_REQUEST'))
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Execute the main request. A source of the URI can be passed, eg: $_SERVER['PATH_INFO'].
|
||||||
|
* If no source is specified, the URI will be automatically detected.
|
||||||
|
*/
|
||||||
|
echo Request::instance()
|
||||||
|
->execute()
|
||||||
|
->send_headers()
|
||||||
|
->response;
|
||||||
|
}
|
||||||
|
|
||||||
|
Then set up a system cron job to run your application's Cron once a minute:
|
||||||
|
|
||||||
|
* * * * * /usr/bin/php -f /path/to/kohana/modules/cron/run.php
|
||||||
|
|
||||||
|
The included `run.php` should work for most cases, but you are free to call `Cron::run()`
|
||||||
|
in any way you see fit.
|
||||||
|
|
||||||
|
|
||||||
|
[1]: http://php.net/manual/language.pseudo-types.php#language.types.callback
|
||||||
|
[2]: http://linux.die.net/man/5/crontab
|
10
includes/kohana/modules/cron/classes/cron.php
Normal file
10
includes/kohana/modules/cron/classes/cron.php
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?php defined('SYSPATH') or die('No direct script access.');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @package Cron
|
||||||
|
*
|
||||||
|
* @author Chris Bandy
|
||||||
|
* @copyright (c) 2010 Chris Bandy
|
||||||
|
* @license http://www.opensource.org/licenses/isc-license.txt
|
||||||
|
*/
|
||||||
|
class Cron extends Kohana_Cron {}
|
621
includes/kohana/modules/cron/classes/kohana/cron.php
Normal file
621
includes/kohana/modules/cron/classes/kohana/cron.php
Normal file
@ -0,0 +1,621 @@
|
|||||||
|
<?php defined('SYSPATH') or die('No direct script access.');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @package Cron
|
||||||
|
*
|
||||||
|
* @author Chris Bandy
|
||||||
|
* @copyright (c) 2010 Chris Bandy
|
||||||
|
* @license http://www.opensource.org/licenses/isc-license.txt
|
||||||
|
*/
|
||||||
|
class Kohana_Cron
|
||||||
|
{
|
||||||
|
protected static $_jobs = array();
|
||||||
|
protected static $_times = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a job to be run
|
||||||
|
*
|
||||||
|
* @param string Unique name
|
||||||
|
* @param array|Cron Job to run
|
||||||
|
*/
|
||||||
|
public static function set($name, $job)
|
||||||
|
{
|
||||||
|
if (is_array($job))
|
||||||
|
{
|
||||||
|
$job = new Cron(reset($job), next($job));
|
||||||
|
}
|
||||||
|
|
||||||
|
Cron::$_jobs[$name] = $job;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the timestamps of when jobs should run
|
||||||
|
*/
|
||||||
|
protected static function _load()
|
||||||
|
{
|
||||||
|
Cron::$_times = Kohana::cache("Cron::run()");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Acquire the Cron mutex
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
protected static function _lock()
|
||||||
|
{
|
||||||
|
$config = Kohana::config('cron');
|
||||||
|
$result = FALSE;
|
||||||
|
|
||||||
|
if (file_exists($config->lock) AND ($stat = @stat($config->lock)) AND time() - $config->window < $stat['mtime'])
|
||||||
|
{
|
||||||
|
// Lock exists and has not expired
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
$fh = fopen($config->lock, 'a');
|
||||||
|
|
||||||
|
if (flock($fh, LOCK_EX))
|
||||||
|
{
|
||||||
|
fseek($fh, 0, SEEK_END);
|
||||||
|
|
||||||
|
if (ftell($fh) === (empty($stat) ? 0 : $stat['size']))
|
||||||
|
{
|
||||||
|
// Current size matches expected size
|
||||||
|
// Claim the file by changing the size
|
||||||
|
fwrite($fh, '.');
|
||||||
|
|
||||||
|
$result = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// else, Another process acquired during flock()
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose($fh);
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store the timestamps of when jobs should run next
|
||||||
|
*/
|
||||||
|
protected static function _save()
|
||||||
|
{
|
||||||
|
Kohana::cache("Cron::run()", Cron::$_times, Kohana::config('cron')->window * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Release the Cron mutex
|
||||||
|
*/
|
||||||
|
protected static function _unlock()
|
||||||
|
{
|
||||||
|
return @unlink(Kohana::config('cron')->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return boolean FALSE when another instance is running
|
||||||
|
*/
|
||||||
|
public static function run()
|
||||||
|
{
|
||||||
|
if (empty(Cron::$_jobs))
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
if ( ! Cron::_lock())
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Cron::_load();
|
||||||
|
|
||||||
|
$now = time();
|
||||||
|
$threshold = $now - Kohana::config('cron')->window;
|
||||||
|
|
||||||
|
foreach (Cron::$_jobs as $name => $job)
|
||||||
|
{
|
||||||
|
if (empty(Cron::$_times[$name]) OR Cron::$_times[$name] < $threshold)
|
||||||
|
{
|
||||||
|
// Expired
|
||||||
|
|
||||||
|
Cron::$_times[$name] = $job->next($now);
|
||||||
|
|
||||||
|
if ($job->next($threshold) < $now)
|
||||||
|
{
|
||||||
|
// Within the window
|
||||||
|
|
||||||
|
$job->execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elseif (Cron::$_times[$name] < $now)
|
||||||
|
{
|
||||||
|
// Within the window
|
||||||
|
|
||||||
|
Cron::$_times[$name] = $job->next($now);
|
||||||
|
|
||||||
|
$job->execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception $e) {}
|
||||||
|
|
||||||
|
Cron::_save();
|
||||||
|
Cron::_unlock();
|
||||||
|
|
||||||
|
if (isset($e))
|
||||||
|
throw $e;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected $_callback;
|
||||||
|
protected $_period;
|
||||||
|
|
||||||
|
public function __construct($period, $callback)
|
||||||
|
{
|
||||||
|
$this->_period = $period;
|
||||||
|
$this->_callback = $callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute this job
|
||||||
|
*/
|
||||||
|
public function execute()
|
||||||
|
{
|
||||||
|
call_user_func($this->_callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the next timestamp in this period
|
||||||
|
*
|
||||||
|
* @param integer Timestamp from which to calculate
|
||||||
|
* @return integer Next timestamp in this period
|
||||||
|
*/
|
||||||
|
public function next($from)
|
||||||
|
{
|
||||||
|
// PHP >= 5.3.0
|
||||||
|
//if ($this->_period instanceof DatePeriod) { return; }
|
||||||
|
//if (is_string($this->_period) AND preg_match('/^P[\dDHMSTWY]+$/', $period)) { $this->_period = new DateInterval($this->_period); }
|
||||||
|
//if ($this->_period instanceof DateInterval) { return; }
|
||||||
|
|
||||||
|
return $this->_next_crontab($from);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the next timestamp of this crontab period
|
||||||
|
*
|
||||||
|
* @param integer Timestamp from which to calculate
|
||||||
|
* @return integer Next timestamp in this period
|
||||||
|
*/
|
||||||
|
protected function _next_crontab($from)
|
||||||
|
{
|
||||||
|
if (is_string($this->_period))
|
||||||
|
{
|
||||||
|
// Convert string to lists of valid values
|
||||||
|
|
||||||
|
if ($this->_period[0] === '@')
|
||||||
|
{
|
||||||
|
switch (substr($this->_period, 1))
|
||||||
|
{
|
||||||
|
case 'annually':
|
||||||
|
case 'yearly':
|
||||||
|
// '0 0 1 1 *'
|
||||||
|
$this->_period = array('minutes' => array(0), 'hours' => array(0), 'monthdays' => array(1), 'months' => array(1), 'weekdays' => range(0,6));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'daily':
|
||||||
|
case 'midnight':
|
||||||
|
// '0 0 * * *'
|
||||||
|
$this->_period = array('minutes' => array(0), 'hours' => array(0), 'monthdays' => range(1,31), 'months' => range(1,12), 'weekdays' => range(0,6));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'hourly':
|
||||||
|
// '0 * * * *'
|
||||||
|
$this->_period = array('minutes' => array(0), 'hours' => range(0,23), 'monthdays' => range(1,31), 'months' => range(1,12), 'weekdays' => range(0,6));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'monthly':
|
||||||
|
// '0 0 1 * *'
|
||||||
|
$this->_period = array('minutes' => array(0), 'hours' => array(0), 'monthdays' => array(1), 'months' => range(1,12), 'weekdays' => range(0,6));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'weekly':
|
||||||
|
// '0 0 * * 0'
|
||||||
|
$this->_period = array('minutes' => array(0), 'hours' => array(0), 'monthdays' => range(1,31), 'months' => range(1,12), 'weekdays' => array(0));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
list($minutes, $hours, $monthdays, $months, $weekdays) = explode(' ', $this->_period);
|
||||||
|
|
||||||
|
$months = strtr(strtolower($months), array(
|
||||||
|
'jan' => 1,
|
||||||
|
'feb' => 2,
|
||||||
|
'mar' => 3,
|
||||||
|
'apr' => 4,
|
||||||
|
'may' => 5,
|
||||||
|
'jun' => 6,
|
||||||
|
'jul' => 7,
|
||||||
|
'aug' => 8,
|
||||||
|
'sep' => 9,
|
||||||
|
'oct' => 10,
|
||||||
|
'nov' => 11,
|
||||||
|
'dec' => 12,
|
||||||
|
));
|
||||||
|
|
||||||
|
$weekdays = strtr(strtolower($weekdays), array(
|
||||||
|
'sun' => 0,
|
||||||
|
'mon' => 1,
|
||||||
|
'tue' => 2,
|
||||||
|
'wed' => 3,
|
||||||
|
'thu' => 4,
|
||||||
|
'fri' => 5,
|
||||||
|
'sat' => 6,
|
||||||
|
));
|
||||||
|
|
||||||
|
$this->_period = array(
|
||||||
|
'minutes' => $this->_parse_crontab_field($minutes, 0, 59),
|
||||||
|
'hours' => $this->_parse_crontab_field($hours, 0, 23),
|
||||||
|
'monthdays' => $this->_parse_crontab_field($monthdays, 1, 31),
|
||||||
|
'months' => $this->_parse_crontab_field($months, 1, 12),
|
||||||
|
'weekdays' => $this->_parse_crontab_field($weekdays, 0, 7)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Ensure Sunday is zero
|
||||||
|
if (end($this->_period['weekdays']) === 7)
|
||||||
|
{
|
||||||
|
array_pop($this->_period['weekdays']);
|
||||||
|
|
||||||
|
if (reset($this->_period['weekdays']) !== 0)
|
||||||
|
{
|
||||||
|
array_unshift($this->_period['weekdays'], 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$from = getdate($from);
|
||||||
|
|
||||||
|
if ( ! in_array($from['mon'], $this->_period['months']))
|
||||||
|
return $this->_next_crontab_month($from);
|
||||||
|
|
||||||
|
if (count($this->_period['weekdays']) === 7)
|
||||||
|
{
|
||||||
|
// Day of Week is unrestricted, defer to Day of Month
|
||||||
|
if ( ! in_array($from['mday'], $this->_period['monthdays']))
|
||||||
|
return $this->_next_crontab_monthday($from);
|
||||||
|
}
|
||||||
|
elseif (count($this->_period['monthdays']) === 31)
|
||||||
|
{
|
||||||
|
// Day of Month is unrestricted, use Day of Week
|
||||||
|
if ( ! in_array($from['wday'], $this->_period['weekdays']))
|
||||||
|
return $this->_next_crontab_weekday($from);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Both Day of Week and Day of Month are restricted
|
||||||
|
if ( ! in_array($from['mday'], $this->_period['monthdays']) AND ! in_array($from['wday'], $this->_period['weekdays']))
|
||||||
|
return $this->_next_crontab_day($from);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! in_array($from['hours'], $this->_period['hours']))
|
||||||
|
return $this->_next_crontab_hour($from);
|
||||||
|
|
||||||
|
return $this->_next_crontab_minute($from);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the first timestamp in the next day of this period when both
|
||||||
|
* Day of Week and Day of Month are restricted
|
||||||
|
*
|
||||||
|
* @uses _next_crontab_month()
|
||||||
|
*
|
||||||
|
* @param array Date array from getdate()
|
||||||
|
* @return integer Timestamp of next restricted Day
|
||||||
|
*/
|
||||||
|
protected function _next_crontab_day(array $from)
|
||||||
|
{
|
||||||
|
// Calculate effective Day of Month for next Day of Week
|
||||||
|
|
||||||
|
if ($from['wday'] >= end($this->_period['weekdays']))
|
||||||
|
{
|
||||||
|
$next = reset($this->_period['weekdays']) + 7;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach ($this->_period['weekdays'] as $next)
|
||||||
|
{
|
||||||
|
if ($from['wday'] < $next)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$monthday = $from['mday'] + $next - $from['wday'];
|
||||||
|
|
||||||
|
if ($monthday <= (int) date('t', mktime(0, 0, 0, $from['mon'], 1, $from['year'])))
|
||||||
|
{
|
||||||
|
// Next Day of Week is in this Month
|
||||||
|
|
||||||
|
if ($from['mday'] >= end($this->_period['monthdays']))
|
||||||
|
{
|
||||||
|
// No next Day of Month, use next Day of Week
|
||||||
|
$from['mday'] = $monthday;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Calculate next Day of Month
|
||||||
|
foreach ($this->_period['monthdays'] as $next)
|
||||||
|
{
|
||||||
|
if ($from['mday'] < $next)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use earliest day
|
||||||
|
$from['mday'] = min($monthday, $next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ($from['mday'] >= end($this->_period['monthdays']))
|
||||||
|
{
|
||||||
|
// No next Day of Month, use next Month
|
||||||
|
return $this->_next_crontab_month($from);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate next Day of Month
|
||||||
|
foreach ($this->_period['monthdays'] as $next)
|
||||||
|
{
|
||||||
|
if ($from['mday'] < $next)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use next Day of Month
|
||||||
|
$from['mday'] = $next;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use first Hour and first Minute
|
||||||
|
return mktime(reset($this->_period['hours']), reset($this->_period['minutes']), 0, $from['mon'], $from['mday'], $from['year']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the first timestamp in the next hour of this period
|
||||||
|
*
|
||||||
|
* @uses _next_crontab_day()
|
||||||
|
* @uses _next_crontab_monthday()
|
||||||
|
* @uses _next_crontab_weekday()
|
||||||
|
*
|
||||||
|
* @param array Date array from getdate()
|
||||||
|
* @return integer Timestamp of next Hour
|
||||||
|
*/
|
||||||
|
protected function _next_crontab_hour(array $from)
|
||||||
|
{
|
||||||
|
if ($from['hours'] >= end($this->_period['hours']))
|
||||||
|
{
|
||||||
|
// No next Hour
|
||||||
|
|
||||||
|
if (count($this->_period['weekdays']) === 7)
|
||||||
|
{
|
||||||
|
// Day of Week is unrestricted, defer to Day of Month
|
||||||
|
return $this->_next_crontab_monthday($from);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count($this->_period['monthdays']) === 31)
|
||||||
|
{
|
||||||
|
// Day of Month is unrestricted, use Day of Week
|
||||||
|
return $this->_next_crontab_weekday($from);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Both Day of Week and Day of Month are restricted
|
||||||
|
return $this->_next_crontab_day($from);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate next Hour
|
||||||
|
foreach ($this->_period['hours'] as $next)
|
||||||
|
{
|
||||||
|
if ($from['hours'] < $next)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use next Hour and first Minute
|
||||||
|
return mktime($next, reset($this->_period['minutes']), 0, $from['mon'], $from['mday'], $from['year']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the timestamp of the next minute in this period
|
||||||
|
*
|
||||||
|
* @uses _next_crontab_hour()
|
||||||
|
*
|
||||||
|
* @param array Date array from getdate()
|
||||||
|
* @return integer Timestamp of next Minute
|
||||||
|
*/
|
||||||
|
protected function _next_crontab_minute(array $from)
|
||||||
|
{
|
||||||
|
if ($from['minutes'] >= end($this->_period['minutes']))
|
||||||
|
{
|
||||||
|
// No next Minute, use next Hour
|
||||||
|
return $this->_next_crontab_hour($from);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate next Minute
|
||||||
|
foreach ($this->_period['minutes'] as $next)
|
||||||
|
{
|
||||||
|
if ($from['minutes'] < $next)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use next Minute
|
||||||
|
return mktime($from['hours'], $next, 0, $from['mon'], $from['mday'], $from['year']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the first timestamp in the next month of this period
|
||||||
|
*
|
||||||
|
* @param array Date array from getdate()
|
||||||
|
* @return integer Timestamp of next Month
|
||||||
|
*/
|
||||||
|
protected function _next_crontab_month(array $from)
|
||||||
|
{
|
||||||
|
if ($from['mon'] >= end($this->_period['months']))
|
||||||
|
{
|
||||||
|
// No next Month, increment Year and use first Month
|
||||||
|
++$from['year'];
|
||||||
|
$from['mon'] = reset($this->_period['months']);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Calculate next Month
|
||||||
|
foreach ($this->_period['months'] as $next)
|
||||||
|
{
|
||||||
|
if ($from['mon'] < $next)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use next Month
|
||||||
|
$from['mon'] = $next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count($this->_period['weekdays']) === 7)
|
||||||
|
{
|
||||||
|
// Day of Week is unrestricted, use first Day of Month
|
||||||
|
$from['mday'] = reset($this->_period['monthdays']);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Calculate Day of Month for the first Day of Week
|
||||||
|
$indices = array_flip($this->_period['weekdays']);
|
||||||
|
|
||||||
|
$monthday = 1;
|
||||||
|
$weekday = (int) date('w', mktime(0, 0, 0, $from['mon'], 1, $from['year']));
|
||||||
|
|
||||||
|
while ( ! isset($indices[$weekday % 7]) AND $monthday < 7)
|
||||||
|
{
|
||||||
|
++$monthday;
|
||||||
|
++$weekday;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count($this->_period['monthdays']) === 31)
|
||||||
|
{
|
||||||
|
// Day of Month is unrestricted, use first Day of Week
|
||||||
|
$from['mday'] = $monthday;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Both Day of Month and Day of Week are restricted, use earliest one
|
||||||
|
$from['mday'] = min($monthday, reset($this->_period['monthdays']));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use first Hour and first Minute
|
||||||
|
return mktime(reset($this->_period['hours']), reset($this->_period['minutes']), 0, $from['mon'], $from['mday'], $from['year']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the first timestamp in the next day of this period when only
|
||||||
|
* Day of Month is restricted
|
||||||
|
*
|
||||||
|
* @uses _next_crontab_month()
|
||||||
|
*
|
||||||
|
* @param array Date array from getdate()
|
||||||
|
* @return integer Timestamp of next Day of Month
|
||||||
|
*/
|
||||||
|
protected function _next_crontab_monthday(array $from)
|
||||||
|
{
|
||||||
|
if ($from['mday'] >= end($this->_period['monthdays']))
|
||||||
|
{
|
||||||
|
// No next Day of Month, use next Month
|
||||||
|
return $this->_next_crontab_month($from);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate next Day of Month
|
||||||
|
foreach ($this->_period['monthdays'] as $next)
|
||||||
|
{
|
||||||
|
if ($from['mday'] < $next)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use next Day of Month, first Hour, and first Minute
|
||||||
|
return mktime(reset($this->_period['hours']), reset($this->_period['minutes']), 0, $from['mon'], $next, $from['year']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the first timestamp in the next day of this period when only
|
||||||
|
* Day of Week is restricted
|
||||||
|
*
|
||||||
|
* @uses _next_crontab_month()
|
||||||
|
*
|
||||||
|
* @param array Date array from getdate()
|
||||||
|
* @return integer Timestamp of next Day of Week
|
||||||
|
*/
|
||||||
|
protected function _next_crontab_weekday(array $from)
|
||||||
|
{
|
||||||
|
// Calculate effective Day of Month for next Day of Week
|
||||||
|
|
||||||
|
if ($from['wday'] >= end($this->_period['weekdays']))
|
||||||
|
{
|
||||||
|
$next = reset($this->_period['weekdays']) + 7;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach ($this->_period['weekdays'] as $next)
|
||||||
|
{
|
||||||
|
if ($from['wday'] < $next)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$monthday = $from['mday'] + $next - $from['wday'];
|
||||||
|
|
||||||
|
if ($monthday > (int) date('t', mktime(0, 0, 0, $from['mon'], 1, $from['year'])))
|
||||||
|
{
|
||||||
|
// Next Day of Week is not in this Month, use next Month
|
||||||
|
return $this->_next_crontab_month($from);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use next Day of Week, first Hour, and first Minute
|
||||||
|
return mktime(reset($this->_period['hours']), reset($this->_period['minutes']), 0, $from['mon'], $monthday, $from['year']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a sorted array of all the values indicated in a Crontab field
|
||||||
|
* @link http://linux.die.net/man/5/crontab
|
||||||
|
*
|
||||||
|
* @param string Crontab field
|
||||||
|
* @param integer Minimum value for this field
|
||||||
|
* @param integer Maximum value for this field
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function _parse_crontab_field($value, $min, $max)
|
||||||
|
{
|
||||||
|
$result = array();
|
||||||
|
|
||||||
|
foreach (explode(',', $value) as $value)
|
||||||
|
{
|
||||||
|
if ($slash = strrpos($value, '/'))
|
||||||
|
{
|
||||||
|
$step = (int) substr($value, $slash + 1);
|
||||||
|
$value = substr($value, 0, $slash);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($value === '*')
|
||||||
|
{
|
||||||
|
$result = array_merge($result, range($min, $max, $slash ? $step : 1));
|
||||||
|
}
|
||||||
|
elseif ($dash = strpos($value, '-'))
|
||||||
|
{
|
||||||
|
$result = array_merge($result, range(max($min, (int) substr($value, 0, $dash)), min($max, (int) substr($value, $dash + 1)), $slash ? $step : 1));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$value = (int) $value;
|
||||||
|
|
||||||
|
if ($min <= $value AND $value <= $max)
|
||||||
|
{
|
||||||
|
$result[] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sort($result);
|
||||||
|
|
||||||
|
return array_unique($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
28
includes/kohana/modules/cron/config/cron.php
Normal file
28
includes/kohana/modules/cron/config/cron.php
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<?php defined('SYSPATH') OR die('No direct access allowed.');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @package Cron
|
||||||
|
*
|
||||||
|
* @author Chris Bandy
|
||||||
|
* @copyright (c) 2010 Chris Bandy
|
||||||
|
* @license http://www.opensource.org/licenses/isc-license.txt
|
||||||
|
*/
|
||||||
|
|
||||||
|
return array
|
||||||
|
(
|
||||||
|
// Path to a writable directory and lock file
|
||||||
|
'lock' => Kohana::$cache_dir.DIRECTORY_SEPARATOR.'cron.lck',
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cron does not run EXACTLY when tasks are scheduled.
|
||||||
|
* A task can be executed up to this many seconds AFTER its scheduled time.
|
||||||
|
*
|
||||||
|
* For example, Cron is run at 10:48 and a task was scheduled to execute at
|
||||||
|
* 10:45, 180 seconds ago. If window is greater than 180, the task will be
|
||||||
|
* executed.
|
||||||
|
*
|
||||||
|
* This value should always be larger than the time it takes to run all
|
||||||
|
* your tasks.
|
||||||
|
*/
|
||||||
|
'window' => 300,
|
||||||
|
);
|
22
includes/kohana/modules/cron/run.php
Normal file
22
includes/kohana/modules/cron/run.php
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @package Cron
|
||||||
|
*
|
||||||
|
* @author Chris Bandy
|
||||||
|
* @copyright (c) 2010 Chris Bandy
|
||||||
|
* @license http://www.opensource.org/licenses/isc-license.txt
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Path to Kohana's index.php
|
||||||
|
$system = dirname(dirname(dirname(__FILE__))).DIRECTORY_SEPARATOR.'index.php';
|
||||||
|
|
||||||
|
if (file_exists($system))
|
||||||
|
{
|
||||||
|
defined('SUPPRESS_REQUEST') or define('SUPPRESS_REQUEST', TRUE);
|
||||||
|
|
||||||
|
include $system;
|
||||||
|
|
||||||
|
// If Cron has been run in APPPATH/bootstrap.php, this second call is harmless
|
||||||
|
Cron::run();
|
||||||
|
}
|
85
includes/kohana/modules/cron/tests/kohana/cron.php
Normal file
85
includes/kohana/modules/cron/tests/kohana/cron.php
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @package Cron
|
||||||
|
* @group kohana
|
||||||
|
* @group kohana.cron
|
||||||
|
*
|
||||||
|
* @author Chris Bandy
|
||||||
|
* @copyright (c) 2010 Chris Bandy
|
||||||
|
* @license http://www.opensource.org/licenses/isc-license.txt
|
||||||
|
*/
|
||||||
|
class Kohana_Cron_Test extends PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
* @dataProvider provider_next
|
||||||
|
*
|
||||||
|
* @param string Period
|
||||||
|
* @param integer Timestamp from which to calculate
|
||||||
|
* @param integer Next timestamp in period
|
||||||
|
*/
|
||||||
|
public function test_next($period, $from, $expected_result)
|
||||||
|
{
|
||||||
|
$cron = new Cron($period, NULL);
|
||||||
|
$result = $cron->next($from);
|
||||||
|
|
||||||
|
$this->assertSame($expected_result, $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function provider_next()
|
||||||
|
{
|
||||||
|
return array
|
||||||
|
(
|
||||||
|
array('@annually', mktime(8, 45, 0, 11, 19, 2009), mktime(0, 0, 0, 1, 1, 2010)),
|
||||||
|
array('@monthly', mktime(8, 45, 0, 11, 19, 2009), mktime(0, 0, 0, 12, 1, 2009)),
|
||||||
|
array('@weekly', mktime(8, 45, 0, 11, 19, 2009), mktime(0, 0, 0, 11, 22, 2009)),
|
||||||
|
array('@daily', mktime(8, 45, 0, 11, 19, 2009), mktime(0, 0, 0, 11, 20, 2009)),
|
||||||
|
array('@hourly', mktime(8, 45, 0, 11, 19, 2009), mktime(9, 0, 0, 11, 19, 2009)),
|
||||||
|
|
||||||
|
array('* * * * *', mktime(8, 45, 0, 11, 19, 2009), mktime(8, 46, 0, 11, 19, 2009)),
|
||||||
|
|
||||||
|
array(
|
||||||
|
'* * * * 0', // Sundays
|
||||||
|
mktime(0, 0, 0, 11, 30, 2009), // Monday, Nov 30, 2009
|
||||||
|
mktime(0, 0, 0, 12, 6, 2009) // Sunday, Dec 6, 2009
|
||||||
|
),
|
||||||
|
|
||||||
|
array(
|
||||||
|
'* * 15 * 6', // 15th and Saturdays
|
||||||
|
mktime(0, 0, 0, 11, 29, 2009), // Sunday, Nov 29, 2009
|
||||||
|
mktime(0, 0, 0, 12, 5, 2009) // Saturday, Dec 5, 2009
|
||||||
|
),
|
||||||
|
|
||||||
|
array(
|
||||||
|
'* * * * 1,5', // Mondays and Fridays
|
||||||
|
mktime(0, 0, 0, 11, 24, 2009), // Tuesday, Nov 24, 2009
|
||||||
|
mktime(0, 0, 0, 11, 27, 2009) // Friday, Nov 27, 2009
|
||||||
|
),
|
||||||
|
|
||||||
|
array(
|
||||||
|
'* * 15 * 6-7', // 15th, Saturdays, and Sundays
|
||||||
|
mktime(0, 0, 0, 11, 23, 2009), // Monday, Nov 23, 2009
|
||||||
|
mktime(0, 0, 0, 11, 28, 2009) // Saturday, Nov 28, 2009
|
||||||
|
),
|
||||||
|
|
||||||
|
array(
|
||||||
|
'* * 15,30 * 2', // 15th, 30th, and Tuesdays
|
||||||
|
mktime(0, 0, 0, 11, 29, 2009), // Sunday, Nov 29, 2009
|
||||||
|
mktime(0, 0, 0, 11, 30, 2009) // Monday, Nov 30, 2009
|
||||||
|
),
|
||||||
|
|
||||||
|
array(
|
||||||
|
'0 0 * * 4', // Midnight on Thursdays
|
||||||
|
mktime(1, 0, 0, 11, 19, 2009), // 01:00 Thursday, Nov 19, 2009
|
||||||
|
mktime(0, 0, 0, 11, 26, 2009) // 00:00 Thursday, Nov 26, 2009
|
||||||
|
),
|
||||||
|
|
||||||
|
array(
|
||||||
|
'0 0 */2 * 4', // Midnight on odd days and Thursdays
|
||||||
|
mktime(1, 0, 0, 11, 19, 2009), // 01:00 Thursday, Nov 19, 2009
|
||||||
|
mktime(0, 0, 0, 11, 21, 2009) // 00:00 Saturday, Nov 21, 2009
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user