Created
November 5, 2011 04:31
-
-
Save EHLOVader/1341111 to your computer and use it in GitHub Desktop.
Cron POC
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
// File containing cronjobs | |
$cronFile = dirname(__FILE__) . '/crontab'; | |
// Lockfile to prevent multiple instances | |
$pidFile = dirname(__FILE__) . '/cron.lock'; | |
// Exit file to signal a running instance to exit | |
$exitFile = dirname(__FILE__) . '/cron.exit'; | |
// Email address to send administrative reports to | |
$adminEmail = 'admin@yourdomain.com'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
// PHPCron v1.00 | |
// written by Katy Coe - http://www.djkaty.com | |
// (c) Intelligent Streaming 2006 | |
// Usage from command-line: | |
// php cron.php [--param] | |
// Usage from web browser: | |
// http://www.yourdomain.com/path/to/cron/cron.php{?param] | |
// Parameters: | |
// status - get running state of PHPCron | |
// edit - edit crontab (from web browser only) | |
// kill - kill running instances of PHPCron | |
// force - kill running instances of PHPCron and create one new instance | |
// Get configuration | |
include "cron.config.php"; | |
// No timeout expiration | |
set_time_limit(0); | |
// Running from command line | |
if (!is_web_environment()) | |
{ | |
// Status report requested? | |
if (array_search('--status', $_SERVER['argv']) !== false) | |
{ | |
echo "Cron is " . (file_exists($exitFile)? 'waiting to exit' : | |
(file_exists($pidFile)? '' : 'not ') . 'running') . ".\r\n"; | |
exit; | |
} | |
echo "Starting cron from the command line.\r\n"; | |
// Start cron | |
cron($cronFile, $pidFile, $exitFile, | |
array_search('--force', $_SERVER['argv']) !== false, | |
array_search('--kill', $_SERVER['argv']) !== false); | |
} | |
// Running from web server | |
else | |
{ | |
// Run the crontab editor interface if 'edit' supplied as GET parameter | |
if (isset($_GET['edit'])) | |
{ | |
edit($cronFile); | |
exit; | |
} | |
// Continue running script upon user abort (when running from web server) | |
ignore_user_abort(true); | |
// Close connection to browser before running shutdown function | |
header("Pragma: public"); | |
header("Content-Type: text/plain; name=\"cron.txt\""); | |
header("Content-Disposition: inline; filename=\"cron.txt\""); | |
header("Cache-Control: no-store"); | |
header("Connection: close"); | |
ob_start(); | |
// Status report requested? | |
if (isset($_GET['status'])) | |
{ | |
echo "Cron is " . (file_exists($exitFile)? 'waiting to exit' : | |
(file_exists($pidFile)? '' : 'not ') . 'running') . ".\r\n"; | |
exit; | |
} | |
else | |
echo "Starting cron from a web server environment.\r\n"; | |
// Start cron | |
cron($cronFile, $pidFile, $exitFile, | |
isset($_GET['force']), | |
isset($_GET['kill'])); | |
} | |
exit; | |
// Class representing a single cronjob | |
// Rules for crontab file format and scheduling conditions taken from: | |
// http://en.wikipedia.org/wiki/Cron | |
class CronJob { | |
public $minutes; | |
public $hours; | |
public $dates; | |
public $months; | |
public $weekdays; | |
public $job; | |
public function __construct($jobText) | |
{ | |
// Get parameters | |
$jobText = trim($jobText); | |
$jobText = preg_replace('#\s{2,}#', ' ', $jobText); | |
$jobParams = split(' ', $jobText, 6); | |
// If insufficient parameters supplied, abort silently | |
if (count($jobParams) < 6) | |
return; | |
// Parse time parameters | |
$this->minutes = $this->parse_param($jobParams[0], 0, 59); | |
$this->hours = $this->parse_param($jobParams[1], 0, 23); | |
$this->dates = $this->parse_param($jobParams[2], 1, 31); | |
$this->months = $this->parse_param($jobParams[3], 1, 12); | |
$this->weekdays = $this->parse_param($jobParams[4], 0, 7); | |
// 0 and 7 are both counted as Sunday | |
if ($this->weekdays == 7) | |
$this->weekdays = 0; | |
// Shell job command | |
$this->job = implode(' ', array_slice($jobParams, 5)); | |
} | |
private function parse_param($text, $min, $max) | |
{ | |
$result = array(); | |
// * - all possible values | |
if ($text == '*') | |
for ($i = $min; $i <= $max; $i++) | |
$result[] = $i; | |
// "*/n" syntax - starts at $min and recurs every n | |
elseif (substr($text, 0, 2) == '*/') | |
for ($i = $min; $i <= $max; $i += substr($text, 2)) | |
$result[] = $i; | |
else | |
{ | |
// Split by commas | |
$timeItems = split(',', $text); | |
foreach ($timeItems as $timeItem) | |
{ | |
// X-Y syntax - starts at X and increments by 1 to Y inclusive, | |
// wrapping around from $max to $min if necessary | |
if (strpos($timeItem, '-') !== false) | |
{ | |
list ($first, $last) = split('-', $timeItem, 2); | |
// Bound specified range within min/max parameters | |
$first = max(min($first, $max), $min); | |
$last = max(min($last, $max), $min); | |
// Non-wrapping range | |
if ($first <= $last) | |
for ($i = $first; $i <= $last; $i++) | |
$result[] = $i; | |
// Wrapping range | |
else { | |
for ($i = $first; $i <= $max; $i++) | |
$result[] = $i; | |
for ($i = $min; $i <= $last; $i++) | |
$result[] = $i; | |
} | |
} | |
// Single number | |
else | |
$result[] = $timeItem; | |
} | |
} | |
return $result; | |
} | |
} | |
// Crontab scheduler | |
function cron($cronFile, $pidFile, $exitFile, $force, $kill) | |
{ | |
// Forced execution if force flag set | |
if ($force || $kill) | |
{ | |
// Check we aren't already waiting | |
if (file_exists($exitFile)) | |
{ | |
echo "Cron is already waiting to die.\r\n"; | |
end_output(); | |
exit; | |
} | |
// Check there is another instance running | |
if (!file_exists($pidFile)) | |
{ | |
echo "There are no instances of cron running.\r\n"; | |
end_output(); | |
exit; | |
} | |
// Signal other instances to exit | |
FileUtils::Lock($exitFile); | |
// Kill all instances including this one if kill flag set | |
if ($kill) { | |
echo "Killing all instances of cron.\r\n"; | |
end_output(); | |
exit; | |
} | |
// Otherwise wait for other instances to die if force flag set | |
if ($force) { | |
echo "Waiting for other instances of cron to die.\r\n"; | |
while (file_exists($pidFile)) | |
sleep(1); | |
echo "Resuming cron.\r\n"; | |
} | |
} | |
// Don't allow multiple instances | |
if (file_exists($pidFile)) | |
{ | |
echo "Cron is already running.\r\n"; | |
end_output(); | |
exit; | |
} | |
// Read cron file | |
if (!file_exists($cronFile)) | |
{ | |
echo "No crontab file found.\r\n"; | |
end_output(); | |
exit; | |
} | |
// Configure cleanup | |
register_shutdown_function('cron_cleanup', $pidFile, $exitFile); | |
// Make web browser connection close here if not already done | |
echo "Cron startup successful.\r\n"; | |
end_output(); | |
// Create lockfile | |
FileUtils::Lock($pidFile); | |
$jobsText = file($cronFile); | |
$jobs = array(); | |
foreach ($jobsText as $jobText) | |
$jobs[] = new CronJob($jobText); | |
// Enter processing and wait loop | |
while (!file_exists($exitFile)) | |
{ | |
$nowStamp = time(); | |
$now = getdate($nowStamp); | |
// Iterate through each job determining whether it's time to run it, | |
// and execute those which match the time criteria | |
foreach ($jobs as $job) | |
if ( array_search($now['minutes'], $job->minutes) !== false | |
and array_search($now['hours'], $job->hours) !== false | |
and array_search($now['mon'], $job->months) !== false | |
and (array_search($now['mday'], $job->dates) !== false | |
or array_search($now['wday'], $job->weekdays) !== false)) | |
{ | |
echo "Running job: " . $job->job . "\r\n"; | |
// Redirect output on non-Windows platforms so process executes in background | |
if (substr(strtoupper(PHP_OS), 0, 3) == 'WIN') | |
@exec($job->job); | |
else | |
@exec($job->job . ' >/dev/null'); | |
} | |
// Wait until 60 seconds expired since last check | |
// (might be less than a minute if the jobs took a while to execute | |
// as we are single-threaded) | |
while (time() < $nowStamp + 60) | |
sleep(1); | |
} | |
} | |
function cron_cleanup($pidFile, $exitFile) | |
{ | |
FileUtils::Unlock($pidFile); | |
FileUtils::Unlock($exitFile); | |
} | |
function is_web_environment() | |
{ | |
return ($_SERVER['argc'] == 0 || $_SERVER['argv'][0] != $_SERVER['PHP_SELF']); | |
} | |
function end_output() | |
{ | |
if (is_web_environment()) | |
{ | |
header("Content-Length: " . ob_get_length()); | |
ob_end_flush(); | |
flush(); | |
} | |
} | |
// Crontab editor | |
function edit($cronFile) | |
{ | |
header("Content-type: text/html"); | |
?> | |
<html> | |
<head> | |
<title>Cron Editor</title> | |
</head> | |
<body style="font-family: Verdana, sans-serif; font-size: x-small"> | |
<?php | |
if (isset($_POST['crontab'])): | |
// Negate escaping | |
if (get_magic_quotes_gpc()) | |
{ | |
if (ini_get('magic_quotes_sybase')) | |
$data = strtr($_POST['crontab'], array("''" => "'")); | |
else | |
$data = stripslashes($_POST['crontab']); | |
} | |
else | |
$data = $_POST['crontab']; | |
$result = @file_put_contents($cronFile, $data); | |
if ($result): | |
?><p style="color: red">The crontab file was updated successfully.</p><?php | |
else: | |
?><p style="color: red">The crontab file could not be updated.</p><?php | |
endif; | |
endif; | |
?> | |
<p>Syntax: | |
<ul> | |
<li>One job per line</li> | |
<li>Line format: <minutes 0-59> <hours 0-23> <days of month 1-31> <months 1-12> <days of week 0-7> <shell command to run></li> | |
<li>Formats of each argument: | |
<ul> | |
<li>* - all possible values</li> | |
<li>*/n - every nth value (eg. */4 as month field = 1, 5, 9)</li> | |
<li>m-n - every value between m and n inclusive</li> | |
<li>a,b,c,d... - values a, b, c, d and so on</li> | |
<li>Dashed ranges can be used with commas eg. 1,5-18,19,30</li> | |
</ul> | |
</li> | |
<li>Days of week start at 0 = Sunday, 1 = Monday etc.; 7 is equivalent to Sunday</li> | |
<li>Job will be executed if minutes, hours, months and either dates or week days match</li> | |
<li>To run a php job, specify the shell command as "php <script name to run>"</li> | |
<li>To run php embedded code, specify the shell command as "php -r "<code to run without php tags>". Dollar signs in embedded code must be escaped with a backslash.</li> | |
</ul> | |
</p> | |
<form action="<?php echo $_SERVER['REQUEST_URI']; ?>" method="post"> | |
<div> | |
<textarea name="crontab" cols="80" rows="20"><?php | |
if (file_exists($cronFile)) | |
echo file_get_contents($cronFile); | |
?></textarea> | |
<p><b>Edit crontab and press Save to commit changes. Cron must be restarted manually for the changes to take effect.</b></p> | |
<input type="submit" value="Save" /> | |
</div> | |
</form> | |
<a href="<?php echo substr($_SERVER['REQUEST_URI'], 0, strrpos($_SERVER['REQUEST_URI'], '?')) . '?force'; ?>">Restart cron</a> | |
</body> | |
</html> | |
<?php | |
} | |
// Helper functions | |
class FileUtils | |
{ | |
// Prevent instantiation | |
private function __construct() {} | |
// Modified from http://no2.php.net/manual/en/function.flock.php user comments (hhw at joymail dot com / 16-Apr-2003 08:50) | |
public static function Lock($lockFile) | |
{ | |
while (file_exists($lockFile)) | |
{ | |
clearstatcache(); | |
usleep(rand(5, 70)); | |
} | |
$file = fopen($lockFile, 'wb'); | |
fwrite($file, 'lock'); | |
fclose($file); | |
} | |
public static function Unlock($lockFile) | |
{ | |
@unlink($lockFile); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
* * * * * php test.php | |
59 * * * * php -r "require_once('cron.config.php'); mail(\$adminEmail, 'PHPCron report', strftime('PHPCron on server ' . \$_SERVER['SERVER_NAME'] . ' is operating normally at %T %Z on %a %d %B %Y.'));" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
$f = fopen('test.txt', 'a'); | |
fwrite($f, 'test executed at ' . strftime("%Y-%m-%d %H:%M:%S") . "\r\n"); | |
fclose($f); | |
exit; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment