Toolbar::writeFile()   A
last analyzed

Complexity

Conditions 4
Paths 13

Size

Total Lines 19
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
cc 4
eloc 11
c 0
b 0
f 0
nc 13
nop 3
dl 0
loc 19
ccs 0
cts 8
cp 0
crap 20
rs 9.9
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