It is possible to set up a cron job that runs every minute and when triggered it checks what jobs are scheduled for that moment.
As a simple idea which could be easily modified to push through the run time details for a particular script if you wanted:-
<?php
include '/core/config.php');
// Test script to allow jobs to be set up (cron style) on a database, but with the addition that jobs can be made
// dependent on other jobs completing first.
// Currently does not support jobs being dependent on more than one parent job.
// It uses a database of 2 tables. One for servers and the other for jobs.
// The server is selected as the one that matches the value of php_uname('n') (hence this can be run on many servers accessing a single database and only executing jobs for the particular server an instance is running on)
// Time ranges are specified in the same way as on CRON jobs:-
// * = no restriction based on that field
// x = when the value of that time parameter matches x
// /x = every x of that field (ie, mod current of that field by x and match if result is 0)
// x-y = when the value of that time parameter is between x and y
// x,y = when the value of the time parameter matches x or y (or z, etc)
// The script field on the scheduling table contains the script / command to be executed. For example if a php script then it might be 'php /usr/webdata/cron_scripts/some_script.php
// Parentid is the id of a job that must have finished before the job is executed.
class scheduling extends core_class
{
public $connections;
private $db;
private $year;
private $month;
private $day;
private $hour;
private $minute;
private $second;
private $day_of_week;
private $background_kick_off = true;
private $completed_jobs = array();
function __construct($connections, $background_kick_off = true)
{
parent::__construct($connections);
$this->background_kick_off = $background_kick_off;
$this->debug_time_start();
$this->connections = $connections;
$this->db = new database($connections['EO'], 'em_scheduling');
if (!$this->db->no_error)
$this->error('E_ERROR', $this->db->error());
$run_date = date('Y/m/d H:i:s w');
list($date_part, $time_part, $this->day_of_week) = explode(' ', $run_date);
list($this->year, $this->month, $this->day) = explode('/', $date_part);
list($this->hour, $this->minute, $this->second) = explode(':', $time_part);
$this->find_jobs(0);
}
function find_jobs($parent_id)
{
$sql = "SELECT a.id, a.script, a.parent_id, a.minutes, a.hours, a.day_of_month, a.months, a.day_of_week, a.script_description, COUNT(DISTINCT b.id) AS child_count
FROM scheduling a
ON s.id = a.server_id
LEFT OUTER JOIN scheduling b
ON a.id = b.parent_id
AND b.enabled = 1
AND (b.minutes = '*' OR FIND_IN_SET('".$this->minute."', b.minutes) OR (SUBSTR(b.minutes, 1, 1) = '/' AND (".$this->minute." % CAST(SUBSTR(b.minutes, 2) AS UNSIGNED)) = 0) OR (b.minutes LIKE '%-%' AND ".$this->minute." BETWEEN CAST(SUBSTRING_INDEX(b.minutes, '-', 1) AS UNSIGNED) AND CAST(SUBSTRING_INDEX(b.minutes, '-', -1) AS UNSIGNED)))
AND (b.hours = '*' OR FIND_IN_SET('".$this->hour."', b.hours) OR (SUBSTR(b.hours, 1, 1) = '/' AND (".$this->hour." % CAST(SUBSTR(b.hours, 2) AS UNSIGNED)) = 0) OR (b.hours LIKE '%-%' AND ".$this->hour." BETWEEN CAST(SUBSTRING_INDEX(b.hours, '-', 1) AS UNSIGNED) AND CAST(SUBSTRING_INDEX(b.hours, '-', -1) AS UNSIGNED)))
AND (b.months = '*' OR FIND_IN_SET('".$this->month."', b.months) OR (SUBSTR(b.months, 1, 1) = '/' AND (".$this->month." % CAST(SUBSTR(b.months, 2) AS UNSIGNED)) = 0) OR (b.months LIKE '%-%' AND ".$this->month." BETWEEN CAST(SUBSTRING_INDEX(b.months, '-', 1) AS UNSIGNED) AND CAST(SUBSTRING_INDEX(b.months, '-', -1) AS UNSIGNED)))
AND ((b.day_of_month = '*' OR FIND_IN_SET('".$this->day."', b.day_of_month) OR (SUBSTR(b.day_of_month, 1, 1) = '/' AND (".$this->day." % CAST(SUBSTR(b.day_of_month, 2) AS UNSIGNED)) = 0) OR (b.day_of_month LIKE '%-%' AND ".$this->day." BETWEEN CAST(SUBSTRING_INDEX(b.day_of_month, '-', 1) AS UNSIGNED) AND CAST(SUBSTRING_INDEX(b.day_of_month, '-', -1) AS UNSIGNED)))
OR (b.day_of_week = '*' OR FIND_IN_SET('".$this->day_of_week."', b.day_of_week) OR (SUBSTR(b.day_of_week, 1, 1) = '/' AND (".$this->day_of_week." % CAST(SUBSTR(b.day_of_week, 2) AS UNSIGNED)) = 0) OR (b.day_of_week LIKE '%-%' AND ".$this->day_of_week." BETWEEN CAST(SUBSTRING_INDEX(b.day_of_week, '-', 1) AS UNSIGNED) AND CAST(SUBSTRING_INDEX(b.day_of_week, '-', -1) AS UNSIGNED))))
WHERE a.parent_id = ".(int)$parent_id."
AND a.enabled = 1
AND (a.minutes = '*' OR FIND_IN_SET('".$this->minute."', a.minutes) OR (SUBSTR(a.minutes, 1, 1) = '/' AND (".$this->minute." % CAST(SUBSTR(a.minutes, 2) AS UNSIGNED)) = 0) OR (a.minutes LIKE '%-%' AND ".$this->minute." BETWEEN CAST(SUBSTRING_INDEX(a.minutes, '-', 1) AS UNSIGNED) AND CAST(SUBSTRING_INDEX(a.minutes, '-', -1) AS UNSIGNED)))
AND (a.hours = '*' OR FIND_IN_SET('".$this->hour."', a.hours) OR (SUBSTR(a.hours, 1, 1) = '/' AND (".$this->hour." % CAST(SUBSTR(a.hours, 2) AS UNSIGNED)) = 0) OR (a.hours LIKE '%-%' AND ".$this->hour." BETWEEN CAST(SUBSTRING_INDEX(a.hours, '-', 1) AS UNSIGNED) AND CAST(SUBSTRING_INDEX(a.hours, '-', -1) AS UNSIGNED)))
AND (a.months = '*' OR FIND_IN_SET('".$this->month."', a.months) OR (SUBSTR(a.months, 1, 1) = '/' AND (".$this->month." % CAST(SUBSTR(a.months, 2) AS UNSIGNED)) = 0) OR (a.months LIKE '%-%' AND ".$this->month." BETWEEN CAST(SUBSTRING_INDEX(a.months, '-', 1) AS UNSIGNED) AND CAST(SUBSTRING_INDEX(a.months, '-', -1) AS UNSIGNED)))
AND ((a.day_of_month = '*' OR FIND_IN_SET('".$this->day."', a.day_of_month) OR (SUBSTR(a.day_of_month, 1, 1) = '/' AND (".$this->day." % CAST(SUBSTR(a.day_of_month, 2) AS UNSIGNED)) = 0) OR (a.day_of_month LIKE '%-%' AND ".$this->day." BETWEEN CAST(SUBSTRING_INDEX(a.day_of_month, '-', 1) AS UNSIGNED) AND CAST(SUBSTRING_INDEX(a.day_of_month, '-', -1) AS UNSIGNED)))
OR (a.day_of_week = '*' OR FIND_IN_SET('".$this->day_of_week."', a.day_of_week) OR (SUBSTR(a.day_of_week, 1, 1) = '/' AND (".$this->day_of_week." % CAST(SUBSTR(a.day_of_week, 2) AS UNSIGNED)) = 0) OR (a.day_of_week LIKE '%-%' AND ".$this->day_of_week." BETWEEN CAST(SUBSTRING_INDEX(a.day_of_week, '-', 1) AS UNSIGNED) AND CAST(SUBSTRING_INDEX(a.day_of_week, '-', -1) AS UNSIGNED))))
GROUP BY a.id, a.script, a.parent_id, a.minutes, a.hours, a.day_of_month, a.months, a.day_of_week
ORDER BY child_count
";
//echo "\r\n $sql \r\n";
$this->db->query($sql) or die($this->db->error());
$process_array = array();
while ($row = $this->db->fetch_assoc())
{
$process_array[] = $row;
}
foreach($process_array as $aProcess)
{
if ($this->background_kick_off and $aProcess['child_count'] == 0)
{
// No jobs to follow so just kick them off as a background task
$this->launchBackgroundProcess($aProcess['script']);
$completed_jobs[$aProcess['id']] = $aProcess['script_description'];
}
else
{
passthru($aProcess['script'].'', $return_var);
if ($return_var == 0)
{
$completed_jobs[$aProcess['id']] = $aProcess['script_description'];
$this->find_jobs($aProcess['id']);
}
}
}
}
private function launchBackgroundProcess($call)
{
// Windows
if($this->is_windows())
{
pclose(popen('start /b '.$call, 'r'));
}
// Some sort of UNIX
else
{
pclose(popen($call.' /dev/null &', 'r'));
}
return true;
}
private function is_windows()
{
if(PHP_OS == 'WINNT' || PHP_OS == 'WIN32')
{
return true;
}
return false;
}
}
$Scheduling = new scheduling($connections, true);
?>
Tables like this:-
CREATE TABLE IF NOT EXISTS `scheduling` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`enabled` tinyint(1) NOT NULL DEFAULT '1',
`script` varchar(255) DEFAULT NULL,
`parent_id` int(11) DEFAULT NULL,
`minutes` varchar(5) DEFAULT NULL,
`hours` varchar(5) DEFAULT NULL,
`day_of_month` varchar(5) DEFAULT NULL,
`months` varchar(5) DEFAULT NULL,
`day_of_week` varchar(5) DEFAULT NULL,
`script_description` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `parent_id` (`server_id`,`parent_id`,`enabled`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=15 ;
--
-- Dumping data for table `scheduling`
--
INSERT INTO `scheduling` (`id`, `enabled`, `script`, `parent_id`, `minutes`, `hours`, `day_of_month`, `months`, `day_of_week`, `script_description`) VALUES
(1, 1, 'php download.php', 0, '*', '*', '*', '*', '*', 'Download files'),
(2, 1, 'php load_data.php', 1, '*', '*', '*', '*', '*', 'Load files to database'),
(3, 1, 'php file_test.php', 1, '*', '*', '*', '*', '*', NULL),
(4, 1, 'php file_test.php', 1, '*', '*', '*', '*', '*', NULL),
(5, 1, 'php file_test.php', 1, '*', '*', '*', '*', '*', NULL),
(6, 1, 'php file_test.php', 1, '*', '*', '*', '*', '*', NULL),
(7, 1, 'php file_test.php', 1, '*', '*', '*', '*', '*', NULL),
(8, 1, 'php file_test.php', 1, '*', '*', '*', '*', '*', NULL),
(9, 1, 'php file_test.php', 1, '*', '*', '*', '*', '*', NULL),
(10, 1, 'php file_test.php', 1, '*', '*', '*', '*', '*', NULL),
(11, 1, 'php file_test.php', 1, '*', '*', '*', '*', '*', NULL),
(12, 1, 'php file_test.php', 1, '*', '*', '*', '*', '*', NULL),
(13, 1, 'php file_test.php', 1, '*', '*', '*', '*', '*', NULL),
(14, 1, 'php file_test.php', 1, '*', '*', '*', '*', '*', NULL);