mirror of
https://github.com/torrentpier/torrentpier
synced 2025-08-22 14:23:57 -07:00
feat(database): add visual markers for Nette Explorer queries in debug panel
Add automatic detection and colorful badges to distinguish Nette Explorer queries from legacy database calls, helping track modernization progress. - Detect Nette SQL patterns (backticks, parentheses) - Add green styled [Nette Explorer] badges - Fix HTML escaping in debug tooltips - Prevent marker duplication
This commit is contained in:
parent
6c0219d53c
commit
91cc6fe0a9
3 changed files with 103 additions and 1 deletions
|
@ -34,6 +34,9 @@ class DatabaseDebugger
|
||||||
public string $explain_hold = '';
|
public string $explain_hold = '';
|
||||||
public string $explain_out = '';
|
public string $explain_out = '';
|
||||||
|
|
||||||
|
// Nette Explorer tracking
|
||||||
|
public bool $is_nette_explorer_query = false;
|
||||||
|
|
||||||
public function __construct(Database $db)
|
public function __construct(Database $db)
|
||||||
{
|
{
|
||||||
$this->db = $db;
|
$this->db = $db;
|
||||||
|
@ -70,6 +73,12 @@ class DatabaseDebugger
|
||||||
|
|
||||||
if ($this->dbg_enabled) {
|
if ($this->dbg_enabled) {
|
||||||
$dbg['sql'] = preg_replace('#^(\s*)(/\*)(.*)(\*/)(\s*)#', '', $this->db->cur_query);
|
$dbg['sql'] = preg_replace('#^(\s*)(/\*)(.*)(\*/)(\s*)#', '', $this->db->cur_query);
|
||||||
|
|
||||||
|
// Also check SQL syntax to detect Nette Explorer queries
|
||||||
|
if (!$this->is_nette_explorer_query && $this->detectNetteExplorerBySqlSyntax($dbg['sql'])) {
|
||||||
|
$this->markAsNetteExplorerQuery();
|
||||||
|
}
|
||||||
|
|
||||||
$dbg['src'] = $this->debug_find_source();
|
$dbg['src'] = $this->debug_find_source();
|
||||||
$dbg['file'] = $this->debug_find_source('file');
|
$dbg['file'] = $this->debug_find_source('file');
|
||||||
$dbg['line'] = $this->debug_find_source('line');
|
$dbg['line'] = $this->debug_find_source('line');
|
||||||
|
@ -96,6 +105,18 @@ class DatabaseDebugger
|
||||||
$dbg['time'] = $this->cur_query_time > 0 ? $this->cur_query_time : (microtime(true) - $this->sql_starttime);
|
$dbg['time'] = $this->cur_query_time > 0 ? $this->cur_query_time : (microtime(true) - $this->sql_starttime);
|
||||||
$dbg['info'] = $this->db->query_info();
|
$dbg['info'] = $this->db->query_info();
|
||||||
$dbg['mem_after'] = function_exists('sys') ? sys('mem') : 0;
|
$dbg['mem_after'] = function_exists('sys') ? sys('mem') : 0;
|
||||||
|
|
||||||
|
// Add Nette Explorer marker to debug info for panel display
|
||||||
|
if ($this->is_nette_explorer_query && !str_contains($dbg['info'], '[Nette Explorer]')) {
|
||||||
|
// Store both plain text and HTML versions
|
||||||
|
$dbg['info_plain'] = $dbg['info'] . ' [Nette Explorer]';
|
||||||
|
$dbg['info'] .= ' <span style="color: #28a745; font-weight: bold; background: #d4edda; padding: 2px 6px; border-radius: 3px; font-size: 11px;">[Nette Explorer]</span>';
|
||||||
|
$dbg['is_nette_explorer'] = true;
|
||||||
|
} else {
|
||||||
|
$dbg['info_plain'] = $dbg['info'];
|
||||||
|
$dbg['is_nette_explorer'] = false;
|
||||||
|
}
|
||||||
|
|
||||||
$id++;
|
$id++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,6 +129,9 @@ class DatabaseDebugger
|
||||||
$this->log_query($this->db->DBS['log_file']);
|
$this->log_query($this->db->DBS['log_file']);
|
||||||
$this->db->DBS['log_counter']--;
|
$this->db->DBS['log_counter']--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reset Nette Explorer flag after query completion
|
||||||
|
$this->resetNetteExplorerFlag();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update timing in main Database object
|
// Update timing in main Database object
|
||||||
|
@ -125,6 +149,12 @@ class DatabaseDebugger
|
||||||
|
|
||||||
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
|
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
|
||||||
|
|
||||||
|
// Check if this is a Nette Explorer query by examining the call stack
|
||||||
|
$isNetteExplorer = $this->detectNetteExplorerInTrace($trace);
|
||||||
|
if ($isNetteExplorer) {
|
||||||
|
$this->markAsNetteExplorerQuery();
|
||||||
|
}
|
||||||
|
|
||||||
// Find first non-DB call (skip Database.php, DebugSelection.php, and DatabaseDebugger.php)
|
// Find first non-DB call (skip Database.php, DebugSelection.php, and DatabaseDebugger.php)
|
||||||
foreach ($trace as $frame) {
|
foreach ($trace as $frame) {
|
||||||
if (isset($frame['file']) &&
|
if (isset($frame['file']) &&
|
||||||
|
@ -148,6 +178,57 @@ class DatabaseDebugger
|
||||||
return 'src not found';
|
return 'src not found';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detect if the current query comes from Nette Explorer by examining the call stack
|
||||||
|
*/
|
||||||
|
public function detectNetteExplorerInTrace(array $trace): bool
|
||||||
|
{
|
||||||
|
foreach ($trace as $frame) {
|
||||||
|
if (isset($frame['class'])) {
|
||||||
|
// Check for Nette Database classes in the call stack
|
||||||
|
if (str_contains($frame['class'], 'Nette\\Database\\') ||
|
||||||
|
str_contains($frame['class'], 'TorrentPier\\Database\\DebugSelection')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($frame['file'])) {
|
||||||
|
// Check for Nette Database files or our DebugSelection
|
||||||
|
if (str_contains($frame['file'], 'vendor/nette/database/') ||
|
||||||
|
str_contains($frame['file'], 'Database/DebugSelection.php')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detect if SQL query syntax suggests it came from Nette Explorer
|
||||||
|
*/
|
||||||
|
public function detectNetteExplorerBySqlSyntax(string $sql): bool
|
||||||
|
{
|
||||||
|
// Nette Database typically generates SQL with these characteristics:
|
||||||
|
// 1. Backticks around column/table names
|
||||||
|
// 2. Parentheses around WHERE conditions like (column = value)
|
||||||
|
// 3. Specific patterns like IN (value) instead of IN (value)
|
||||||
|
|
||||||
|
$nettePatterns = [
|
||||||
|
'/`[a-zA-Z0-9_]+`/', // Backticks around identifiers
|
||||||
|
'/WHERE\s*\([^)]+\)/', // Parentheses around WHERE conditions
|
||||||
|
'/SELECT\s+`[^`]+`.*FROM\s+`[^`]+`/', // SELECT with backticked columns and tables
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($nettePatterns as $pattern) {
|
||||||
|
if (preg_match($pattern, $sql)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prepare for logging
|
* Prepare for logging
|
||||||
*/
|
*/
|
||||||
|
@ -346,4 +427,20 @@ class DatabaseDebugger
|
||||||
$this->explain_hold = '';
|
$this->explain_hold = '';
|
||||||
$this->explain_out = '';
|
$this->explain_out = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mark next query as coming from Nette Explorer
|
||||||
|
*/
|
||||||
|
public function markAsNetteExplorerQuery(): void
|
||||||
|
{
|
||||||
|
$this->is_nette_explorer_query = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset Nette Explorer query flag
|
||||||
|
*/
|
||||||
|
public function resetNetteExplorerFlag(): void
|
||||||
|
{
|
||||||
|
$this->is_nette_explorer_query = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,6 +78,9 @@ class DebugSelection
|
||||||
// Use the actual SQL with substituted parameters for both logging and EXPLAIN
|
// Use the actual SQL with substituted parameters for both logging and EXPLAIN
|
||||||
$sql = $this->generateSqlForLogging($method, $arguments, false);
|
$sql = $this->generateSqlForLogging($method, $arguments, false);
|
||||||
|
|
||||||
|
// Mark this query as coming from Nette Explorer
|
||||||
|
$this->db->debugger->markAsNetteExplorerQuery();
|
||||||
|
|
||||||
// Set the query for debug logging
|
// Set the query for debug logging
|
||||||
$this->db->cur_query = $sql;
|
$this->db->cur_query = $sql;
|
||||||
$this->db->debug('start');
|
$this->db->debug('start');
|
||||||
|
|
|
@ -258,9 +258,11 @@ class Dev
|
||||||
$sql = $this->shortQueryInstance($dbg['sql'], true);
|
$sql = $this->shortQueryInstance($dbg['sql'], true);
|
||||||
$time = sprintf('%.3f', $dbg['time']);
|
$time = sprintf('%.3f', $dbg['time']);
|
||||||
$perc = '[' . round($dbg['time'] * 100 / $db_obj->sql_timetotal) . '%]';
|
$perc = '[' . round($dbg['time'] * 100 / $db_obj->sql_timetotal) . '%]';
|
||||||
|
// Use plain text version for title attribute to avoid HTML issues
|
||||||
|
$info_plain = !empty($dbg['info_plain']) ? $dbg['info_plain'] . ' [' . $dbg['src'] . ']' : $dbg['src'];
|
||||||
$info = !empty($dbg['info']) ? $dbg['info'] . ' [' . $dbg['src'] . ']' : $dbg['src'];
|
$info = !empty($dbg['info']) ? $dbg['info'] . ' [' . $dbg['src'] . ']' : $dbg['src'];
|
||||||
|
|
||||||
$log .= '<div onclick="$(this).toggleClass(\'sqlHighlight\');" class="sqlLogRow" title="' . $info . '">'
|
$log .= '<div onclick="$(this).toggleClass(\'sqlHighlight\');" class="sqlLogRow" title="' . htmlspecialchars($info_plain) . '">'
|
||||||
. '<span style="letter-spacing: -1px;">' . $time . ' </span>'
|
. '<span style="letter-spacing: -1px;">' . $time . ' </span>'
|
||||||
. '<span class="copyElement" data-clipboard-target="#' . $id . '" title="Copy to clipboard" style="color: rgb(128,128,128); letter-spacing: -1px;">' . $perc . '</span> '
|
. '<span class="copyElement" data-clipboard-target="#' . $id . '" title="Copy to clipboard" style="color: rgb(128,128,128); letter-spacing: -1px;">' . $perc . '</span> '
|
||||||
. '<span style="letter-spacing: 0;" id="' . $id . '">' . $sql . '</span>'
|
. '<span style="letter-spacing: 0;" id="' . $id . '">' . $sql . '</span>'
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue