Created
September 14, 2016 12:36
-
-
Save Ellrion/f8c288bf2e85fb04c48fc0b33b5379ce to your computer and use it in GitHub Desktop.
Консольная команда с блокировкой.
This file contains hidden or 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 | |
namespace App\Console; | |
use Illuminate\Console\Command; | |
use Symfony\Component\Console\Input\InputInterface; | |
use Symfony\Component\Console\Output\OutputInterface; | |
class BaseCommand extends Command | |
{ | |
/** | |
* Обычный запуск команды. | |
* Возможно конкурентное выполнение команды. | |
*/ | |
const RUN_SIMPLE = 0; | |
/** | |
* Эксклюзивный запуск команды. | |
* Выполняется только один экземпляр команды. | |
*/ | |
const RUN_BLOCKING = 1; | |
/** | |
* Организует очередь экземпляров команд. | |
* Выполняется только один экземпляр команды, | |
* каждый последующий запуск команды помещает ее в очередь на выполнение. | |
*/ | |
const RUN_QUEUE = 2; | |
/** | |
* Управление запуском команды. Задается константой с префиксом "RUN_". | |
* | |
* @var int | |
*/ | |
protected $behavior = self::RUN_SIMPLE; | |
/** | |
* Дескриптор файла блокировки. | |
* | |
* @var resource | |
*/ | |
protected $lockHandle; | |
/** | |
* Возвращает путь к файлу блокировки. | |
* По-умолчанию, это "storage/lock/(имя команды).lock". | |
* | |
* @return string | |
*/ | |
protected function getLockPath() | |
{ | |
return storage_path() . '/lock/' . strtolower(str_replace(':', '_', $this->name)) . '.lock'; | |
} | |
/** | |
* Возвращает флаги для захвата блокировки. | |
* | |
* @return int | |
*/ | |
protected function getLockFlags() | |
{ | |
switch ($this->behavior) { | |
case self::RUN_BLOCKING: | |
return LOCK_EX | LOCK_NB; | |
case self::RUN_QUEUE: | |
return LOCK_EX; | |
default: | |
return LOCK_UN; | |
} | |
} | |
/** | |
* Захват блокировки. | |
* | |
* @return bool | |
*/ | |
protected function lock() | |
{ | |
if (is_resource($this->lockHandle)) { | |
$this->free(); | |
} | |
$this->lockHandle = fopen($this->getLockPath(), 'c'); | |
$lock = false !== $this->lockHandle && flock($this->lockHandle, $this->getLockFlags()); | |
if (false === $lock) { | |
$this->lockHandle = null; | |
} | |
return $lock; | |
} | |
/** | |
* Освобождение блокировки. | |
* | |
* @return bool | |
*/ | |
protected function free() | |
{ | |
if (!is_resource($this->lockHandle)) { | |
return false; | |
} | |
fflush($this->lockHandle); | |
flock($this->lockHandle, LOCK_UN); | |
fclose($this->lockHandle); | |
return $this->removeLockFile(); | |
} | |
/** | |
* Удаление файла блокировки | |
* | |
* @return bool | |
*/ | |
protected function removeLockFile() | |
{ | |
try { | |
$file = $this->getLockPath(); | |
if (is_file($file) && !is_link($file)) { | |
if (@unlink($file) === false) { | |
throw new \Exception(sprintf('Failed to remove lock file "%s".', $file)); | |
} | |
} else { | |
return false; | |
} | |
return true; | |
} catch (\Exception $e) { | |
return false; | |
} | |
} | |
/** | |
* Проверяет, используется ли блокировка в управлении запуском. | |
* | |
* @return bool | |
*/ | |
protected function isUsedLock() | |
{ | |
return $this->behavior !== self::RUN_SIMPLE; | |
} | |
/** | |
* Execute the console command. | |
* | |
* @param \Symfony\Component\Console\Input\InputInterface $input | |
* @param \Symfony\Component\Console\Output\OutputInterface $output | |
* @return mixed | |
*/ | |
protected function execute(InputInterface $input, OutputInterface $output) | |
{ | |
if (!$this->isUsedLock() || $this->lock()) { | |
$method = method_exists($this, 'handle') ? 'handle' : 'fire'; | |
call_user_func([$this, $method]); | |
} | |
$this->free(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment