SvgDataCollector::getLoaderSuccessCount()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 3
ccs 0
cts 2
cp 0
crap 2
rs 10
1
<?php
2
3
/*
4
 * This file is part of ocubom/twig-extra-bundle
5
 *
6
 * © Oscar Cubo Medina <https://ocubom.github.io>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Ocubom\TwigExtraBundle\DataCollector;
13
14
use Symfony\Component\HttpFoundation\Request;
15
use Symfony\Component\HttpFoundation\Response;
16
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
17
use Symfony\Component\HttpKernel\DataCollector\LateDataCollectorInterface;
18
use Symfony\Component\VarDumper\Caster\ClassStub;
19
20
class SvgDataCollector extends DataCollector implements LateDataCollectorInterface
21
{
22
    public const TRACE_SUCCESS = 'success';
23
    public const TRACE_FAILURE = 'failure';
24
    public const TRACE_WARNING = 'warning';
25
26
    public const TRACE_LEVELS = [
27
        self::TRACE_SUCCESS,
28
        self::TRACE_WARNING,
29
        self::TRACE_FAILURE,
30
    ];
31
32
    /** @var array<array-key,SvgTraceableLoader> */
33
    private array $loaders = [];
34
35
    public function addLoader(string $name, SvgTraceableLoader $loader): void
36
    {
37
        $this->loaders[$name] = $loader;
38
    }
39
40
    public function collect(Request $request, Response $response, \Throwable $exception = null): void
41
    {
42
        // Noop. Everything is collected live by the traceable loader & cloned as late as possible.
43
    }
44
45
    public function lateCollect(): void
46
    {
47
        $data = [
48
            'svg' => [
49
                'instances' => [],
50
            ],
51
            'loaders' => [
52
                'instances' => [],
53
                'counters' => [
54
                    'success' => 0,
55
                    'warning' => 0,
56
                    'failure' => 0,
57
                ],
58
            ],
59
        ];
60
61
        foreach ($this->getLogTraces() as $trace) {
62
            // Initialize loader struct
63
            $data['loaders']['instances'][$trace['loader_name']] = $data['loaders']['instances'][$trace['loader_name']] ?? [
64
                'class' => $trace['loader_class'],
65
                'counters' => [
66
                    'success' => 0,
67
                    'warning' => 0,
68
                    'failure' => 0,
69
                ],
70
            ];
71
72
            // Update loader counters
73
            ++$data['loaders']['counters'][$trace['type']];
74
            ++$data['loaders']['instances'][$trace['loader_name']]['counters'][$trace['type']];
75
76
            // Generate a unique key for the search
77
            $key = sha1($trace['search_ident']);
78
79
            $data['svg']['instances'][$key] = $data['svg']['instances'][$key] ?? [
80
                // Metadata
81
                'key' => $key,
82
                'timestamp' => $trace['ts1']->format('Y-m-d\\TH:i:s.uP'),
83
                'ts0' => $trace['ts0'],
84
                'ts1' => $trace['ts1'],
85
                // Search input
86
                'search_ident' => $trace['search_ident'],
87
                // Search output
88
                'value' => $trace['value'],
89
                'type' => $trace['type'],
90
                'level' => $trace['level'],
91
                // Extra
92
                'traces' => [],
93
                'counters' => [
94
                    'success' => 0,
95
                    'warning' => 0,
96
                    'failure' => 0,
97
                ],
98
            ];
99
100
            // Update global data if this trace improves resolution
101
            if ($trace['level'] < $data['svg']['instances'][$key]['level']) {
102
                $data['svg']['instances'][$key] = array_merge($data['svg']['instances'][$key], [
103
                    // Search output
104
                    'value' => $trace['value'],
105
                    'type' => $trace['type'],
106
                ]);
107
            }
108
109
            $traceKey = sha1(serialize([
110
                $trace['search_ident'],
111
                $trace['search_options'],
112
                $trace['loader_name'],
113
            ]));
114
115
            $data['svg']['instances'][$key]['traces'][$traceKey] = array_merge($trace, [
116
                'key' => $traceKey,
117
                'timestamp' => $trace['ts1']->format('Y-m-d\\TH:i:s.uP'),
118
            ]);
119
        }
120
121
        uasort($data['svg']['instances'], function ($x, $y) {
122
            return strnatcasecmp($x['search_ident'], $y['search_ident'])
123
                ?: $x['ts1'] <=> $y['ts1']
124
                ?: strnatcasecmp($x['loader_name'], $y['loader_name']);
125
        });
126
127
        $data['svg']['counters'] = array_reduce(
128
            $data['svg']['instances'],
129
            function ($counters, $svg) {
130
                ++$counters[$svg['type']];
131
132
                return $counters;
133
            },
134
            [
135
                'success' => 0,
136
                'warning' => 0,
137
                'failure' => 0,
138
            ]
139
        );
140
141
        $this->data = $this->cloneVar($data);
142
    }
143
144
    private function getLogTraces(): iterable
145
    {
146
        $logs = [];
147
148
        foreach ($this->loaders as $name => $loader) {
149
            foreach ($loader->getTraces() as $trace) {
150
                $trace['search_options'] = $trace['search_options'] ?? [];
151
                if (is_array($trace['search_options'])) {
152
                    uksort($trace['search_options'], 'strnatcasecmp');
153
                }
154
155
                $logs[] = array_merge($trace, [
156
                    // Loader
157
                    'loader_class' => new ClassStub($loader->getTracedClassName()),
158
                    'loader_name' => $name,
159
                    // Search output
160
                    'type' => $type = $trace['value'] instanceof \Throwable
161
                        ? self::TRACE_FAILURE
162
                        : self::TRACE_SUCCESS,
163
                    'level' => array_search($type, self::TRACE_LEVELS),
164
                    'value' => $trace['value'] instanceof \Throwable
165
                        ? $trace['value']
166
                        : (string) $trace['value'],
167
                ]);
168
            }
169
        }
170
171
        usort($logs, function ($x, $y) {
172
            return $x['ts1'] <=> $y['ts1']
173
                ?: strnatcasecmp($x['loader_name'], $y['loader_name']);
174
        });
175
176
        foreach ($logs as $trace) {
177
            yield sha1($trace['search_ident']) => $trace;
178
        }
179
    }
180
181
    public function reset(): void
182
    {
183
        $this->data = [];
184
185
        foreach ($this->loaders as $loader) {
186
            $loader->resetTraces();
187
        }
188
    }
189
190
    public function getName(): string
191
    {
192
        return 'svg';
193
    }
194
195
    public function getSvg(): array
196
    {
197
        return iterator_to_array($this->data['svg']['instances']);
198
    }
199
200
    public function getSvgTotalCount(): int
201
    {
202
        return count($this->data['svg']['instances']);
203
    }
204
205
    public function getSvgSuccessCount(): int
206
    {
207
        return $this->data['svg']['counters']['success'] ?? 0;
208
    }
209
210
    public function getSvgWarningCount(): int
211
    {
212
        return $this->data['svg']['counters']['warning'] ?? 0;
213
    }
214
215
    public function getSvgFailureCount(): int
216
    {
217
        return $this->data['svg']['counters']['failure'] ?? 0;
218
    }
219
220
    public function getLoaders(): array
221
    {
222
        return iterator_to_array(call_user_func(function () {
223
            foreach ($this->data['loaders']['instances'] as $name => $loader) {
224
                yield $name => $loader['class'];
225
            }
226
        }));
227
    }
228
229
    public function getLoaderTotalCount(string $name = null): int
230
    {
231
        return (int) array_sum($this->getLoaderCounters($name));
232
    }
233
234
    public function getLoaderSuccessCount(string $name = null): int
235
    {
236
        return $this->getLoaderCounters($name)['success'] ?? 0;
237
    }
238
239
    public function getLoaderWarningCount(string $name = null): int
240
    {
241
        return $this->getLoaderCounters($name)['warning'] ?? 0;
242
    }
243
244
    public function getLoaderFailureCount(string $name = null): int
245
    {
246
        return $this->getLoaderCounters($name)['failure'] ?? 0;
247
    }
248
249
    public function getLoaderCounters(string $name = null): array
250
    {
251
        return iterator_to_array(call_user_func(function () use ($name) {
252
            $counters = empty($name)
253
                ? $this->data['loaders']['counters']
254
                : $this->data['loaders']['instances'][$name]['counters'];
255
256
            foreach (array_keys(iterator_to_array($counters)) as $key) {
257
                yield $key => $counters[$key];
258
            }
259
        }));
260
    }
261
}
262