Prometheus   A
last analyzed

Complexity

Total Complexity 33

Size/Duplication

Total Lines 210
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 33
eloc 88
c 1
b 0
f 0
dl 0
loc 210
ccs 103
cts 103
cp 1
rs 9.76

14 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 1
A extractLabels() 0 19 4
A setMetric() 0 19 2
A setMetricValues() 0 7 2
A setHelpText() 0 15 2
A handleLine() 0 6 1
A extractMetricNameAndSuffix() 0 11 2
A setLabels() 0 4 2
A parseStream() 0 9 2
A setType() 0 14 2
A setTimestamp() 0 4 3
A parseText() 0 15 3
A createMetricIfNew() 0 4 2
A setBucket() 0 22 5
1
<?php
2
3
declare(strict_types=1);
4
5
namespace ArangoClient\Prometheus;
6
7
use GuzzleHttp\Psr7\StreamWrapper;
8
use Psr\Http\Message\ResponseInterface;
9
10
class Prometheus
11
{
12
    protected Metrics $output;
13
14 4
    public function __construct()
15
    {
16 4
        $this->output = new Metrics();
17
    }
18
19
20 1
    public function parseStream(ResponseInterface $from): Metrics
21
    {
22 1
        $stream = $from->getBody();
23 1
        $resource = StreamWrapper::getResource($stream);
24
25 1
        while ($line = stream_get_line($resource, 1000000, "\n")) {
26 1
            $this->handleLine($line);
27
        }
28 1
        return $this->output;
29
    }
30
31 3
    public function parseText(string $content): Metrics
32
    {
33 3
        $fileObject = new \SplFileObject('php://memory', 'r+');
34 3
        $fileObject->fwrite($content);
35 3
        $fileObject->rewind();
36
37 3
        while ($fileObject->valid()) {
38 3
            $line = $fileObject->current();
39 3
            if (is_string($line)) {
40 3
                $this->handleLine($line);
41
            }
42 3
            $fileObject->next();
43
        }
44
45 3
        return $this->output;
46
    }
47
48 4
    protected function handleLine(string $line): void
49
    {
50 4
        match (true) {
51 4
            str_starts_with($line, "# TYPE ") => $this->setType($line),
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->setType($line) targeting ArangoClient\Prometheus\Prometheus::setType() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
52 4
            str_starts_with($line, "# HELP ") => $this->setHelpText($line),
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->setHelpText($line) targeting ArangoClient\Prometheus\Prometheus::setHelpText() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
53 4
            default =>  $this->setMetric($line),
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->setMetric($line) targeting ArangoClient\Prometheus\Prometheus::setMetric() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
54 4
        };
55
    }
56
57 4
    protected function createMetricIfNew(string $name): void
58
    {
59 4
        if (!property_exists($this->output, $name)) {
60 4
            $this->output->$name = new Metric($name);
61
        }
62
    }
63
64 4
    protected function setType(string $line): void
65
    {
66 4
        $matches = [];
67
        if (
68 4
            preg_match(
69 4
                "/^# TYPE (?'metricName'[a-zA-Z_:][a-zA-Z0-9_:]*) (?'type'counter|gauge|histogram|summary)$/",
70 4
                $line,
71 4
                $matches,
72 4
            )
73
        ) {
74 4
            $metricName = $matches['metricName'];
75 4
            $this->createMetricIfNew($metricName);
76
77 4
            $this->output->$metricName->type = $matches['type'];
78
        }
79
    }
80
81 4
    protected function setHelpText(string $line): void
82
    {
83 4
        $matches = [];
84
        if (
85 4
            preg_match(
86 4
                "/^# HELP (?'metricName'[a-zA-Z_:][a-zA-Z0-9_:]*) (?'helpText'[\W\w]*)/",
87 4
                $line,
88 4
                $matches,
89 4
            )
90
        ) {
91 4
            $metricName = $matches['metricName'];
92
93 4
            $this->createMetricIfNew($metricName);
94
95 4
            $this->output->$metricName->help = $matches['helpText'];
96
        }
97
    }
98
99 4
    protected function setMetric(string $line): void
100
    {
101 4
        $matches = [];
102
        if (
103 4
            preg_match(
104 4
                "/^(?'rawMetricName'[a-zA-Z_:][a-zA-Z0-9_:]*)(?'rawLabels'{[\W\w]*})? (?'value'[-+.,eE\d]+) ?(?'timestamp'[0-9]+)?$/",
105 4
                $line,
106 4
                $matches,
107 4
            )
108
        ) {
109
            // The first group contains the metricName and the suffix
110 4
            [$metricName, $suffix] = $this->extractMetricNameAndSuffix($matches['rawMetricName']);
111
112 4
            $this->createMetricIfNew($metricName);
113
114
            // Feed the remaining matches to the suffix dependent parser
115 4
            match ($suffix) {
116 4
                "bucket" => $this->setBucket($metricName, $matches),
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->setBucket($metricName, $matches) targeting ArangoClient\Prometheus\Prometheus::setBucket() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
117 4
                default => $this->setMetricValues($metricName, $matches, $suffix),
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->setMetricValues($...ame, $matches, $suffix) targeting ArangoClient\Prometheus\...heus::setMetricValues() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
118 4
            };
119
        }
120
    }
121
122
    /**
123
     * @return string[]
124
     */
125 4
    protected function extractMetricNameAndSuffix(string $value): array
126
    {
127 4
        $nameParticles = explode("_", $value);
128 4
        $suffix = array_pop($nameParticles);
129
130 4
        if (in_array($suffix, ["bucket", "count", "sum"])) {
131 3
            $baseName = implode("_", $nameParticles);
132 3
            return [$baseName, $suffix];
133
        }
134
135 3
        return [$value, 'value'];
136
    }
137
138
    /**
139
     * @param array<string|int|float> $matches
140
     */
141 2
    protected function setBucket(mixed $metricName, array $matches): void
142
    {
143 2
        $labels = null;
144 2
        $timestamp = null;
145
146 2
        $value = is_numeric($matches['value']) ? +$matches['value'] : $matches['value'];
147
148 2
        if (array_key_exists("rawLabels", $matches)) {
149 2
            $labels = $this->extractLabels((string) $matches['rawLabels']);
150
        }
151
152 2
        if (array_key_exists("timestamp", $matches)) {
153 1
            $timestamp = is_numeric($matches['timestamp']) ? +$matches['timestamp'] : $matches['timestamp'];
154
        }
155
156 2
        $bucket = new Bucket(
157 2
            $value,
158 2
            $labels,
159 2
            $timestamp,
160 2
        );
161
162 2
        $this->output->$metricName->buckets[] = $bucket;
163
    }
164
165
    /**
166
     * @param array<string, mixed> $matches
167
     */
168 4
    protected function setMetricValues(string $metricName, array $matches, string $suffix): void
169
    {
170 4
        $this->output->$metricName->$suffix = is_numeric($matches['value']) ? +$matches['value'] : $matches['value'];
171
172 4
        $this->setLabels($metricName, $matches);
173
174 4
        $this->setTimestamp($metricName, $matches);
175
    }
176
177
    /**
178
     * @param array<string, mixed> $matches
179
     */
180 4
    protected function setLabels(string $metricName, array $matches): void
181
    {
182 4
        if (array_key_exists("rawLabels", $matches)) {
183 4
            $this->output->$metricName->labels  = $this->extractLabels($matches['rawLabels']);
184
        }
185
    }
186
187
    /**
188
     * @param array<string, mixed> $matches
189
     */
190 4
    protected function setTimestamp(string $metricName, array $matches): void
191
    {
192 4
        if (array_key_exists("timestamp", $matches)) {
193 1
            $this->output->$metricName->timestamp = is_numeric($matches['timestamp']) ? +$matches['timestamp'] : $matches['timestamp'];
194
        }
195
    }
196
197
198
    /**
199
     * @return array<string, float|int|string>
200
     */
201 4
    protected function extractLabels(string $rawLabels): array
202
    {
203 4
        $labels = [];
204 4
        $matches = [];
205
        if (
206 4
            preg_match_all(
207 4
                "/(?'label'[a-zA-Z0-9]*)=\"(?'value'[^\"]*)\"/",
208 4
                $rawLabels,
209 4
                $matches,
210 4
            )
211
        ) {
212 4
            foreach ($matches['label'] as $key => $label) {
213 4
                $value = $matches['value'][$key];
214
215 4
                $labels[(string) $label] = is_numeric($value) ? +$value : $value;
216
            }
217
        }
218
219 4
        return $labels;
220
    }
221
}
222