Я расширил концепцию от zerkms, чтобы создать функцию, которую можно вызывать из начала cron.
Используя Cronlocker, вы указываете имя блокировки, а затем имя функции обратного вызова, которая будет вызываться, если cron выключен. При желании вы можете передать массив параметров функции обратного вызова. Также есть дополнительная функция обратного вызова, если вам нужно сделать что-то другое, если блокировка включена.
В некоторых случаях я получал несколько исключений и хотел иметь возможность их перехватить, и я добавил функцию для обработки фатальных исключений, которую следует добавить. Я хотел иметь возможность открыть файл из браузера и обойти cronlock, так что это встроено.
Я обнаружил, что, поскольку я часто использовал это, были случаи, когда я хотел заблокировать запуск других крон во время работы этого cron, поэтому я добавил дополнительный массив блокировок, которые являются другими именами блокировок для блокировки.
Затем были случаи, когда я хотел, чтобы этот cron запускался после того, как другие crons завершили работу, поэтому есть необязательный массив ожиданий блокировок, которые представляют собой другие имена блокировок для ожидания, пока ни один из них не будет запущен.
простой пример:
Cronlocker::CronLock('cron1', 'RunThis');
function RunThis() {
echo('I ran!');
}
параметры обратного вызова и функции отказа:
Cronlocker::CronLock('cron2', 'RunThat', ['ran'], 'ImLocked');
function RunThat($x) {
echo('I also ran! ' . $x);
}
function ImLocked($x) {
echo('I am locked :-( ' . $x);
}
блокировка и ожидание:
Cronlocker::CronLock('cron3', 'RunAgain', null, null, ['cron1'], ['cron2']);
function RunAgain() {
echo('I ran.<br />');
echo('I block cron1 while I am running.<br />')
echo('I wait for cron2 to finish if it is running.');
}
класс:
class Cronlocker {
private static $LockFile = null;
private static $LockFileBlocks = [];
private static $LockFileWait = null;
private static function GetLockfileName($lockname) {
return "/tmp/lock-" . $lockname . ".txt";
}
/**
* Locks a PHP script from being executed more than once at a time
* @param string $lockname Use a unique lock name for each lock that needs to be applied.
* @param string $callback The name of the function to call if the lock is OFF
* @param array $callbackParams Optional array of parameters to apply to the callback function when called
* @param string $callbackFail Optional name of the function to call if the lock is ON
* @param string[] $lockblocks Optional array of locknames for other crons to also block while this cron is running
* @param string[] $lockwaits Optional array of locknames for other crons to wait until they finish running before this cron will run
* @see http://stackoverflow.com/questions/5428631/php-preventing-collision-in-cron-file-lock-safe
*/
public static function CronLock($lockname, $callback, $callbackParams = null, $callbackFail = null, $lockblocks = [], $lockwaits = []) {
// check all the crons we are waiting for to finish running
if (!empty($lockwaits)) {
$waitingOnCron = true;
while ($waitingOnCron) {
$waitingOnCron = false;
foreach ($lockwaits as $lockwait) {
self::$LockFileWait = null;
$tempfile = self::GetLockfileName($lockwait);
try {
self::$LockFileWait = fopen($tempfile, "w+");
} catch (Exception $e) {
//ignore error
}
if (flock(self::$LockFileWait, LOCK_EX | LOCK_NB)) { // do an exclusive lock
// cron we're waiting on isn't running
flock(self::$LockFileWait, LOCK_UN); // release the lock
} else {
// we're wating on a cron
$waitingOnCron = true;
}
if (is_resource(self::$LockFileWait))
fclose(self::$LockFileWait);
if ($waitingOnCron) break; // no need to check any more
}
if ($waitingOnCron) sleep(15); // wait a few seconds
}
}
// block any additional crons from starting
if (!empty($lockblocks)) {
self::$LockFileBlocks = [];
foreach ($lockblocks as $lockblock) {
$tempfile = self::GetLockfileName($lockblock);
try {
$block = fopen($tempfile, "w+");
} catch (Exception $e) {
//ignore error
}
if (flock($block, LOCK_EX | LOCK_NB)) { // do an exclusive lock
// lock made
self::$LockFileBlocks[] = $block;
} else {
// couldn't lock it, we ignore and move on
}
}
}
// set the cronlock
self::$LockFile = null;
$tempfile = self::GetLockfileName($lockname);
$return = null;
try {
if (file_exists($tempfile) && !is_writable($tempfile)) {
//assume we're hitting this from a browser and execute it regardless of the cronlock
if (empty($callbackParams))
$return = $callback();
else
$return = call_user_func_array($callback, $callbackParams);
} else {
self::$LockFile = fopen($tempfile, "w+");
}
} catch (Exception $e) {
//ignore error
}
if (!empty(self::$LockFile)) {
if (flock(self::$LockFile, LOCK_EX | LOCK_NB)) { // do an exclusive lock
// do the work
if (empty($callbackParams))
$return = $callback();
else
$return = call_user_func_array($callback, $callbackParams);
flock(self::$LockFile, LOCK_UN); // release the lock
} else {
// call the failed function
if (!empty($callbackFail)) {
if (empty($callbackParams))
$return = $callbackFail();
else
$return = call_user_func_array($callbackFail, $callbackParams);
}
}
if (is_resource(self::$LockFile))
fclose(self::$LockFile);
}
// remove any lockblocks
if (!empty($lockblocks)) {
foreach (self::$LockFileBlocks as $LockFileBlock) {
flock($LockFileBlock, LOCK_UN); // release the lock
if (is_resource($LockFileBlock))
fclose($LockFileBlock);
}
}
return $return;
}
/**
* Releases the Cron Lock locking file, useful to specify on fatal errors
*/
public static function ReleaseCronLock() {
// release the cronlock
if (!empty(self::$LockFile) && is_resource(self::$LockFile)) {
var_dump('Cronlock released after error encountered: ' . self::$LockFile);
flock(self::$LockFile, LOCK_UN);
fclose(self::$LockFile);
}
// release any lockblocks too
foreach (self::$LockFileBlocks as $LockFileBlock) {
if (!empty($LockFileBlock) && is_resource($LockFileBlock)) {
flock($LockFileBlock, LOCK_UN);
fclose($LockFileBlock);
}
}
}
}
Также следует реализовать на общей странице или встроить в существующий обработчик фатальных ошибок:
function fatal_handler() {
// For cleaning up crons that fail
Cronlocker::ReleaseCronLock();
}
register_shutdown_function("fatal_handler");
person
Nico Westerdale
schedule
26.04.2018
flock
- person zerkms   schedule 25.03.2011flock
, будет снята. - person zerkms   schedule 25.03.2011