Passed
Push — master ( 920e7f...178763 )
by Alexander
03:00
created

SystemdState::checkFromString()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 5
nc 2
nop 1
dl 0
loc 11
rs 10
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()
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
        $retval = [];
72
        foreach (TypesInterface::ALLOWED_TYPES as $suffix) {
73
            $tempName = $unitName . '.' . $suffix;
74
            if (array_key_exists($tempName, $this->reports)) {
75
                $retval[$tempName] = $this->reports[$tempName];
76
                // Don't break here, as we can have the same unit in different types (e.g. service and timer)
77
            }
78
        }
79
80
        if (count($retval)) {
81
            return $retval;
82
        }
83
84
        throw new \RangeException(sprintf('No report found for `%s`. Maybe misspelled?', $unitName));
85
    }
86
87
    /**
88
     * @param String $string
89
     * @return SystemdState
90
     */
91
    public function checkFromString(String $string): self
92
    {
93
        $explode = explode(PHP_EOL . PHP_EOL, $string);
94
95
        if (count($explode) < 1 || strlen($string) === 0) {
96
            throw new \InvalidArgumentException('Invalid payload passed to ' . __FUNCTION__);
97
        }
98
99
        array_walk($explode, [$this, 'handleServiceResponse']);
100
101
        return $this;
102
    }
103
104
    private function checkState(): void
105
    {
106
        if (!count($this->services)) {
107
            throw new \InvalidArgumentException('You have to add at least one unit to check!');
108
        }
109
110
        $command = sprintf(self::$command, implode(' ', $this->services));
111
        $exec = shell_exec($command);
112
113
        if ($exec === null) {
114
            throw new \RuntimeException(sprintf('There was an error executing the command: `%s`', $command));
115
        }
116
117
        $this->checkFromString($exec);
118
    }
119
120
    /**
121
     * @param string $service
122
     */
123
    private function handleServiceResponse(string $service): void
124
    {
125
        $split = explode(PHP_EOL, $service);
126
        $type = TypesInterface::TYPE_SYSTEMD;
127
128
        preg_match('/Id=(?<name>.*)/m', $service, $matches);
129
130
        if (!empty($matches)) {
131
            $explode = explode('.', $matches['name']);
132
            $type = $explode[count($explode) - 1];
133
        }
134
135
        if (!in_array($type, TypesInterface::ALLOWED_TYPES)) {
136
            throw new \RuntimeException(sprintf('Type `%s` is not a valid / known type.', $type));
137
        }
138
139
        $className = __NAMESPACE__ . '\\Types\\' . ucfirst($type);
140
        /** @var AbstractType $type */
141
        $type = new $className();
142
143
        array_walk($split, [$this, 'handleLine'], $type);
144
        $this->reports[$type->getId()] = $type;
145
    }
146
147
    /**
148
     * @param string $line
149
     * @param int $key
150
     * @param TypesInterface $type
151
     */
152
    private function handleLine(string $line, int $key, TypesInterface $type): void
153
    {
154
        $explode = explode('=', $line);
155
        $propertyName = $explode[0];
156
157
        if (empty($propertyName)) {
158
            return;
159
        }
160
161
        if (!property_exists($type, $propertyName)) {
162
            throw new \RuntimeException(
163
                sprintf(
164
                    'Property `%s` is not known to me. Please fill a Bug report!' . PHP_EOL . 'Info: `%s`',
165
                    $propertyName,
166
                    $line
167
                )
168
            );
169
        }
170
171
        $type->{'set' . $propertyName}(Parser::parseValueByContent($explode));
172
    }
173
}
174