Command::__construct()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 1
Bugs 0 Features 1
Metric Value
eloc 5
c 1
b 0
f 1
dl 0
loc 8
ccs 0
cts 8
cp 0
rs 10
cc 3
nc 3
nop 2
crap 12
1
<?php
2
3
namespace kalanis\kw_clipr\Support\Posix;
4
5
6
use kalanis\kw_clipr\Interfaces\IStatuses;
7
8
9
/**
10
 * Class Command
11
 * @package kalanis\kw_clipr\Support\Posix
12
 * Processing external commands on posix machine
13
 * @codeCoverageIgnore because it runs Exec
14
 */
15
class Command
16
{
17
    protected string $command = '';
18
    protected bool $success = false;
19
    protected int $returnStatus = 0;
20
    /**
21
     * Output - what it write to output lines
22
     * @var array<int, string>
23
     */
24
    protected array $outputLines = [];
25
26
    /**
27
     * Run command immediately
28
     * @param string|null $command
29
     * @param array<int, string>|null $outputLines
30
     */
31
    public function __construct(?string $command = null, ?array &$outputLines = null)
32
    {
33
        if (!$this->isExecFuncAvailable()) {
34
            throw new \LogicException('This system did not allow to use Exec.', IStatuses::STATUS_SW_INTERNAL);
35
        }
36
        if (!is_null($command)) {
37
            $this->setCommand($command);
38
            $this->exec($outputLines);
39
        }
40
    }
41
42
    protected function isExecFuncAvailable(): bool
43
    {
44
        if (
45
            in_array(strtolower(strval(ini_get('safe_mode'))), array('on', '1'), true)
46
            || (!function_exists('exec'))
47
        ) {
48
            return false;
49
        }
50
        return !in_array('exec', array_map('trim', explode(',', strval(ini_get('disable_functions')))));
51
    }
52
53
    public function setCommand(string $command): void
54
    {
55
        $this->command = $command;
56
        $this->success = false;
57
        $this->returnStatus = 0;
58
        $this->outputLines = [];
59
    }
60
61
    public function getCommand(): string
62
    {
63
        return $this->command;
64
    }
65
66
    public function hasSuccess(): bool
67
    {
68
        return $this->success;
69
    }
70
71
    public function getReturnStatus(): int
72
    {
73
        return $this->returnStatus;
74
    }
75
76
    /**
77
     * @return array<int, string>
78
     */
79
    public function getOutputLines(): array
80
    {
81
        return $this->outputLines;
82
    }
83
84
    /**
85
     * Run preset command via EXEC
86
     * @param array<int, string>|null $outputLines
87
     * @return bool
88
     */
89
    public function exec(array &$outputLines = null): bool
90
    {
91
        exec($this->command, $outputLines, $return);
92
        $this->outputLines = $outputLines;
93
        $this->returnStatus = $return;
94
        $this->success = (0 === $return);
95
        return $this->success;
96
    }
97
98
    /**
99
     * Run preset command and return process id
100
     * @return string
101
     */
102
    public function runOnBackground(): string
103
    {
104
        $this->command .= ' & echo $!';
105
        $this->exec();
106
        return $this->outputLines[0];
107
    }
108
109
    /**
110
     * Run preset command on background, yet still wait for finish
111
     * @param int $processCheckPeriod
112
     * @return bool
113
     */
114
    public function execOnBackground(int $processCheckPeriod = 5): bool
115
    {
116
        $iTimeStarted = time();
117
        $processId = $this->runOnBackground();
118
        if (!$processId) {
119
            return $this->returnError(IStatuses::STATUS_NO_PROCESS_ID);
120
        }
121
        while (true) {
122
            $cmd = new self('ps ' . $processId);
123
            if ($cmd->hasSuccess()) {
124
                if ((time() - $iTimeStarted) >= $iTimeStarted) {
125
                    return $this->returnError(IStatuses::STATUS_TIMEOUT);
126
                }
127
                sleep($processCheckPeriod);
128
            } else {
129
                $this->success = true;
130
                return true;
131
            }
132
        }
133
        // @phpstan-ignore-next-line
134
        return false;
135
    }
136
137
    protected function returnError(int $iErrorCode): bool
138
    {
139
        $this->success = false;
140
        $this->returnStatus = $iErrorCode;
141
        return false;
142
    }
143
}
144