Toolbar::renderTimelineRecursive()   F
last analyzed

Complexity

Conditions 16
Paths 1921

Size

Total Lines 60
Code Lines 37

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 272

Importance

Changes 0
Metric Value
cc 16
eloc 37
c 0
b 0
f 0
nc 1921
nop 8
dl 0
loc 60
ccs 0
cts 31
cp 0
crap 272
rs 1.4

How to fix   Long Method    Complexity    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
/**
4
 * This file is part of Blitz PHP framework.
5
 *
6
 * (c) 2022 Dimitri Sitchet Tomkeu <[email protected]>
7
 *
8
 * For the full copyright and license information, please view
9
 * the LICENSE file that was distributed with this source code.
10
 */
11
12
namespace BlitzPHP\Debug;
13
14
use BlitzPHP\Core\Application;
15
use BlitzPHP\Debug\Toolbar\Collectors\BaseCollector;
16
use BlitzPHP\Debug\Toolbar\Collectors\Config;
17
use BlitzPHP\Debug\Toolbar\Collectors\HistoryCollector;
18
use BlitzPHP\Formatter\JsonFormatter;
19
use BlitzPHP\Formatter\XmlFormatter;
20
use BlitzPHP\Http\Request;
21
use BlitzPHP\Http\Response;
22
use BlitzPHP\Utilities\Date;
23
use BlitzPHP\View\Parser;
24
use Exception;
25
use GuzzleHttp\Psr7\Utils;
26
use Kint\Kint;
27
use Psr\Http\Message\ResponseInterface;
28
use Psr\Http\Message\ServerRequestInterface;
29
use stdClass;
30
31
/**
32
 * Affiche une barre d'outils avec des bits de statistiques pour aider un développeur dans le débogage.
33
 *
34
 * Inspiration: http://prophiler.fabfuel.de
35
 *
36
 * @credit	<a href="https://codeigniter.com">CodeIgniter 4.2 - CodeIgniter\Debug\Toolbar</a>
37
 */
38
class Toolbar
39
{
40
    /**
41
     * Paramètres de configuration de la barre d'outils.
42
     *
43
     * @var stdClass
44
     */
45
    protected $config;
46
47
    /**
48
     * Collecteurs à utiliser et à exposer.
49
     *
50
     * @var list<BaseCollector>
0 ignored issues
show
Bug introduced by
The type BlitzPHP\Debug\list was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
51
     */
52
    protected $collectors = [];
53
54
    /**
55
     * Dossier de sauvegarde des information de debogage
56
     */
57
    private string $debugPath = FRAMEWORK_STORAGE_PATH . 'debugbar';
58
59
    /**
60
     * Constructeur
61
     */
62
    public function __construct(?stdClass $config = null)
63
    {
64
        $this->config = $config ?? (object) config('toolbar');
65
66
        foreach ($this->config->collectors as $collector) {
67
            if (! class_exists($collector)) {
68
                logger()->critical(
69
                    'Le collecteur de la barre d\'outils n\'existe pas (' . $collector . ').'
70
                    . ' Veuillez vérifier $collectors dans le fichier app/Config/toolbar.php.'
71
                );
72
73
                continue;
74
            }
75
76
            $this->collectors[] = new $collector();
77
        }
78
    }
79
80
    /**
81
     * Renvoie toutes les données requises par la barre de débogage
82
     *
83
     * @param float   $startTime Heure de début de l'application
84
     * @param Request $request
85
     *
86
     * @return string Données encodées en JSON
87
     */
88
    public function run(float $startTime, float $totalTime, ServerRequestInterface $request, ResponseInterface $response): string
89
    {
90
        // Éléments de données utilisés dans la vue.
91
        $data['url']             = current_url();
0 ignored issues
show
Comprehensibility Best Practice introduced by
$data was never initialized. Although not strictly required by PHP, it is generally a good practice to add $data = array(); before regardless.
Loading history...
92
        $data['method']          = $request->getMethod();
93
        $data['isAJAX']          = $request->ajax();
0 ignored issues
show
Bug introduced by
The method ajax() does not exist on Psr\Http\Message\ServerRequestInterface. It seems like you code against a sub-type of Psr\Http\Message\ServerRequestInterface such as BlitzPHP\Http\ServerRequest. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

93
        /** @scrutinizer ignore-call */ 
94
        $data['isAJAX']          = $request->ajax();
Loading history...
94
        $data['startTime']       = $startTime;
95
        $data['totalTime']       = $totalTime * 1000;
96
        $data['totalMemory']     = number_format(memory_get_peak_usage() / 1024 / 1024, 3);
97
        $data['segmentDuration'] = $this->roundTo($data['totalTime'] / 7);
98
        $data['segmentCount']    = (int) ceil($data['totalTime'] / $data['segmentDuration']);
99
        $data['blitzVersion']    = Application::VERSION;
100
        $data['collectors']      = [];
101
102
        foreach ($this->collectors as $collector) {
103
            $data['collectors'][] = $collector->getAsArray();
104
        }
105
106
        foreach ($this->collectVarData() as $heading => $items) {
107
            $varData = [];
108
109
            if (is_array($items)) {
110
                foreach ($items as $key => $value) {
111
                    if (is_string($value)) {
112
                        $varData[esc($key)] = esc($value);
113
                    } else {
114
                        $oldKintMode       = Kint::$mode_default;
115
                        $oldKintCalledFrom = Kint::$display_called_from;
116
117
                        Kint::$mode_default        = Kint::MODE_RICH;
118
                        Kint::$display_called_from = false;
119
120
                        $kint = @Kint::dump($value);
121
                        $kint = substr($kint, strpos($kint, '</style>') + 8);
122
123
                        Kint::$mode_default        = $oldKintMode;
124
                        Kint::$display_called_from = $oldKintCalledFrom;
125
126
                        $varData[esc($key)] = $kint;
127
                    }
128
                }
129
            }
130
131
            $data['vars']['varData'][esc($heading)] = $varData;
132
        }
133
134
        if ($_SESSION !== []) {
135
            foreach ($_SESSION as $key => $value) {
136
                // Remplacez les données binaires par une chaîne pour éviter l'échec de json_encode.
137
                if (is_string($value) && preg_match('~[^\x20-\x7E\t\r\n]~', $value)) {
138
                    $value = 'donnée binaire';
139
                }
140
141
                $data['vars']['session'][esc($key)] = is_string($value) ? esc($value) : '<pre>' . esc(print_r($value, true)) . '</pre>';
0 ignored issues
show
Bug introduced by
Are you sure esc(print_r($value, true)) of type array|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

141
                $data['vars']['session'][esc($key)] = is_string($value) ? esc($value) : '<pre>' . /** @scrutinizer ignore-type */ esc(print_r($value, true)) . '</pre>';
Loading history...
Bug introduced by
It seems like print_r($value, true) can also be of type true; however, parameter $data of esc() does only seem to accept array|string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

141
                $data['vars']['session'][esc($key)] = is_string($value) ? esc($value) : '<pre>' . esc(/** @scrutinizer ignore-type */ print_r($value, true)) . '</pre>';
Loading history...
142
            }
143
        }
144
145
        foreach ($request->getQueryParams() as $name => $value) {
146
            $data['vars']['get'][esc($name)] = is_array($value) ? '<pre>' . esc(print_r($value, true)) . '</pre>' : esc($value);
147
        }
148
149
        foreach ($request->getParsedBody() as $name => $value) {
150
            $data['vars']['post'][esc($name)] = is_array($value) ? '<pre>' . esc(print_r($value, true)) . '</pre>' : esc($value);
151
        }
152
153
        foreach ($request->getHeaders() as $header => $value) {
154
            if (empty($value)) {
155
                continue;
156
            }
157
            $data['vars']['headers'][esc($header)] = esc($request->getHeaderLine($header));
158
        }
159
160
        foreach ($request->getCookieParams() as $name => $value) {
161
            $data['vars']['cookies'][esc($name)] = esc($value);
162
        }
163
164
        $data['vars']['request'] = ($request->is('ssl') ? 'HTTPS' : 'HTTP') . '/' . $request->getProtocolVersion();
0 ignored issues
show
Bug introduced by
The method is() does not exist on Psr\Http\Message\ServerRequestInterface. It seems like you code against a sub-type of Psr\Http\Message\ServerRequestInterface such as BlitzPHP\Http\ServerRequest. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

164
        $data['vars']['request'] = ($request->/** @scrutinizer ignore-call */ is('ssl') ? 'HTTPS' : 'HTTP') . '/' . $request->getProtocolVersion();
Loading history...
165
166
        $data['vars']['response'] = [
167
            'statusCode'  => $response->getStatusCode(),
168
            'reason'      => esc($response->getReasonPhrase()),
169
            'contentType' => esc($response->getHeaderLine('Content-Type')),
170
            'headers'     => [],
171
        ];
172
173
        foreach ($response->getHeaders() as $header => $value) {
174
            if (empty($value)) {
175
                continue;
176
            }
177
            $data['vars']['response']['headers'][esc($header)] = esc($response->getHeaderLine($header));
178
        }
179
180
        $data['config'] = Config::display();
181
182
        return json_encode($data);
183
    }
184
185
    /**
186
     * Appelé dans la vue pour afficher la chronologie elle-même.
187
     */
188
    protected function renderTimeline(array $collectors, float $startTime, int $segmentCount, int $segmentDuration, array &$styles): string
189
    {
190
        $rows       = $this->collectTimelineData($collectors);
191
        $styleCount = 0;
192
193
        // Utiliser la fonction de rendu récursif
194
        return $this->renderTimelineRecursive($rows, $startTime, $segmentCount, $segmentDuration, $styles, $styleCount);
195
    }
196
197
    /**
198
     * Restitue de manière récursive les éléments de la chronologie et leurs enfants.
199
     */
200
    protected function renderTimelineRecursive(array $rows, float $startTime, int $segmentCount, int $segmentDuration, array &$styles, int &$styleCount, int $level = 0, bool $isChild = false): string
201
    {
202
        $displayTime = $segmentCount * $segmentDuration;
203
204
        $output = '';
205
206
        foreach ($rows as $row) {
207
            $hasChildren = isset($row['children']) && ! empty($row['children']);
208
            $isQuery     = isset($row['query']) && ! empty($row['query']);
209
210
            // Ouvrir la chronologie du contrôleur par défaut
211
            $open = $row['name'] === 'Controller';
212
213
            if ($hasChildren || $isQuery) {
214
                $output .= '<tr class="timeline-parent' . ($open ? ' timeline-parent-open' : '') . '" id="timeline-' . $styleCount . '_parent" data-toggle="childrows" data-child="timeline-' . $styleCount . '">';
215
            } else {
216
                $output .= '<tr>';
217
            }
218
219
            $output .= '<td class="' . ($isChild ? 'debug-bar-width30' : '') . ' debug-bar-level-' . $level . '" >' . ($hasChildren || $isQuery ? '<nav></nav>' : '') . $row['name'] . '</td>';
220
            $output .= '<td class="' . ($isChild ? 'debug-bar-width10' : '') . '">' . $row['component'] . '</td>';
221
            $output .= '<td class="' . ($isChild ? 'debug-bar-width10 ' : '') . 'debug-bar-alignRight">' . number_format($row['duration'] * 1000, 2) . ' ms</td>';
222
            $output .= "<td class='debug-bar-noverflow' colspan='{$segmentCount}'>";
223
224
            $offset = ((((float) $row['start'] - $startTime) * 1000) / $displayTime) * 100;
225
            $length = (((float) $row['duration'] * 1000) / $displayTime) * 100;
226
227
            $styles['debug-bar-timeline-' . $styleCount] = "left: {$offset}%; width: {$length}%;";
228
229
            $output .= "<span class='timer debug-bar-timeline-{$styleCount}' title='" . number_format($length, 2) . "%'></span>";
230
            $output .= '</td>';
231
            $output .= '</tr>';
232
233
            $styleCount++;
234
235
            // Ajouter des enfants le cas échéant
236
            if ($hasChildren || $isQuery) {
237
                $output .= '<tr class="child-row ' . ($open ? '' : ' debug-bar-ndisplay') . '" id="timeline-' . ($styleCount - 1) . '_children" >';
238
                $output .= '<td colspan="' . ($segmentCount + 3) . '" class="child-container">';
239
                $output .= '<table class="timeline">';
240
                $output .= '<tbody>';
241
242
                if ($isQuery) {
243
                    // Sortie de la chaîne de requête si requête
244
                    $output .= '<tr>';
245
                    $output .= '<td class="query-container debug-bar-level-' . ($level + 1) . '" >' . $row['query'] . '</td>';
246
                    $output .= '</tr>';
247
                } else {
248
                    // Rendre récursivement les enfants
249
                    $output .= $this->renderTimelineRecursive($row['children'], $startTime, $segmentCount, $segmentDuration, $styles, $styleCount, $level + 1, true);
250
                }
251
252
                $output .= '</tbody>';
253
                $output .= '</table>';
254
                $output .= '</td>';
255
                $output .= '</tr>';
256
            }
257
        }
258
259
        return $output;
260
    }
261
262
    /**
263
     * Renvoie un tableau trié de tableaux de données chronologiques à partir des collecteurs.
264
     *
265
     * @param mixed $collectors
266
     */
267
    protected function collectTimelineData($collectors): array
268
    {
269
        $data = [];
270
271
        // Le collecter
272
        foreach ($collectors as $collector) {
273
            if (! $collector['hasTimelineData']) {
274
                continue;
275
            }
276
277
            $data = array_merge($data, $collector['timelineData']);
278
        }
279
280
        // Le trier
281
        $sortArray = [
282
            array_column($data, 'start'), SORT_NUMERIC, SORT_ASC,
283
            array_column($data, 'duration'), SORT_NUMERIC, SORT_DESC,
284
            &$data,
285
        ];
286
287
        array_multisort(...$sortArray);
0 ignored issues
show
Bug introduced by
It seems like $sortArray can also be of type integer; however, parameter $array of array_multisort() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

287
        array_multisort(/** @scrutinizer ignore-type */ ...$sortArray);
Loading history...
288
289
        // Ajouter une heure de fin à chaque élément
290
        array_walk($data, static function (&$row): void {
291
            $row['end'] = $row['start'] + $row['duration'];
292
        });
293
294
        // Le grouper
295
        $data = $this->structureTimelineData($data);
296
297
        return $data;
298
    }
299
300
    /**
301
     * Organise les données de chronologie déjà triées dans une structure parent => enfant.
302
     */
303
    protected function structureTimelineData(array $elements): array
304
    {
305
        // Nous nous définissons comme le premier élément du tableau
306
        $element = array_shift($elements);
307
308
        // Si nous avons des enfants derrière nous, récupérez-les et attachez-les-nous
309
        while ($elements !== [] && $elements[array_key_first($elements)]['end'] <= $element['end']) {
310
            $element['children'][] = array_shift($elements);
311
        }
312
313
        // Assurez-vous que nos enfants sachent s'ils ont eux aussi des enfants
314
        if (isset($element['children'])) {
315
            $element['children'] = $this->structureTimelineData($element['children']);
316
        }
317
318
        // Si nous n'avons pas de frères et sœurs plus jeunes, nous pouvons revenir
319
        if ($elements === []) {
320
            return [$element];
321
        }
322
323
        // Assurez-vous que nos jeunes frères et sœurs connaissent également leurs proches
324
        return array_merge([$element], $this->structureTimelineData($elements));
325
    }
326
327
    /**
328
     * Renvoie un tableau de données de tous les modules
329
     * qui devrait être affiché dans l'onglet 'Vars'.
330
     */
331
    protected function collectVarData(): array
332
    {
333
        if (! ($this->config->collectVarData ?? true)) {
334
            return [];
335
        }
336
337
        $data = [];
338
339
        foreach ($this->collectors as $collector) {
340
            if (! $collector->hasVarData()) {
341
                continue;
342
            }
343
344
            $data = array_merge($data, $collector->getVarData());
345
        }
346
347
        return $data;
348
    }
349
350
    /**
351
     * Arrondit un nombre à la valeur incrémentielle la plus proche.
352
     */
353
    protected function roundTo(float $number, int $increments = 5): float
354
    {
355
        $increments = 1 / $increments;
356
357
        return ceil($number * $increments) / $increments;
358
    }
359
360
    /**
361
     * Traite la barre d'outils de débogage pour la requête en cours.
362
     *
363
     * Cette méthode détermine s'il faut afficher la barre d'outils de débogage ou la préparer pour une utilisation ultérieure.
364
     *
365
     * @param array             $stats    Un tableau contenant des statistiques de performances.
366
     * @param Request           $request  La requête serveur en cours.
367
     * @param ResponseInterface $response La réponse en cours.
368
     *
369
     * @return ResponseInterface La réponse traitée, avec la barre d'outils de débogage injectée ou préparée pour une utilisation ultérieure.
370
     */
371
    public function process(array $stats, ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
372
    {
373
        if ($request->hasAny('blitzphp-debugbar', 'debugbar_time')) {
0 ignored issues
show
Bug introduced by
The method hasAny() does not exist on Psr\Http\Message\ServerRequestInterface. It seems like you code against a sub-type of Psr\Http\Message\ServerRequestInterface such as BlitzPHP\Http\ServerRequest. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

373
        if ($request->/** @scrutinizer ignore-call */ hasAny('blitzphp-debugbar', 'debugbar_time')) {
Loading history...
374
            return $this->respond($request);
375
        }
376
377
        return $this->prepare($stats, $request, $response);
378
    }
379
380
    /**
381
     * Préparez-vous au débogage..
382
     */
383
    public function prepare(array $stats, ?ServerRequestInterface $request = null, ?ResponseInterface $response = null): ResponseInterface
384
    {
385
        /** @var Request $request */
386
        $request ??= service('request');
387
        /** @var Response $response */
388
        $response ??= service('response');
389
390
        // Si on est en CLI ou en prod, pas la peine de continuer car la debugbar n'est pas utilisable dans ces environnements
391
        if (is_cli() || on_prod()) {
392
            return $response;
393
        }
394
395
        // Si on a desactiver le debogage ou l'affichage de la debugbar, on s'arrete
396
        if (! BLITZ_DEBUG || ! $this->config->show_debugbar) {
397
            return $response;
398
        }
399
400
        $toolbar = service('toolbar', $this->config);
401
        $data    = $toolbar->run(
402
            $stats['startTime'],
403
            $stats['totalTime'],
404
            $request,
405
            $response
406
        );
407
408
        // Mise à jour vers microtime() pour que nous puissions obtenir l'historique
409
        $time = sprintf('%.6f', Date::now()->format('U.u'));
410
411
        if (! is_dir($this->debugPath)) {
412
            mkdir($this->debugPath, 0o777);
413
        }
414
415
        $this->writeFile($this->debugPath . '/debugbar_' . $time . '.json', $data, 'w+');
416
417
        $format = $response->getHeaderLine('Content-Type');
418
419
        // Les formats non HTML ne doivent pas inclure la barre de débogage,
420
        // puis nous envoyons des en-têtes indiquant où trouver les données de débogage pour cette réponse
421
        if ($request->ajax() || ! str_contains($format, 'html')) {
422
            return $response
0 ignored issues
show
Bug Best Practice introduced by
The expression return $response->withHe...debugbar_time='.$time)) returns the type Psr\Http\Message\MessageInterface which includes types incompatible with the type-hinted return Psr\Http\Message\ResponseInterface.
Loading history...
423
                ->withHeader('Debugbar-Time', "{$time}")
424
                ->withHeader('Debugbar-Link', site_url("?debugbar_time={$time}"));
425
        }
426
427
        $oldKintMode        = Kint::$mode_default;
428
        Kint::$mode_default = Kint::MODE_RICH;
429
        $kintScript         = @Kint::dump('');
430
        Kint::$mode_default = $oldKintMode;
431
        $kintScript         = substr($kintScript, 0, strpos($kintScript, '</style>') + 8);
432
        $kintScript         = ($kintScript === '0') ? '' : $kintScript;
433
434
        $script = PHP_EOL
435
            . '<script id="debugbar_loader" '
436
            . 'data-time="' . $time . '" '
437
            . 'src="' . site_url() . '?blitzphp-debugbar"></script>'
438
            . '<script id="debugbar_dynamic_script"></script>'
439
            . '<style id="debugbar_dynamic_style"></style>'
440
            . $kintScript
441
            . PHP_EOL;
442
443
        if (str_contains($responseContent = (string) $response->getBody(), '<head>')) {
444
            $responseContent = preg_replace(
445
                '/<head>/',
446
                '<head>' . $script,
447
                $responseContent,
448
                1,
449
            );
450
        } else {
451
            $responseContent .= $script;
452
        }
453
454
        return $response->withBody(Utils::streamFor($responseContent));
0 ignored issues
show
Bug Best Practice introduced by
The expression return $response->withBo...mFor($responseContent)) returns the type Psr\Http\Message\MessageInterface which includes types incompatible with the type-hinted return Psr\Http\Message\ResponseInterface.
Loading history...
455
    }
456
457
    /**
458
     * Injectez la barre d'outils de débogage dans la réponse.
459
     *
460
     * @param Request $request
461
     *
462
     * @codeCoverageIgnore
463
     */
464
    public function respond(ServerRequestInterface $request): Response
465
    {
466
        $response = new Response();
467
468
        if (on_test()) {
469
            return $response;
470
        }
471
472
        // Si la requête contient '?blitzphp-debugbar alors nous sommes
473
        // renvoie simplement le script de chargement
474
        if ($request->getQuery('blitzphp-debugbar') !== null) {
0 ignored issues
show
Bug introduced by
The method getQuery() does not exist on Psr\Http\Message\ServerRequestInterface. Did you maybe mean getQueryParams()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

474
        if ($request->/** @scrutinizer ignore-call */ getQuery('blitzphp-debugbar') !== null) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
475
            $response = $response->withType('application/javascript');
476
477
            ob_start();
478
            include $this->config->view_path . DS . 'toolbarloader.js';
479
            $output = ob_get_clean();
480
481
            return $response->withStringBody(str_replace('{url}', rtrim(site_url(), '/'), $output));
482
        }
483
484
        // Sinon, s'il inclut ?debugbar_time, alors
485
        // nous devrions retourner la barre de débogage entière.
486
        if (null !== $debugbarTime = $request->getQuery('debugbar_time')) {
487
            // Négociation du type de contenu pour formater la sortie
488
            $format   = $request->negotiate('media', ['text/html', 'application/json', 'application/xml']);
0 ignored issues
show
Bug introduced by
The method negotiate() does not exist on Psr\Http\Message\ServerRequestInterface. It seems like you code against a sub-type of Psr\Http\Message\ServerRequestInterface such as BlitzPHP\Http\ServerRequest. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

488
            /** @scrutinizer ignore-call */ 
489
            $format   = $request->negotiate('media', ['text/html', 'application/json', 'application/xml']);
Loading history...
489
            $response = $response->withType($format);
490
            $format   = explode('/', $format)[1];
491
492
            $filename = 'debugbar_' . $debugbarTime;
493
            $filename = $this->debugPath . DS . $filename . '.json';
494
495
            if (is_file($filename)) {
496
                // Affiche la barre d'outils si elle existe
497
                return $response->withStringBody($this->format($debugbarTime, file_get_contents($filename), $format));
498
            }
499
        }
500
501
        // Nom de fichier introuvable
502
        return $response->withStatus(404);
503
    }
504
505
    /**
506
     * Formatte la sortie
507
     *
508
     * @param mixed $debugbar_time
509
     */
510
    protected function format($debugbar_time, string $data, string $format = 'html'): string
511
    {
512
        $data = json_decode($data, true);
513
514
        if ($this->config->max_history !== 0 && preg_match('/\d+\.\d{6}/s', (string) $debugbar_time, $debugbarTime)) {
515
            $history = new HistoryCollector();
516
            $history->setFiles(
517
                $debugbarTime[0],
518
                $this->config->max_history
519
            );
520
521
            $data['collectors'][] = $history->getAsArray();
522
        }
523
        $output = '';
524
525
        switch ($format) {
526
            case 'html':
527
                $data['styles'] = [];
528
                extract($data);
529
                $parser = new Parser([], $this->config->view_path);
530
                ob_start();
531
                include rtrim($this->config->view_path, '/\\') . DS . 'toolbar.tpl.php';
532
                $output = ob_get_clean();
533
                break;
534
535
            case 'json':
536
                $formatter = new JsonFormatter();
537
                $output    = $formatter->format($data);
538
                break;
539
540
            case 'xml':
541
                $formatter = new XmlFormatter();
542
                $output    = $formatter->format($data);
543
                break;
544
        }
545
546
        return $output;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $output could return the type false which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
547
    }
548
549
    /**
550
     * Écrit des données dans le fichier spécifié dans le chemin.
551
     * Crée un nouveau fichier s'il n'existe pas.
552
     */
553
    protected function writeFile(string $path, string $data, string $mode = 'wb'): bool
554
    {
555
        try {
556
            $fp = fopen($path, $mode);
557
558
            flock($fp, LOCK_EX);
559
560
            for ($result = $written = 0, $length = strlen($data); $written < $length; $written += $result) {
561
                if (($result = fwrite($fp, substr($data, $written))) === false) {
562
                    break;
563
                }
564
            }
565
566
            flock($fp, LOCK_UN);
567
            fclose($fp);
568
569
            return is_int($result);
570
        } catch (Exception) {
571
            return false;
572
        }
573
    }
574
}
575