Ellrion
9/14/2016 - 1:03 PM

OverlappingControlled.php

<?php 

namespace App\Console;

use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

trait OverlappingControlled
{
    /**
     * Дескриптор файла блокировки.
     *
     * @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()
    {
        return empty($this->withoutOverlapping) ? LOCK_UN : LOCK_EX | LOCK_NB;
    }

    /**
     * Захват блокировки.
     *
     * @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 !empty($this->withoutOverlapping) && !$this->option('force');
    }

    /**
     * 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()) {
          $result = parent::execute($input, $output);
        }

        $this->free();

        return $result;
    }
}