-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathAbstractTerminableCommand.php
More file actions
121 lines (93 loc) · 3.51 KB
/
AbstractTerminableCommand.php
File metadata and controls
121 lines (93 loc) · 3.51 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
<?php
declare(strict_types=1);
namespace Facile\TerminableLoop;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Command\SignalableCommandInterface;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
if (
PHP_VERSION_ID >= 8_02_00
&& interface_exists(SignalableCommandInterface::class)
&& in_array(
SignalableCommandInterface::class,
class_implements(Command::class),
true
)
) {
require_once __DIR__ . '/AbstractTerminableCommandAfterSymfony7_3.php';
} else {
abstract class AbstractTerminableCommand extends Command
{
private const REQUEST_TO_TERMINATE = 143;
/** @var int */
private $sleepDuration;
/** @var bool */
private $signalShutdownRequested;
public function __construct(?string $name = null)
{
$this->sleepDuration = 0;
$this->signalShutdownRequested = false;
parent::__construct($name);
}
final protected function execute(InputInterface $input, OutputInterface $output): int
{
$this->trapSignals();
$output->writeln('Starting ' . ($this->getName() ?? static::class), OutputInterface::VERBOSITY_VERBOSE);
if ($this->signalShutdownRequested) {
$output->writeln('Signal received, skipping execution', OutputInterface::VERBOSITY_NORMAL);
return self::REQUEST_TO_TERMINATE;
}
$exitCode = $this->commandBody($input, $output);
$this->sleep($output);
/** @psalm-suppress DocblockTypeContradiction */
if ($this->signalShutdownRequested) {
$output->writeln('Signal received, terminating with exit code ' . self::REQUEST_TO_TERMINATE, OutputInterface::VERBOSITY_NORMAL);
return self::REQUEST_TO_TERMINATE;
}
return $exitCode;
}
abstract protected function commandBody(InputInterface $input, OutputInterface $output): int;
public function handleSignal(int $signal): void
{
switch ($signal) {
// Shutdown signals
case SIGTERM:
case SIGINT:
$this->signalShutdownRequested = true;
break;
}
}
private function trapSignals(): void
{
pcntl_async_signals(true);
// Add the signal handler
pcntl_signal(SIGTERM, [$this, 'handleSignal']);
pcntl_signal(SIGINT, [$this, 'handleSignal']);
}
protected function getSleepDuration(): int
{
return $this->sleepDuration;
}
protected function setSleepDuration(int $sleepDuration): void
{
if ($sleepDuration < 0) {
throw new \InvalidArgumentException('Invalid timeout provided to ' . __METHOD__);
}
$this->sleepDuration = $sleepDuration;
}
private function sleep(OutputInterface $output): void
{
if (0 === $this->sleepDuration) {
return;
}
$sleepCountDown = $this->sleepDuration;
while (! $this->signalShutdownRequested && --$sleepCountDown) {
sleep(1);
}
$output->writeln(
sprintf('Slept %d second(s)', $this->sleepDuration - $sleepCountDown),
OutputInterface::VERBOSITY_DEBUG
);
}
}
}