Completed
Push — master ( 4e8223...162bbd )
by Tomáš
07:39
created

src/Adapter/Nette/Tracy/DoctrineSQLPanel.php (1 issue)

Labels
Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Portiny\Doctrine\Adapter\Nette\Tracy;
6
7
use Doctrine\DBAL\Logging\SQLLogger;
8
use Doctrine\ORM\Cache\Logging\CacheLoggerChain;
9
use Doctrine\ORM\Cache\Logging\StatisticsCacheLogger;
10
use Doctrine\ORM\EntityManager;
11
use Tracy\Debugger;
12
use Tracy\Dumper;
13
use Tracy\IBarPanel;
14
15
/**
16
 * Debug panel for Doctrine
17
 *
18
 * @author David Grudl
19
 * @author Patrik Votoček
20
 * @author Jan Mareš
21
 * @author Tomáš Pilař
22
 */
23
final class DoctrineSQLPanel implements IBarPanel, SQLLogger
24
{
25
	/**
26
	 * @var int
27
	 */
28
	public const DATA_INDEX_SQL = 0;
29
30
	/**
31
	 * @var int
32
	 */
33
	public const DATA_INDEX_PARAMS = 1;
34
35
	/**
36
	 * @var int
37
	 */
38
	public const DATA_INDEX_TYPES = 2;
39
40
	/**
41
	 * @var int
42
	 */
43
	public const DATA_INDEX_TIME = 3;
44
45
	/**
46
	 * @var int
47
	 */
48
	public const DATA_INDEX_TRACE = 4;
49
50
	/**
51
	 * @var int
52
	 */
53
	public const DATA_INDEX_COUNT = 5;
54
55
	/**
56
	 * @var int
57
	 */
58
	private $totalTime = 0;
59
60
	/**
61
	 * @var bool
62
	 */
63
	private $sortQueries = FALSE;
64
65
	/**
66
	 * @var array
67
	 */
68
	private $queries = [];
69
70
	/**
71
	 * @var EntityManager
72
	 */
73
	private $entityManager;
74
75 6
	public function __construct(EntityManager $entityManager)
76
	{
77 6
		$this->entityManager = $entityManager;
78 6
	}
79
80
	/**
81
	 * {@inheritdoc}
82
	 */
83 4
	public function startQuery($sql, ?array $params = NULL, ?array $types = NULL): void
84
	{
85 4
		Debugger::timer('doctrine');
86
87 4
		$this->queries[] = [
88 4
			self::DATA_INDEX_SQL => $sql,
89 4
			self::DATA_INDEX_PARAMS => $params,
90 4
			self::DATA_INDEX_TYPES => $types,
91 4
			self::DATA_INDEX_TIME => 0,
92 4
			self::DATA_INDEX_TRACE => debug_backtrace(PHP_VERSION_ID >= 50306 ? DEBUG_BACKTRACE_IGNORE_ARGS : FALSE),
93
		];
94 4
	}
95
96
	/**
97
	 * {@inheritdoc}
98
	 */
99 4
	public function stopQuery(): void
100
	{
101 4
		$keys = array_keys($this->queries);
102 4
		$key = end($keys);
103 4
		$this->queries[$key][self::DATA_INDEX_TIME] = Debugger::timer('doctrine');
104 4
		$this->totalTime += $this->queries[$key][self::DATA_INDEX_TIME];
105 4
	}
106
107
	/**
108
	 * {@inheritdoc}
109
	 */
110 1
	public function getTab(): string
111
	{
112
		return '<span title="Doctrine 2">'
113
			. '<img  src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAKT2lDQ1BQaG90b3Nob3A'
114
			. 'gSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIo'
115
			. 'K2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDw'
116
			. 'rIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl'
117
			. '7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABR'
118
			. 'G8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8'
119
			. 't6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcp'
120
			. 'Xff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcU'
121
			. 'l0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAA'
122
			. 'ARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLy'
123
			. 'BCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLV'
124
			. 'Duag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQ'
125
			. 'SgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkm'
126
			. 'xpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2'
127
			. 'ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl'
128
			. '3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61M'
129
			. 'bU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvO'
130
			. 'UZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9l'
131
			. 'T3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcN'
132
			. 'AQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2B'
133
			. 'aeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8W'
134
			. 'uw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc'
135
			. '+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrde'
136
			. 'wt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1'
137
			. 'BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU8'
138
			. '5ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJT'
139
			. 'wQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZB'
140
			. 'xQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsI'
141
			. 'S4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+'
142
			. '1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7Z'
143
			. 'DuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXH'
144
			. 'txwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRpt'
145
			. 'TmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK'
146
			. '4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu'
147
			. '72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6'
148
			. 'i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8'
149
			. 'EA5jz/GMzLdsAAAAJcEhZcwAALiMAAC4jAXilP3YAAAAHdElNRQffCxUPDinoxmA5AAABO1BMVEUAAADjcTnqakDwgzrxgz7ziDv'
150
			. '1gz72iUD5hz35iD75iT7/gAD/gED/gEf/gID/iz25VS33hDv3iUjngzf/hT7xgTz3hj31gTz8iTv4hj7tfTr5hj77ij78iT73hj3'
151
			. '6iD37hkD8iT35hz77hz77iT73hj36hz76hz/6hz76iD79iD/2hTz7iD75hz37iD76iD77iT75hz77iT75iD78ij/6iT/6iT74hz7'
152
			. '3hj37hz33hj30hD31gDb1gjj1gjn1hDr1hDv1hTz1hT31iEP1iUP2hT32hT72hj32i0b2lVX3hTr3hj33hz73k1L3mFz3mV33ml7'
153
			. '3m1/3nGP4hjz4hz74n2X4oGf4qXf4sYL5hz76iD781bz83cr849H96dz969/+7uT+8+v+8+3+9O7+9/H++PT//Pv//v7///+xeeL'
154
			. 'XAAAAO3RSTlMAAAAAAAAAAAAAAAAAAAAAAgUGBw0QEBERFBUZHR4fKSotP0RKW15lc32GlaWsydvo6err6+z2+vv8/YuXaogAAAA'
155
			. 'BYktHRGjLbPQiAAAAuklEQVQYGQXBhyIVABQA0FNkr6zsGTKzZT83e5Vsks39/y/oHFD7ZbC+HADMLU82qgRQaSpivsUHAGVtCxE'
156
			. '37aoBRRrGI+K6B1BhZGntVyHisB8wtBOX+bq7EWfdxfjUfBPx8yHf9wrxHfTF6vHvk5fMP4UfdTAafzMzM3NzqwsG4ur56fEt837'
157
			. 'loAk6D6Kwfp75by1moMpExEXmXcTOMPB5No7yNmJ7TA3g2+Lp/vX0VyWAUjp6W/mI/zsxJP3EcQMdAAAAAElFTkSuQmCC" />'
158 1
			. count($this->queries) . ' queries'
159 1
			. ($this->totalTime ? ' / ' . sprintf('%0.1f', $this->totalTime * 1000) . 'ms' : '')
160 1
			. '</span>';
161
	}
162
163
	/**
164
	 * {@inheritdoc}
165
	 */
166 1
	public function getPanel(): string
167
	{
168 1
		$s = '';
169
170 1
		if ($this->sortQueries) {
171
			$this->queries = $this->sortQueries($this->queries, self::DATA_INDEX_TIME);
172
		}
173
174 1
		foreach ($this->queries as $query) {
175 1
			$s .= $this->processQuery($query);
176
		}
177
178 1
		return $this->renderStyles() .
179 1
			'<h1>Queries: ' . count($this->queries) .
180 1
			($this->totalTime ? ', time: ' . sprintf('%0.3f', $this->totalTime * 1000) . ' ms' : '') .
181 1
			'</h1>
182
			<div class="tracy-inner nette-Doctrine2Panel">
183 1
			' . $this->renderPanelCacheStatistics() . '
184
			<h2>Queries</h2>
185
			<table>
186 1
			<tr><th>Time&nbsp;ms</th><th>SQL</th><th>Params</th><th>Trace</th></tr>' . $s .
187 1
			'</table>
188
			</div>';
189
	}
190
191
	/**
192
	 * Binds panel to debug bar.
193
	 */
194 6
	public function bindToBar(): void
195
	{
196 6
		if (! $this->isTracyEnabled()) {
197 6
			return;
198
		}
199
200
		$this->entityManager->getConfiguration()->setSQLLogger($this);
201
		Debugger::getBar()->addPanel($this);
202
	}
203
204
	/**
205
	 * @param bool $sortQueries
206
	 */
207
	public function setSortQueries($sortQueries): void
208
	{
209
		$this->sortQueries = $sortQueries;
210
	}
211
212 2
	public function getQueries(): array
213
	{
214 2
		return $this->queries;
215
	}
216
217
	private function sortQueries(array $queries, int $key): array
218
	{
219
		uasort(
220
			$queries,
221
			function ($a, $b) use ($key) {
222
				if ($a[$key] === $b[$key]) {
223
					return 0;
224
				}
225
226
				return $a[$key] > $b[$key] ? -1 : 1;
227
			}
228
		);
229
230
		return $queries;
231
	}
232
233 1
	private function processQuery(array $query): string
234
	{
235 1
		$s = '<tr>';
236 1
		$s .= '<td>' . sprintf('%0.3f', $query[self::DATA_INDEX_TIME] * 1000);
237
238 1
		if (isset($query[self::DATA_INDEX_COUNT])) {
239
			$s .= '/' . sprintf('%d', $query[self::DATA_INDEX_COUNT]);
240
		}
241
242 1
		$s .= '</td>';
243
244
		$s .= '<td class="nette-Doctrine2Panel-sql" style="min-width: 400px">' .
245 1
			Helper::dumpSql($query[self::DATA_INDEX_SQL]);
246
247 1
		$s .= '</td>';
248 1
		$s .= '<td>' . Dumper::toHtml($query[self::DATA_INDEX_PARAMS]) . '</td>';
249 1
		$s .= '<td>' . Dumper::toHtml($query[self::DATA_INDEX_TRACE]) . '</td>';
250 1
		$s .= '</tr>';
251
252 1
		return $s;
253
	}
254
255 1
	private function renderStyles(): string
256
	{
257 1
		return '<style>
258
			#tracy-debug td.nette-Doctrine2Panel-sql { background: white !important }
259
			#tracy-debug .nette-Doctrine2Panel-source { color: #BBB !important }
260
			#tracy-debug div.tracy-inner.nette-Doctrine2Panel { max-width: 1000px }
261
			#tracy-debug .nette-Doctrine2Panel tr table { margin: 8px 0; max-height: 150px; overflow:auto }
262
			#tracy-debug .nette-Doctrine2Panel-cache-green { color: green !important; font-weight: bold }
263
			#tracy-debug .nette-Doctrine2Panel-cache-red { color: red !important; font-weight: bold }
264
			#tracy-debug .nette-Doctrine2Panel h2 { font-size: 23px; }
265
			</style>';
266
	}
267
268 1
	private function renderPanelCacheStatistics(): string
269
	{
270 1
		if (empty($this->entityManager)) {
271
			return '';
272
		}
273
274 1
		$config = $this->entityManager->getConfiguration();
275 1
		if (! $config->isSecondLevelCacheEnabled()) {
276 1
			return '';
277
		}
278
279
		$cacheConfiguration = $config->getSecondLevelCacheConfiguration();
280
		if (! $cacheConfiguration) {
281
			return '';
282
		}
283
284
		$cacheLogger = $cacheConfiguration->getCacheLogger();
285
		if (! $cacheLogger instanceof CacheLoggerChain) {
0 ignored issues
show
The class Doctrine\ORM\Cache\Logging\CacheLoggerChain does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
286
			return '';
287
		}
288
289
		/** @var StatisticsCacheLogger|null $statistics */
290
		$statistics = $cacheLogger->getLogger('statistics');
291
		if (! $statistics) {
292
			return '';
293
		}
294
295
		$cacheDriver = $this->entityManager->getConfiguration()->getMetadataCacheImpl();
296
		$driverInformation = '';
297
		if ($cacheDriver) {
298
			$driverInformation = get_class($cacheDriver);
299
		}
300
301
		return '<h2>Second Level Cache</h2>
302
			<table>
303
				<tr>
304
					<td>Driver</td>
305
					<td><strong>' . $driverInformation . '</strong></td>
306
				</tr>
307
				<tr>
308
					<td>Cache hits</td>
309
					<td>
310
						<strong class="nette-Doctrine2Panel-cache-green">' . $statistics->getHitCount() . '</strong>
311
					</td>
312
				</tr>
313
				<tr>
314
					<td>Cache misses</td>
315
					<td>
316
						<strong class="nette-Doctrine2Panel-cache-red">' . $statistics->getMissCount() . '</strong>
317
					</td>
318
				</tr>
319
				<tr>
320
					<td>Cache puts</td>
321
					<td>
322
						<strong class="nette-Doctrine2Panel-cache-red">' . $statistics->getPutCount() . '</strong>
323
					</td>
324
				</tr>
325
			</table>';
326
	}
327
328 6
	private function isTracyEnabled(): bool
329
	{
330 6
		return PHP_SAPI !== 'cli' && Debugger::isEnabled() && ! Debugger::$productionMode;
331
	}
332
}
333