Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions spec/support/application/app/Config/toolbar.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@
*/

return [
'collectors' => [],
'collect_var_data' => true,
'max_history' => 20,
'view_path' => SYST_PATH . 'Debug' . DS . 'Toolbar' . DS . 'Views',
'max_queries' => 100,
'show_debugbar' => true,
'collectors' => [],
'collect_var_data' => true,
'max_history' => 20,
'view_path' => SYST_PATH . 'Debug' . DS . 'Toolbar' . DS . 'Views',
'max_queries' => 100,
'show_debugbar' => true,
'watched_directories' => ['app'],
'watched_extensions' => ['php', 'css', 'js', 'html', 'svg', 'json', 'env'],
];
55 changes: 55 additions & 0 deletions spec/system/framework/HotReloader/DirectoryHasher.spec.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php

/**
* This file is part of Blitz PHP framework.
*
* (c) 2022 Dimitri Sitchet Tomkeu <[email protected]>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/

use BlitzPHP\Exceptions\FrameworkException;
use BlitzPHP\HotReloader\DirectoryHasher;

use function Kahlan\expect;

describe('HotReloader / DirectoryHasher', function (): void {
beforeAll(function (): void {
$this->hasher = new DirectoryHasher();
});

it('hashApp', function (): void {
$results = $this->hasher->hashApp();

expect($results)->toBeA('array');
expect($results)->toContainKey('app');
});

it('Leve une exception si on essai de hasher un dossier invalide', function (): void {
$path = $path = APP_PATH . 'Foo';

expect(fn() => $this->hasher->hashDirectory($path))
->toThrow(FrameworkException::invalidDirectory($path));
});

it('Chaque dossier a un hash unique', function (): void {
$hash1 = $this->hasher->hashDirectory(APP_PATH);
$hash2 = $this->hasher->hashDirectory(SYST_PATH);

expect($hash1)->not->toBe($hash2);
});

it('Un meme dossier produira le meme hash', function (): void {
$hash1 = $this->hasher->hashDirectory(APP_PATH);
$hash2 = $this->hasher->hashDirectory(APP_PATH);

expect($hash1)->toBe($hash2);
});

it ('hash', function (): void {
$expected = md5(implode('', $this->hasher->hashApp()));

expect($expected)->toBe($this->hasher->hash());
});
});
141 changes: 78 additions & 63 deletions src/Debug/Toolbar.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@
use BlitzPHP\Formatter\JsonFormatter;
use BlitzPHP\Formatter\XmlFormatter;
use BlitzPHP\Http\Request;
use BlitzPHP\Http\Response;
use BlitzPHP\Utilities\Date;
use BlitzPHP\View\Parser;
use Exception;
use GuzzleHttp\Psr7\Utils;
use Kint\Kint;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use stdClass;
Expand Down Expand Up @@ -66,8 +66,8 @@ public function __construct(?stdClass $config = null)
foreach ($this->config->collectors as $collector) {
if (! class_exists($collector)) {
logger()->critical(
'Toolbar collector does not exist (' . $collector . ').'
. ' Please check $collectors in the app/Config/toolbar.php file.'
'Le collecteur de la barre d\'outils n\'existe pas (' . $collector . ').'
. ' Veuillez vérifier $collectors dans le fichier app/Config/toolbar.php.'
);

continue;
Expand All @@ -80,16 +80,17 @@ public function __construct(?stdClass $config = null)
/**
* Renvoie toutes les données requises par la barre de débogage
*
* @param float $startTime Heure de début de l'application
* @param float $startTime Heure de début de l'application
* @param Request $request
*
* @return string Données encodées en JSON
*/
public function run(float $startTime, float $totalTime, ServerRequestInterface $request, ResponseInterface $response): string
{
// Éléments de données utilisés dans la vue.
$data['url'] = current_url();
$data['method'] = strtoupper($request->getMethod());
$data['isAJAX'] = service('request')->ajax();
$data['method'] = $request->getMethod();
$data['isAJAX'] = $request->ajax();
$data['startTime'] = $startTime;
$data['totalTime'] = $totalTime * 1000;
$data['totalMemory'] = number_format(memory_get_peak_usage() / 1024 / 1024, 3);
Expand Down Expand Up @@ -134,7 +135,7 @@ public function run(float $startTime, float $totalTime, ServerRequestInterface $
foreach ($_SESSION as $key => $value) {
// Remplacez les données binaires par une chaîne pour éviter l'échec de json_encode.
if (is_string($value) && preg_match('~[^\x20-\x7E\t\r\n]~', $value)) {
$value = 'binary data';
$value = 'donnée binaire';
}

$data['vars']['session'][esc($key)] = is_string($value) ? esc($value) : '<pre>' . esc(print_r($value, true)) . '</pre>';
Expand All @@ -160,7 +161,7 @@ public function run(float $startTime, float $totalTime, ServerRequestInterface $
$data['vars']['cookies'][esc($name)] = esc($value);
}

$data['vars']['request'] = (service('request')->is('ssl') ? 'HTTPS' : 'HTTP') . '/' . $request->getProtocolVersion();
$data['vars']['request'] = ($request->is('ssl') ? 'HTTPS' : 'HTTP') . '/' . $request->getProtocolVersion();

$data['vars']['response'] = [
'statusCode' => $response->getStatusCode(),
Expand Down Expand Up @@ -210,12 +211,12 @@ protected function renderTimelineRecursive(array $rows, float $startTime, int $s
$open = $row['name'] === 'Controller';

if ($hasChildren || $isQuery) {
$output .= '<tr class="timeline-parent' . ($open ? ' timeline-parent-open' : '') . '" id="timeline-' . $styleCount . '_parent" onclick="blitzphpDebugBar.toggleChildRows(\'timeline-' . $styleCount . '\');">';
$output .= '<tr class="timeline-parent' . ($open ? ' timeline-parent-open' : '') . '" id="timeline-' . $styleCount . '_parent" data-toggle="childrows" data-child="timeline-' . $styleCount . '">';
} else {
$output .= '<tr>';
}

$output .= '<td class="' . ($isChild ? 'debug-bar-width30' : '') . '" style="--level: ' . $level . ';">' . ($hasChildren || $isQuery ? '<nav></nav>' : '') . $row['name'] . '</td>';
$output .= '<td class="' . ($isChild ? 'debug-bar-width30' : '') . ' debug-bar-level-' . $level . '" >' . ($hasChildren || $isQuery ? '<nav></nav>' : '') . $row['name'] . '</td>';
$output .= '<td class="' . ($isChild ? 'debug-bar-width10' : '') . '">' . $row['component'] . '</td>';
$output .= '<td class="' . ($isChild ? 'debug-bar-width10 ' : '') . 'debug-bar-alignRight">' . number_format($row['duration'] * 1000, 2) . ' ms</td>';
$output .= "<td class='debug-bar-noverflow' colspan='{$segmentCount}'>";
Expand All @@ -233,15 +234,15 @@ protected function renderTimelineRecursive(array $rows, float $startTime, int $s

// Ajouter des enfants le cas échéant
if ($hasChildren || $isQuery) {
$output .= '<tr class="child-row" id="timeline-' . ($styleCount - 1) . '_children" style="' . ($open ? '' : 'display: none;') . '">';
$output .= '<tr class="child-row ' . ($open ? '' : ' debug-bar-ndisplay') . '" id="timeline-' . ($styleCount - 1) . '_children" >';
$output .= '<td colspan="' . ($segmentCount + 3) . '" class="child-container">';
$output .= '<table class="timeline">';
$output .= '<tbody>';

if ($isQuery) {
// Sortie de la chaîne de requête si requête
$output .= '<tr>';
$output .= '<td class="query-container" style="--level: ' . ($level + 1) . ';">' . $row['query'] . '</td>';
$output .= '<td class="query-container debug-bar-level-' . ($level + 1) . '" >' . $row['query'] . '</td>';
$output .= '</tr>';
} else {
// Rendre récursivement les enfants
Expand Down Expand Up @@ -356,13 +357,34 @@ protected function roundTo(float $number, int $increments = 5): float
return ceil($number * $increments) / $increments;
}

/**
* Traite la barre d'outils de débogage pour la requête en cours.
*
* 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.
*
* @param array $stats Un tableau contenant des statistiques de performances.
* @param Request $request La requête serveur en cours.
* @param ResponseInterface $response La réponse en cours.
*
* @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.
*/
public function process(array $stats, ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
{
if ($request->hasAny('blitzphp-debugbar', 'debugbar_time')) {
return $this->respond($request);
}

return $this->prepare($stats, $request, $response);
}

/**
* Préparez-vous au débogage..
*/
public function prepare(array $stats, ?RequestInterface $request = null, ?ResponseInterface $response = null): ResponseInterface
public function prepare(array $stats, ?ServerRequestInterface $request = null, ?ResponseInterface $response = null): ResponseInterface
{
/** @var Request $request */
$request ??= service('request');
/** @var Response $response */
$response ??= service('response');

// Si on est en CLI ou en prod, pas la peine de continuer car la debugbar n'est pas utilisable dans ces environnements
Expand Down Expand Up @@ -402,95 +424,88 @@ public function prepare(array $stats, ?RequestInterface $request = null, ?Respon
->withHeader('Debugbar-Link', site_url("?debugbar_time={$time}"));
}

$_SESSION['_blitz_debugbar_'] = array_merge($_SESSION['_blitz_debugbar_'] ?? [], compact('time'));

$debugRenderer = $this->respond();

// Extract css
preg_match('/<style (?:.+)>(.+)<\/style>/', $debugRenderer, $matches);
$style = $matches[0] ?? '';
$debugRenderer = str_replace($style, '', $debugRenderer);

// Extract js
preg_match('/<script (?:.+)>(.+)<\/script>/', $debugRenderer, $matches);
$js = $matches[0] ?? '';
$debugRenderer = str_replace($js, '', $debugRenderer);

$responseContent = $response->getBody()->getContents();

if (str_contains($responseContent, '<head>')) {
$responseContent = preg_replace('/<head>/', '<head>' . $style, $responseContent, 1);
} else {
$responseContent .= $style;
}

if (str_contains($responseContent, '</body>')) {
$responseContent = preg_replace('/<\/body>/', '<div id="toolbarContainer">' . trim(preg_replace('/\s+/', ' ', $debugRenderer)) . '</div>' . $js . '<script>blitzphpDebugBar.init();</script></body>', $responseContent, 1);
$oldKintMode = Kint::$mode_default;
Kint::$mode_default = Kint::MODE_RICH;
$kintScript = @Kint::dump('');
Kint::$mode_default = $oldKintMode;
$kintScript = substr($kintScript, 0, strpos($kintScript, '</style>') + 8);
$kintScript = ($kintScript === '0') ? '' : $kintScript;

$script = PHP_EOL
. '<script id="debugbar_loader" '
. 'data-time="' . $time . '" '
. 'src="' . site_url() . '?blitzphp-debugbar"></script>'
. '<script id="debugbar_dynamic_script"></script>'
. '<style id="debugbar_dynamic_style"></style>'
. $kintScript
. PHP_EOL;

if (str_contains($responseContent = (string) $response->getBody(), '<head>')) {
$responseContent = preg_replace(
'/<head>/',
'<head>' . $script,
$responseContent,
1,
);
} else {
$responseContent .= '<div id="toolbarContainer">' . trim(preg_replace('/\s+/', ' ', $debugRenderer)) . '</div>' . $js . '<script>blitzphpDebugBar.init();</script>';
$responseContent .= $script;
}

return $response->withBody(
Utils::streamFor($responseContent)
);
return $response->withBody(Utils::streamFor($responseContent));
}

/**
* Injectez la barre d'outils de débogage dans la réponse.
*
* @return string
* @param Request $request
*
* @codeCoverageIgnore
*/
public function respond()
public function respond(ServerRequestInterface $request): Response
{
$response = new Response();

if (on_test()) {
return '';
return $response;
}

$request = service('request');

// Si la requête contient '?debugbar alors nous sommes
// Si la requête contient '?blitzphp-debugbar alors nous sommes
// renvoie simplement le script de chargement
if ($request->getQuery('debugbar') !== null) {
header('Content-Type: application/javascript');
if ($request->getQuery('blitzphp-debugbar') !== null) {
$response = $response->withType('application/javascript');

ob_start();
include $this->config->view_path . 'toolbarloader.js';
include $this->config->view_path . DS . 'toolbarloader.js';
$output = ob_get_clean();

return str_replace('{url}', rtrim(site_url(), '/'), $output);
return $response->withStringBody(str_replace('{url}', rtrim(site_url(), '/'), $output));
}

// Sinon, s'il inclut ?debugbar_time, alors
// nous devrions retourner la barre de débogage entière.
$debugbarTime = $_SESSION['_blitz_debugbar_']['time'] ?? $request->getQuery('debugbar_time');
if ($debugbarTime) {
if (null !== $debugbarTime = $request->getQuery('debugbar_time')) {
// Négociation du type de contenu pour formater la sortie
$format = $request->negotiate('media', ['text/html', 'application/json', 'application/xml']);
$format = explode('/', $format)[1];
$format = $request->negotiate('media', ['text/html', 'application/json', 'application/xml']);
$response = $response->withType($format);
$format = explode('/', $format)[1];

$filename = 'debugbar_' . $debugbarTime;
$filename = $this->debugPath . DS . $filename . '.json';

if (is_file($filename)) {
// Affiche la barre d'outils si elle existe
return $this->format($debugbarTime, file_get_contents($filename), $format);
return $response->withStringBody($this->format($debugbarTime, file_get_contents($filename), $format));
}

// Nom de fichier introuvable
http_response_code(404);

exit; // Quitter ici est nécessaire pour éviter de charger la page d'index
}

return '';
// Nom de fichier introuvable
return $response->withStatus(404);
}

/**
* Formatte la sortie
*
* @param float $debugbar_time
* @param mixed $debugbar_time
*/
protected function format($debugbar_time, string $data, string $format = 'html'): string
{
Expand Down
23 changes: 21 additions & 2 deletions src/Debug/Toolbar/Collectors/RoutesCollector.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,25 @@ public function __construct()
/**
* {@inheritDoc}
*
* @return array{
* matchedRoute: array<array{
* directory: string,
* controller: string,
* method: string,
* paramCount: int,
* truePCount: int,
* params: list<array{
* name: string,
* value: mixed
* }>
* }>,
* routes: list<array{
* method: string,
* route: string,
* handler: string
* }>
* }
*
* @throws ReflectionException
*/
public function display(): array
Expand Down Expand Up @@ -91,8 +110,8 @@ public function display(): array
$matchedRoute = [
[
'directory' => $this->router->directory(),
'controller' => $this->router->controllerName(),
'method' => $this->router->methodName(),
'controller' => is_string($controller = $this->router->controllerName()) ? $controller : 'Non défini',
'method' => is_string($controller) ? $this->router->methodName() : 'Non définie',
'paramCount' => count($this->router->params()),
'truePCount' => count($params),
'params' => $params,
Expand Down
Loading