SystemdState::handleServiceResponse()   A
last analyzed

Complexity

Conditions 3
Paths 4

Size

Total Lines 22
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 12
nc 4
nop 1
dl 0
loc 22
rs 9.8666
c 0
b 0
f 0
1
<?php
2
3
namespace CyberLine\SystemdState;
4
5
use CyberLine\SystemdState\Types\AbstractType;
6
use CyberLine\SystemdState\Types\TypesInterface;
7
8
class SystemdState
9
{
10
    private static $command = '/bin/systemctl show %s --no-pager';
11
12
    private $services = [];
13
14
    private $reports = [];
15
16
    /**
17
     * SystemdState constructor.
18
     * @param array $services
19
     */
20
    public function __construct(array $services = [])
21
    {
22
        array_walk($services, [$this, 'addCheckUnit']);
23
    }
24
25
    /**
26
     * @param string $name
27
     * @return SystemdState
28
     */
29
    public function addCheckUnit(string $name): self
30
    {
31
        $this->services[] = escapeshellcmd($name);
32
33
        return $this;
34
    }
35
36
    public function addAllUnits(): self
37
    {
38
        $this->addCheckUnit('*');
39
40
        return $this;
41
    }
42
43
    /**
44
     * @return array
45
     */
46
    public function getSystemdInfo(): array
47
    {
48
        $this->addCheckUnit('');
49
50
        return $this->getReport('systemd');
51
    }
52
53
    /**
54
     * @param string|null $unitName
55
     * @return array
56
     */
57
    public function getReport(?string $unitName = null): array
58
    {
59
        if (empty($this->reports)) {
60
            $this->checkState();
61
        }
62
63
        if (!$unitName) {
64
            return $this->reports;
65
        }
66
67
        if (array_key_exists($unitName, $this->reports)) {
68
            return [$this->reports[$unitName]];
69
        }
70
71
        return $this->getReportsByTypeMultiple($unitName);
72
    }
73
74
    /**
75
     * @param String $string
76
     * @return SystemdState
77
     */
78
    public function checkFromString(String $string): self
79
    {
80
        $explode = explode(PHP_EOL . PHP_EOL, $string);
81
82
        if ($string === '' || count($explode) < 1) {
83
            throw new \InvalidArgumentException('Invalid payload passed to ' . __FUNCTION__);
84
        }
85
86
        array_walk($explode, [$this, 'handleServiceResponse']);
87
88
        return $this;
89
    }
90
91
    private function checkState(): void
92
    {
93
        if (!count($this->services)) {
94
            throw new \InvalidArgumentException('You have to add at least one unit to check!');
95
        }
96
97
        $command = sprintf(self::$command, implode(' ', $this->services));
98
        $exec = shell_exec($command);
99
100
        if ($exec === null) {
101
            throw new \RuntimeException(sprintf('There was an error executing the command: `%s`', $command));
102
        }
103
104
        $this->checkFromString($exec);
105
    }
106
107
    /**
108
     * @param string $service
109
     */
110
    private function handleServiceResponse(string $service): void
111
    {
112
        $split = explode(PHP_EOL, $service);
113
        $type = TypesInterface::TYPE_SYSTEMD;
114
115
        preg_match('/Id=(?<name>.*)/m', $service, $matches);
116
117
        if (!empty($matches)) {
118
            $explode = explode('.', $matches['name']);
119
            $type = $explode[count($explode) - 1];
120
        }
121
122
        if (!in_array($type, TypesInterface::ALLOWED_TYPES, true)) {
123
            throw new \RuntimeException(sprintf('Type `%s` is not a valid / known type.', $type));
124
        }
125
126
        $className = __NAMESPACE__ . '\\Types\\' . ucfirst($type);
127
        /** @var AbstractType $type */
128
        $type = new $className();
129
130
        array_walk($split, [$this, 'handleLine'], $type);
131
        $this->reports[$type->getId()] = $type;
132
    }
133
134
    /**
135
     * @param string $line
136
     * @param int $key
137
     * @param TypesInterface $type
138
     */
139
    private function handleLine(string $line, int $key, TypesInterface $type): void
140
    {
141
        $explode = explode('=', $line, 2);
142
        $propertyName = $explode[0];
143
144
        if (empty($propertyName)) {
145
            return;
146
        }
147
148
        $type->{'set' . $propertyName}(Parser::parseValueByContent($explode));
149
    }
150
151
    /**
152
     * @param string $unitName
153
     * @return array
154
     */
155
    private function getReportsByTypeMultiple(string $unitName): array
156
    {
157
        $retval = [];
158
        foreach (TypesInterface::ALLOWED_TYPES as $suffix) {
159
            $tempName = $unitName . '.' . $suffix;
160
            if (array_key_exists($tempName, $this->reports)) {
161
                $retval[$tempName] = $this->reports[$tempName];
162
                // Don't break here, as we can have the same unit in different types (e.g. service and timer)
163
            }
164
        }
165
166
        if (count($retval)) {
167
            return $retval;
168
        }
169
170
        throw new \RangeException(sprintf('No report found for `%s`. Maybe misspelled?', $unitName));
171
    }
172
}
173