1: <?php
2:
3: 4: 5: 6:
7:
8: namespace Nette\Bridges\DatabaseTracy;
9:
10: use Nette;
11: use Nette\Database\Helpers;
12: use Tracy;
13:
14:
15: 16: 17:
18: class ConnectionPanel implements Tracy\IBarPanel
19: {
20: use Nette\SmartObject;
21:
22:
23: public $maxQueries = 100;
24:
25:
26: public $name;
27:
28:
29: public $explain = true;
30:
31:
32: public $disabled = false;
33:
34:
35: private $totalTime = 0;
36:
37:
38: private $count = 0;
39:
40:
41: private $queries = [];
42:
43:
44: public function __construct(Nette\Database\Connection $connection)
45: {
46: $connection->onQuery[] = [$this, 'logQuery'];
47: }
48:
49:
50: public function logQuery(Nette\Database\Connection $connection, $result)
51: {
52: if ($this->disabled) {
53: return;
54: }
55: $this->count++;
56:
57: $source = null;
58: $trace = $result instanceof \PDOException ? $result->getTrace() : debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
59: foreach ($trace as $row) {
60: if (isset($row['file']) && is_file($row['file']) && !Tracy\Debugger::getBluescreen()->isCollapsed($row['file'])) {
61: if ((isset($row['function']) && strpos($row['function'], 'call_user_func') === 0)
62: || (isset($row['class']) && is_subclass_of($row['class'], '\\Nette\\Database\\Connection'))
63: ) {
64: continue;
65: }
66: $source = [$row['file'], (int) $row['line']];
67: break;
68: }
69: }
70: if ($result instanceof Nette\Database\ResultSet) {
71: $this->totalTime += $result->getTime();
72: if ($this->count < $this->maxQueries) {
73: $this->queries[] = [$connection, $result->getQueryString(), $result->getParameters(), $source, $result->getTime(), $result->getRowCount(), null];
74: }
75:
76: } elseif ($result instanceof \PDOException && $this->count < $this->maxQueries) {
77: $this->queries[] = [$connection, $result->queryString, null, $source, null, null, $result->getMessage()];
78: }
79: }
80:
81:
82: public static function renderException($e)
83: {
84: if (!$e instanceof \PDOException) {
85: return;
86: }
87: if (isset($e->queryString)) {
88: $sql = $e->queryString;
89:
90: } elseif ($item = Tracy\Helpers::findTrace($e->getTrace(), 'PDO::prepare')) {
91: $sql = $item['args'][0];
92: }
93: return isset($sql) ? [
94: 'tab' => 'SQL',
95: 'panel' => Helpers::dumpSql($sql),
96: ] : null;
97: }
98:
99:
100: public function getTab()
101: {
102: $name = $this->name;
103: $count = $this->count;
104: $totalTime = $this->totalTime;
105: ob_start(function () {});
106: require __DIR__ . '/templates/ConnectionPanel.tab.phtml';
107: return ob_get_clean();
108: }
109:
110:
111: public function getPanel()
112: {
113: $this->disabled = true;
114: if (!$this->count) {
115: return;
116: }
117:
118: $name = $this->name;
119: $count = $this->count;
120: $totalTime = $this->totalTime;
121: $queries = [];
122: foreach ($this->queries as $query) {
123: list($connection, $sql, $params, $source, $time, $rows, $error) = $query;
124: $explain = null;
125: if (!$error && $this->explain && preg_match('#\s*\(?\s*SELECT\s#iA', $sql)) {
126: try {
127: $cmd = is_string($this->explain) ? $this->explain : 'EXPLAIN';
128: $explain = $connection->queryArgs("$cmd $sql", $params)->fetchAll();
129: } catch (\PDOException $e) {
130: }
131: }
132: $query[] = $explain;
133: $queries[] = $query;
134: }
135:
136: ob_start(function () {});
137: require __DIR__ . '/templates/ConnectionPanel.panel.phtml';
138: return ob_get_clean();
139: }
140: }
141: