Passed
Push — master ( 1ebea5...fd2df3 )
by Alexander
03:01
created

SystemdState::handleLine()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 20
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 11
nc 3
nop 3
dl 0
loc 20
rs 9.9
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
    private function checkState(): void
88
    {
89
        if (!count($this->services)) {
90
            throw new \InvalidArgumentException('You have to add at least one unit to check!');
91
        }
92
93
        $command = sprintf(self::$command, implode(' ', $this->services));
94
        $exec = shell_exec($command);
95
96
        if ($exec === null) {
97
            throw new \RuntimeException(sprintf('There was an error executing the command: `%s`', $command));
98
        }
99
100
        $explode = explode(PHP_EOL . PHP_EOL, $exec);
101
        array_walk($explode, [$this, 'handleServiceResponse']);
102
    }
103
104
    /**
105
     * @param string $service
106
     */
107
    private function handleServiceResponse(string $service): void
108
    {
109
        $split = explode(PHP_EOL, $service);
110
        $type = TypesInterface::TYPE_SYSTEMD;
111
112
        preg_match('/Id=(?<name>.*)/m', $service, $matches);
113
114
        if (!empty($matches)) {
115
            $explode = explode('.', $matches['name']);
116
            $type = $explode[count($explode) - 1];
117
        }
118
119
        if (!in_array($type, TypesInterface::ALLOWED_TYPES)) {
120
            throw new \RuntimeException(sprintf('Type `%s` is not a valid / known type.', $type));
121
        }
122
123
        $className = __NAMESPACE__ . '\\Types\\' . ucfirst($type);
124
        /** @var AbstractType $class */
125
        $type = new $className();
126
127
        array_walk($split, [$this, 'handleLine'], $type);
128
        $this->reports[$type->getId()] = $type;
129
    }
130
131
    /**
132
     * @param string $line
133
     * @param int $key
134
     * @param TypesInterface $type
135
     */
136
    private function handleLine(string $line, int $key, TypesInterface $type): void
137
    {
138
        $explode = explode('=', $line);
139
        $propertyName = $explode[0];
140
141
        if (empty($propertyName)) {
142
            return;
143
        }
144
145
        if (!property_exists($type, $propertyName)) {
146
            throw new \RuntimeException(
147
                sprintf(
148
                    'Property `%s` is not known to me. Please fill a Bug report!' . PHP_EOL . 'Info: `%s`',
149
                    $propertyName,
150
                    $line
151
                )
152
            );
153
        }
154
155
        $type->{'set' . $propertyName}(Parser::parseValueByContent($explode));
156
    }
157
}
158