CommandExecutionLock::isLocked()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 3
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
1
<?php
2
3
namespace Weew\Console;
4
5
use Weew\Console\Exceptions\CommandIsAlreadyRunningException;
6
use Weew\ConsoleArguments\ICommand;
7
8
class CommandExecutionLock implements ICommandExecutionLock {
9
    /**
10
     * @var array
11
     */
12
    protected $locks = [];
13
14
    /**
15
     * CommandExecutionLock constructor.
16
     */
17
    public function __construct() {
18
        $this->handleShutdowns();
19
    }
20
21
    /**
22
     * @param ICommand $command
23
     * @param bool $allowParallelism
24
     *
25
     * @return string
26
     */
27
    public function lockCommand(ICommand $command, $allowParallelism = true) {
28
        if ($command->isParallel() && $allowParallelism) {
29
            return;
30
        }
31
32
        if ($this->isLocked($command->getName())) {
33
            throw new CommandIsAlreadyRunningException(s(
34
                'Command "%s" is already being executed. ' .
35
                'Parallel execution for this command has been forbidden. ' .
36
                'This is the corresponding lock file "%s".',
37
                $command->getName(),
38
                $this->getLockName($command->getName())
39
            ));
40
        }
41
42
        return $this->createLock($command->getName());
43
    }
44
45
    /**
46
     * @param ICommand $command
47
     */
48
    public function unlockCommand(ICommand $command) {
49
        $this->deleteLock($command->getName());
50
    }
51
52
    /**
53
     * Delete all locks created by this particular instance.
54
     */
55
    public function unlockAllCommands() {
56
        $this->deleteAllLocks();
57
    }
58
59
    /**
60
     * Handle shutdown events and clean up lock files.
61
     */
62
    protected function handleShutdowns() {
63
        declare(ticks = 1);
64
65
        $self = $this;
66
67
        $cleanup = function($signal = null) use ($self) {
68
            if ($signal === SIGTERM) {
69
                fprintf(STDERR, 'Received SIGTERM...');
70
            } else if ($signal === SIGINT) {
71
                fprintf(STDERR, 'Received SIGINT...');
72
            } else if ($signal === SIGTSTP) {
73
                fprintf(STDERR, 'Received SIGTSTP...');
74
            }
75
76
            $self->deleteAllLocks();
77
            exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method handleShutdowns() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
78
        };
79
80
        if (extension_loaded('pcntl')) {
81
            pcntl_signal(SIGTERM, $cleanup, false);
82
            pcntl_signal(SIGINT, $cleanup, false);
83
            pcntl_signal(SIGTSTP, $cleanup, false);
84
        }
85
86
        register_shutdown_function($cleanup);
87
    }
88
89
    /**
90
     * @return string
91
     */
92
    protected function getLockFileBaseName() {
93
        return path(sys_get_temp_dir(), md5(__DIR__), 'console_lock');
94
    }
95
96
    /**
97
     * @param string $value
98
     *
99
     * @return string
100
     */
101
    protected function getLockName($value) {
102
        return s('%s_%s', $this->getLockFileBaseName(), md5($value));
103
    }
104
105
    /**
106
     * @param string $value
107
     *
108
     * @return string
109
     */
110
    protected function createLock($value) {
111
        $lockFile = $this->getLockName($value);
112
        file_create($lockFile);
113
        $this->locks[$value] = $lockFile;
114
115
        return $lockFile;
116
    }
117
118
    /**
119
     * @param string $value
120
     */
121
    protected function deleteLock($value) {
122
        file_delete($this->getLockName($value));
123
        unset($this->locks[$value]);
124
    }
125
126
    /**
127
     * Remove all locks for commands called trough
128
     * this particular lock instance.
129
     */
130
    protected function deleteAllLocks() {
131
        foreach ($this->locks as $commandName => $lockFile) {
132
            $this->deleteLock($commandName);
133
        }
134
    }
135
136
    /**
137
     * @param string $value
138
     *
139
     * @return bool
140
     */
141
    protected function isLocked($value) {
142
        return file_exists($this->getLockName($value));
143
    }
144
}
145