Issues (28)

src/Adapter/Nette/Tracy/DoctrineSQLPanel.php (5 issues)

1
<?php declare(strict_types = 1);
2
3
namespace Portiny\Doctrine\Adapter\Nette\Tracy;
4
5
use Doctrine\DBAL\Logging\SQLLogger;
6
use Doctrine\ORM\Cache\Logging\CacheLoggerChain;
7
use Doctrine\ORM\Cache\Logging\StatisticsCacheLogger;
8
use Doctrine\ORM\EntityManager;
9
use Tracy\Debugger;
10
use Tracy\Dumper;
11
use Tracy\IBarPanel;
12
13
/**
14
 * Debug panel for Doctrine
15
 *
16
 * @author David Grudl
17
 * @author Patrik Votoček
18
 * @author Jan Mareš
19
 * @author Tomáš Pilař
20
 */
21
final class DoctrineSQLPanel implements IBarPanel, SQLLogger
0 ignored issues
show
Deprecated Code introduced by
The interface Doctrine\DBAL\Logging\SQLLogger has been deprecated: Use {@see \Doctrine\DBAL\Logging\Middleware} or implement {@see \Doctrine\DBAL\Driver\Middleware} instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

21
final class DoctrineSQLPanel implements IBarPanel, /** @scrutinizer ignore-deprecated */ SQLLogger

This interface has been deprecated. The supplier of the interface has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the interface will be removed and what other interface to use instead.

Loading history...
22
{
23
	public const DATA_INDEX_SQL = 0;
24
25
	public const DATA_INDEX_PARAMS = 1;
26
27
	public const DATA_INDEX_TYPES = 2;
28
29
	public const DATA_INDEX_TIME = 3;
30
31
	public const DATA_INDEX_TRACE = 4;
32
33
	public const DATA_INDEX_COUNT = 5;
34
35
	/**
36
	 * @var int
37
	 */
38
	private $totalTime = 0;
39
40
	/**
41
	 * @var bool
42
	 */
43
	private $sortQueries = false;
44
45
	/**
46
	 * @var array
47
	 */
48
	private $queries = [];
49
50
	/**
51
	 * @var EntityManager
52
	 */
53
	private $entityManager;
54
55
56 4
	public function __construct(EntityManager $entityManager)
57
	{
58 4
		$this->entityManager = $entityManager;
59 4
	}
60
61
62
	/**
63
	 * {@inheritdoc}
64
	 */
65 4
	public function startQuery($sql, ?array $params = null, ?array $types = null): void
66
	{
67 4
		Debugger::timer('doctrine');
68
69 4
		$this->queries[] = [
70 4
			self::DATA_INDEX_SQL => $sql,
71 4
			self::DATA_INDEX_PARAMS => $params,
72 4
			self::DATA_INDEX_TYPES => $types,
73 4
			self::DATA_INDEX_TIME => 0,
74 4
			self::DATA_INDEX_TRACE => debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS),
75
		];
76 4
	}
77
78
79
	/**
80
	 * {@inheritdoc}
81
	 */
82 4
	public function stopQuery(): void
83
	{
84 4
		$keys = array_keys($this->queries);
85 4
		$key = end($keys);
86 4
		$this->queries[$key][self::DATA_INDEX_TIME] = Debugger::timer('doctrine');
87 4
		$this->totalTime += $this->queries[$key][self::DATA_INDEX_TIME];
88 4
	}
89
90
91
	/**
92
	 * {@inheritdoc}
93
	 */
94 1
	public function getTab(): string
95
	{
96
		return '<span title="Doctrine 2">'
97
			. '<img  src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAKT2lDQ1BQaG90b3Nob3A'
98
			. 'gSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIo'
99
			. 'K2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDw'
100
			. 'rIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl'
101
			. '7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABR'
102
			. 'G8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8'
103
			. 't6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcp'
104
			. 'Xff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcU'
105
			. 'l0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAA'
106
			. 'ARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLy'
107
			. 'BCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLV'
108
			. 'Duag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQ'
109
			. 'SgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkm'
110
			. 'xpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2'
111
			. 'ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl'
112
			. '3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61M'
113
			. 'bU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvO'
114
			. 'UZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9l'
115
			. 'T3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcN'
116
			. 'AQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2B'
117
			. 'aeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8W'
118
			. 'uw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc'
119
			. '+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrde'
120
			. 'wt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1'
121
			. 'BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU8'
122
			. '5ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJT'
123
			. 'wQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZB'
124
			. 'xQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsI'
125
			. 'S4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+'
126
			. '1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7Z'
127
			. 'DuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXH'
128
			. 'txwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRpt'
129
			. 'TmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK'
130
			. '4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu'
131
			. '72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6'
132
			. 'i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8'
133
			. 'EA5jz/GMzLdsAAAAJcEhZcwAALiMAAC4jAXilP3YAAAAHdElNRQffCxUPDinoxmA5AAABO1BMVEUAAADjcTnqakDwgzrxgz7ziDv'
134
			. '1gz72iUD5hz35iD75iT7/gAD/gED/gEf/gID/iz25VS33hDv3iUjngzf/hT7xgTz3hj31gTz8iTv4hj7tfTr5hj77ij78iT73hj3'
135
			. '6iD37hkD8iT35hz77hz77iT73hj36hz76hz/6hz76iD79iD/2hTz7iD75hz37iD76iD77iT75hz77iT75iD78ij/6iT/6iT74hz7'
136
			. '3hj37hz33hj30hD31gDb1gjj1gjn1hDr1hDv1hTz1hT31iEP1iUP2hT32hT72hj32i0b2lVX3hTr3hj33hz73k1L3mFz3mV33ml7'
137
			. '3m1/3nGP4hjz4hz74n2X4oGf4qXf4sYL5hz76iD781bz83cr849H96dz969/+7uT+8+v+8+3+9O7+9/H++PT//Pv//v7///+xeeL'
138
			. 'XAAAAO3RSTlMAAAAAAAAAAAAAAAAAAAAAAgUGBw0QEBERFBUZHR4fKSotP0RKW15lc32GlaWsydvo6err6+z2+vv8/YuXaogAAAA'
139
			. 'BYktHRGjLbPQiAAAAuklEQVQYGQXBhyIVABQA0FNkr6zsGTKzZT83e5Vsks39/y/oHFD7ZbC+HADMLU82qgRQaSpivsUHAGVtCxE'
140
			. '37aoBRRrGI+K6B1BhZGntVyHisB8wtBOX+bq7EWfdxfjUfBPx8yHf9wrxHfTF6vHvk5fMP4UfdTAafzMzM3NzqwsG4ur56fEt837'
141
			. 'loAk6D6Kwfp75by1moMpExEXmXcTOMPB5No7yNmJ7TA3g2+Lp/vX0VyWAUjp6W/mI/zsxJP3EcQMdAAAAAElFTkSuQmCC" />'
142 1
			. count($this->queries) . ' queries'
143 1
			. ($this->totalTime ? ' / ' . sprintf('%0.1f', $this->totalTime * 1000) . 'ms' : '')
144 1
			. '</span>';
145
	}
146
147
148
	/**
149
	 * {@inheritdoc}
150
	 */
151 1
	public function getPanel(): string
152
	{
153 1
		$s = '';
154
155 1
		if ($this->sortQueries) {
156
			$this->queries = $this->sortQueries($this->queries, self::DATA_INDEX_TIME);
157
		}
158
159 1
		foreach ($this->queries as $query) {
160 1
			$s .= $this->processQuery($query);
161
		}
162
163 1
		return $this->renderStyles() .
164 1
			'<h1>Queries: ' . count($this->queries) .
165 1
			($this->totalTime ? ', time: ' . sprintf('%0.3f', $this->totalTime * 1000) . ' ms' : '') .
166 1
			'</h1>
167
			<div class="tracy-inner nette-Doctrine2Panel">
168 1
			' . $this->renderPanelCacheStatistics() . '
169
			<h2>Queries</h2>
170
			<table>
171 1
			<tr><th>Time&nbsp;ms</th><th>SQL</th><th>Params</th><th>Trace</th></tr>' . $s .
172 1
			'</table>
173
			</div>';
174
	}
175
176
177
	/**
178
	 * Binds panel to debug bar.
179
	 */
180
	public function bindToBar(): void
181
	{
182
		if (! $this->isTracyEnabled()) {
183
			return;
184
		}
185
186
		$this->entityManager->getConfiguration()->setSQLLogger($this);
0 ignored issues
show
Deprecated Code introduced by
The function Doctrine\DBAL\Configuration::setSQLLogger() has been deprecated: Use {@see setMiddlewares()} and {@see \Doctrine\DBAL\Logging\Middleware} instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

186
		/** @scrutinizer ignore-deprecated */ $this->entityManager->getConfiguration()->setSQLLogger($this);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
187
		Debugger::getBar()->addPanel($this);
188
	}
189
190
191
	/**
192
	 * @param bool $sortQueries
193
	 */
194
	public function setSortQueries($sortQueries): void
195
	{
196
		$this->sortQueries = $sortQueries;
197
	}
198
199
200 2
	public function getQueries(): array
201
	{
202 2
		return $this->queries;
203
	}
204
205
206
	private function sortQueries(array $queries, int $key): array
207
	{
208
		uasort(
209
			$queries,
210
			function ($a, $b) use ($key) {
211
				if ($a[$key] === $b[$key]) {
212
					return 0;
213
				}
214
215
				return $a[$key] > $b[$key] ? -1 : 1;
216
			}
217
		);
218
219
		return $queries;
220
	}
221
222
223 1
	private function processQuery(array $query): string
224
	{
225 1
		$s = '<tr>';
226 1
		$s .= '<td>' . sprintf('%0.3f', $query[self::DATA_INDEX_TIME] * 1000);
227
228 1
		if (isset($query[self::DATA_INDEX_COUNT])) {
229
			$s .= '/' . sprintf('%d', $query[self::DATA_INDEX_COUNT]);
230
		}
231
232 1
		$s .= '</td>';
233
234
		$s .= '<td class="nette-Doctrine2Panel-sql" style="min-width: 400px">' .
235 1
			Helper::dumpSql($query[self::DATA_INDEX_SQL]);
236
237 1
		$s .= '</td>';
238 1
		$s .= '<td>' . Dumper::toHtml($query[self::DATA_INDEX_PARAMS]) . '</td>';
239 1
		$s .= '<td>' . Dumper::toHtml($query[self::DATA_INDEX_TRACE]) . '</td>';
240 1
		$s .= '</tr>';
241
242 1
		return $s;
243
	}
244
245
246 1
	private function renderStyles(): string
247
	{
248 1
		return '<style>
249
			#tracy-debug td.nette-Doctrine2Panel-sql { background: white !important }
250
			#tracy-debug .nette-Doctrine2Panel-source { color: #BBB !important }
251
			#tracy-debug div.tracy-inner.nette-Doctrine2Panel { max-width: 1000px }
252
			#tracy-debug .nette-Doctrine2Panel tr table { margin: 8px 0; max-height: 150px; overflow:auto }
253
			#tracy-debug .nette-Doctrine2Panel-cache-green { color: green !important; font-weight: bold }
254
			#tracy-debug .nette-Doctrine2Panel-cache-red { color: red !important; font-weight: bold }
255
			#tracy-debug .nette-Doctrine2Panel h2 { font-size: 23px; }
256
			</style>';
257
	}
258
259
260 1
	private function renderPanelCacheStatistics(): string
261
	{
262 1
		if (empty($this->entityManager)) {
263
			return '';
264
		}
265
266 1
		$config = $this->entityManager->getConfiguration();
267 1
		if (! $config->isSecondLevelCacheEnabled()) {
268 1
			return '';
269
		}
270
271
		$cacheConfiguration = $config->getSecondLevelCacheConfiguration();
272
		if (! $cacheConfiguration) {
273
			return '';
274
		}
275
276
		$cacheLogger = $cacheConfiguration->getCacheLogger();
277
		if (! $cacheLogger instanceof CacheLoggerChain) {
278
			return '';
279
		}
280
281
		/** @var StatisticsCacheLogger|null $statistics */
282
		$statistics = $cacheLogger->getLogger('statistics');
283
		if (! $statistics) {
0 ignored issues
show
$statistics is of type Doctrine\ORM\Cache\Logging\StatisticsCacheLogger, thus it always evaluated to true.
Loading history...
284
			return '';
285
		}
286
287
		$cacheDriver = $this->entityManager->getConfiguration()->getMetadataCacheImpl();
0 ignored issues
show
Deprecated Code introduced by
The function Doctrine\ORM\Configuration::getMetadataCacheImpl() has been deprecated: Deprecated in favor of getMetadataCache ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

287
		$cacheDriver = /** @scrutinizer ignore-deprecated */ $this->entityManager->getConfiguration()->getMetadataCacheImpl();

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
288
		$driverInformation = '';
289
		if ($cacheDriver) {
290
			$driverInformation = get_class($cacheDriver);
291
		}
292
293
		return '<h2>Second Level Cache</h2>
294
			<table>
295
				<tr>
296
					<td>Driver</td>
297
					<td><strong>' . $driverInformation . '</strong></td>
298
				</tr>
299
				<tr>
300
					<td>Cache hits</td>
301
					<td>
302
						<strong class="nette-Doctrine2Panel-cache-green">' . $statistics->getHitCount() . '</strong>
303
					</td>
304
				</tr>
305
				<tr>
306
					<td>Cache misses</td>
307
					<td>
308
						<strong class="nette-Doctrine2Panel-cache-red">' . $statistics->getMissCount() . '</strong>
309
					</td>
310
				</tr>
311
				<tr>
312
					<td>Cache puts</td>
313
					<td>
314
						<strong class="nette-Doctrine2Panel-cache-red">' . $statistics->getPutCount() . '</strong>
315
					</td>
316
				</tr>
317
			</table>';
318
	}
319
320
321
	private function isTracyEnabled(): bool
322
	{
323
		return PHP_SAPI !== 'cli' && Debugger::isEnabled() && ! Debugger::$productionMode;
0 ignored issues
show
Bug Best Practice introduced by
The expression Tracy\Debugger::productionMode of type boolean|null is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
324
	}
325
326
}
327