Completed
Push — master ( 6a29f2...e72bde )
by Filip
02:05
created

Panel::renderException()   D

Complexity

Conditions 27
Paths 28

Size

Total Lines 100
Code Lines 61

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 100
rs 4.509
cc 27
eloc 61
nc 28
nop 2

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * This file is part of the Kdyby (http://www.kdyby.org)
5
 *
6
 * Copyright (c) 2008 Filip Procházka ([email protected])
7
 *
8
 * For the full copyright and license information, please view the file license.txt that was distributed with this source code.
9
 */
10
11
namespace Kdyby\Doctrine\Diagnostics;
12
13
use Doctrine;
14
use Doctrine\Common\Collections\ArrayCollection;
15
use Doctrine\Common\Persistence\Proxy;
16
use Doctrine\Common\Annotations\AnnotationException;
17
use Doctrine\DBAL\Platforms\AbstractPlatform;
18
use Doctrine\DBAL\Types\Type;
19
use Kdyby;
20
use Nette;
21
use Nette\Utils\Strings;
22
use Tracy\Bar;
23
use Tracy\BlueScreen;
24
use Tracy\Debugger;
25
use Tracy\Dumper;
26
use Tracy\Helpers;
27
use Tracy\IBarPanel;
28
29
30
31
/**
32
 * Debug panel for Doctrine
33
 *
34
 * @author David Grudl
35
 * @author Patrik Votoček
36
 * @author Filip Procházka <[email protected]>
37
 */
38
class Panel extends Nette\Object implements IBarPanel, Doctrine\DBAL\Logging\SQLLogger
39
{
40
41
	/**
42
	 * @var int logged time
43
	 */
44
	public $totalTime = 0;
45
46
	/**
47
	 * @var array
48
	 */
49
	public $queries = [];
50
51
	/**
52
	 * @var array
53
	 */
54
	public $failed = [];
55
56
	/**
57
	 * @var array
58
	 */
59
	public $skipPaths = [
60
		'vendor/nette/', 'src/Nette/',
61
		'vendor/doctrine/collections/', 'lib/Doctrine/Collections/',
62
		'vendor/doctrine/common/', 'lib/Doctrine/Common/',
63
		'vendor/doctrine/dbal/', 'lib/Doctrine/DBAL/',
64
		'vendor/doctrine/orm/', 'lib/Doctrine/ORM/',
65
		'vendor/kdyby/doctrine/', 'src/Kdyby/Doctrine/',
66
		'vendor/phpunit',
67
	];
68
69
	/**
70
	 * @var \Doctrine\DBAL\Connection
71
	 */
72
	private $connection;
73
74
	/**
75
	 * @var \Doctrine\ORM\EntityManager
76
	 */
77
	private $em;
78
79
	/**
80
	 * @var array
81
	 */
82
	private $whitelistExceptions = [];
83
84
	/**
85
	 * @var Doctrine\ORM\UnitOfWork
86
	 */
87
	private $unitOfWorkSnapshot;
88
89
90
91
	public function markExceptionOwner(Doctrine\ORM\EntityManager $em, $exception)
92
	{
93
		if ($this->em !== $em) {
94
			return;
95
		}
96
97
		$this->whitelistExceptions[] = $exception;
98
	}
99
100
101
102
	public function snapshotUnitOfWork(Doctrine\ORM\EntityManager $em)
103
	{
104
		if ($this->em !== $em) {
105
			return;
106
		}
107
108
		$this->unitOfWorkSnapshot = clone $em->getUnitOfWork();
109
	}
110
111
112
113
	/***************** Doctrine\DBAL\Logging\SQLLogger ********************/
114
115
116
117
	/**
118
	 * @param string
119
	 * @param array
120
	 * @param array
121
	 */
122
	public function startQuery($sql, array $params = NULL, array $types = NULL)
123
	{
124
		Debugger::timer('doctrine');
125
126
		$source = NULL;
127
		foreach (debug_backtrace(FALSE) as $row) {
128
			if (isset($row['file']) && $this->filterTracePaths(realpath($row['file']))) {
129
				if (isset($row['class']) && stripos($row['class'], '\\' . Proxy::MARKER) !== FALSE) {
130
					if (!in_array('Doctrine\Common\Persistence\Proxy', class_implements($row['class']))) {
131
						continue;
132
133
					} elseif (isset($row['function']) && $row['function'] === '__load') {
134
						continue;
135
					}
136
137
				} elseif (stripos($row['file'], DIRECTORY_SEPARATOR . Proxy::MARKER) !== FALSE) {
138
					continue;
139
				}
140
141
				$source = [$row['file'], (int) $row['line']];
142
				break;
143
			}
144
		}
145
146
		$this->queries[] = [$sql, $params, NULL, $types, $source];
147
	}
148
149
150
151
	/**
152
	 * @param string $file
153
	 * @return boolean
154
	 */
155
	protected function filterTracePaths($file)
156
	{
157
		$file = str_replace(DIRECTORY_SEPARATOR, '/', $file);
158
		$return = is_file($file);
159
		foreach ($this->skipPaths as $path) {
160
			if (!$return) {
161
				break;
162
			}
163
			$return = $return && strpos($file, '/' . trim($path, '/') . '/') === FALSE;
164
		}
165
		return $return;
166
	}
167
168
169
170
	/**
171
	 * @return array
172
	 */
173
	public function stopQuery()
174
	{
175
		$keys = array_keys($this->queries);
176
		$key = end($keys);
177
		$this->queries[$key][2] = $time = Debugger::timer('doctrine');
178
		$this->totalTime += $time;
179
180
		return $this->queries[$key] + array_fill_keys(range(0, 4), NULL);
181
	}
182
183
184
185
	/**
186
	 * @param \Exception|\Throwable $exception
187
	 */
188
	public function queryFailed($exception)
189
	{
190
		$this->failed[spl_object_hash($exception)] = $this->stopQuery();
191
	}
192
193
194
195
	/***************** Tracy\IBarPanel ********************/
196
197
198
199
	/**
200
	 * @return string
201
	 */
202
	public function getTab()
203
	{
204
		return '<span title="Doctrine 2">'
205
			. '<svg viewBox="0 0 2048 2048"><path fill="#aaa" d="M1024 896q237 0 443-43t325-127v170q0 69-103 128t-280 93.5-385 34.5-385-34.5-280-93.5-103-128v-170q119 84 325 127t443 43zm0 768q237 0 443-43t325-127v170q0 69-103 128t-280 93.5-385 34.5-385-34.5-280-93.5-103-128v-170q119 84 325 127t443 43zm0-384q237 0 443-43t325-127v170q0 69-103 128t-280 93.5-385 34.5-385-34.5-280-93.5-103-128v-170q119 84 325 127t443 43zm0-1152q208 0 385 34.5t280 93.5 103 128v128q0 69-103 128t-280 93.5-385 34.5-385-34.5-280-93.5-103-128v-128q0-69 103-128t280-93.5 385-34.5z"></path></svg>'
206
			. '<span class="tracy-label">'
207
			. count($this->queries) . ' queries'
208
			. ($this->totalTime ? ' / ' . sprintf('%0.1f', $this->totalTime * 1000) . ' ms' : '')
209
			. '</span>'
210
			. '</span>';
211
	}
212
213
214
215
	/**
216
	 * @return string
217
	 */
218
	public function getPanel()
219
	{
220
		if (empty($this->queries)) {
221
			return '';
222
		}
223
224
		$connParams = $this->connection->getParams();
225
		if ($connParams['driver'] === 'pdo_sqlite' && isset($connParams['path'])) {
226
			$host = 'path: ' . basename($connParams['path']);
227
228
		} else {
229
			$host = sprintf('host: %s%s/%s',
230
				$this->connection->getHost(),
231
				(($p = $this->connection->getPort()) ? ':' . $p : ''),
232
				$this->connection->getDatabase()
233
			);
234
		}
235
236
		return
237
			$this->renderStyles() .
238
			sprintf('<h1>Queries: %s %s, %s</h1>',
239
				count($this->queries),
240
				($this->totalTime ? ', time: ' . sprintf('%0.3f', $this->totalTime * 1000) . ' ms' : ''),
241
				$host
242
			) .
243
			'<div class="nette-inner tracy-inner nette-Doctrine2Panel">' .
244
				implode('<br>', array_filter([
245
					$this->renderPanelCacheStatistics(),
246
					$this->renderPanelQueries()
247
				])) .
248
			'</div>';
249
	}
250
251
252
253
	private function renderPanelCacheStatistics()
254
	{
255
		if (empty($this->em)) {
256
			return '';
257
		}
258
259
		$config = $this->em->getConfiguration();
260
		if (!$config->isSecondLevelCacheEnabled()) {
261
			return '';
262
		}
263
264
		$loggerChain = $config->getSecondLevelCacheConfiguration()
265
			->getCacheLogger();
266
267
		if (!$loggerChain instanceof Doctrine\ORM\Cache\Logging\CacheLoggerChain) {
0 ignored issues
show
Bug introduced by
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...
268
			return '';
269
		}
270
271
		if (!$statistics = $loggerChain->getLogger('statistics')) {
272
			return '';
273
		}
274
275
		return Dumper::toHtml($statistics, [Dumper::DEPTH => 5]);
276
	}
277
278
279
280
	private function renderPanelQueries()
281
	{
282
		if (empty($this->queries)) {
283
			return "";
284
		}
285
286
		$s = "";
287
		foreach ($this->queries as $query) {
288
			$s .= $this->processQuery($query);
289
		}
290
291
		return '<table><tr><th>ms</th><th>SQL Statement</th></tr>' . $s . '</table>';
292
	}
293
294
295
296
	/**
297
	 * @return string
298
	 */
299
	protected function renderStyles()
300
	{
301
		return '<style>
302
			#nette-debug td.nette-Doctrine2Panel-sql { background: white !important}
303
			#nette-debug .nette-Doctrine2Panel-source { color: #BBB !important }
304
			#nette-debug nette-Doctrine2Panel tr table { margin: 8px 0; max-height: 150px; overflow:auto }
305
			#tracy-debug td.nette-Doctrine2Panel-sql { background: white !important}
306
			#tracy-debug .nette-Doctrine2Panel-source { color: #BBB !important }
307
			#tracy-debug nette-Doctrine2Panel tr table { margin: 8px 0; max-height: 150px; overflow:auto }
308
		</style>';
309
	}
310
311
312
313
	/**
314
	 * @param array
315
	 * @return string
316
	 */
317
	protected function processQuery(array $query)
318
	{
319
		$h = 'htmlspecialchars';
320
		list($sql, $params, $time, $types, $source) = $query;
321
322
		$s = self::highlightQuery(static::formatQuery($sql, (array) $params, (array) $types, $this->connection ? $this->connection->getDatabasePlatform() : NULL));
323
		if ($source) {
324
			$s .= self::editorLink($source[0], $source[1], $h('.../' . basename(dirname($source[0]))) . '/<b>' . $h(basename($source[0])) . '</b>');
325
		}
326
327
		return '<tr><td>' . sprintf('%0.3f', $time * 1000) . '</td>' .
328
			'<td class = "nette-Doctrine2Panel-sql">' . $s . '</td></tr>';
329
	}
330
331
332
333
	/****************** Exceptions handling *********************/
334
335
336
337
	/**
338
	 * @param \Exception|\Throwable $e
339
	 * @return void|array
340
	 */
341
	public function renderQueryException($e)
342
	{
343
		if ($e instanceof \PDOException && count($this->queries)) {
344
			$types = $params = [];
345
346
			if ($this->connection !== NULL) {
347
				if (!$e instanceof Kdyby\Doctrine\DBALException || $e->connection !== $this->connection) {
348
					return NULL;
349
350
				} elseif (!isset($this->failed[spl_object_hash($e)])) {
351
					return NULL;
352
				}
353
354
				list($sql, $params, , , $source) = $this->failed[spl_object_hash($e)];
355
356
			} else {
357
				list($sql, $params, , $types, $source) = end($this->queries) + range(1, 5);
358
			}
359
360
			if (!$sql) {
361
				return NULL;
362
			}
363
364
			return [
365
				'tab' => 'SQL',
366
				'panel' => $this->dumpQuery($sql, $params, $types, $source),
367
			];
368
369
		} elseif ($e instanceof Kdyby\Doctrine\QueryException && $e->query !== NULL) {
370
			if ($e->query instanceof Doctrine\ORM\Query) {
0 ignored issues
show
Bug introduced by
The class Doctrine\ORM\Query 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...
371
				return [
372
					'tab' => 'DQL',
373
					'panel' => $this->dumpQuery($e->query->getDQL(), $e->query->getParameters()),
374
				];
375
376
			} elseif ($e->query instanceof Kdyby\Doctrine\NativeQueryWrapper) {
377
				return [
378
					'tab' => 'Native SQL',
379
					'panel' => $this->dumpQuery($e->query->getSQL(), $e->query->getParameters()),
380
				];
381
			}
382
		}
383
	}
384
385
386
387
	/**
388
	 * @param \Exception|\Throwable $e
389
	 * @param \Nette\DI\Container $dic
0 ignored issues
show
Bug introduced by
There is no parameter named $dic. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
390
	 * @return array
391
	 */
392
	public function renderEntityManagerException($e)
393
	{
394
		if (!in_array($e, $this->whitelistExceptions, TRUE)) {
395
			return NULL; // ignore
396
		}
397
398
		if (strpos(get_class($e), 'Doctrine\\ORM\\') !== FALSE && Helpers::findTrace($e->getTrace(), 'Doctrine\ORM\EntityManager::flush')) {
399
			$UoW = $this->unitOfWorkSnapshot ?: $this->em->getUnitOfWork();
400
401
			$panel = '<div class="inner"><p><b>IdentityMap</b></p>' .
402
				Dumper::toHtml($UoW->getIdentityMap(), [Dumper::COLLAPSE => TRUE]) .
403
				'</div>';
404
405
			if ($scheduled = $UoW->getScheduledEntityInsertions()) {
406
				$panel .= '<div class="inner"><p><b>Scheduled entity insertions</b></p>' .
407
					Dumper::toHtml($scheduled, [Dumper::COLLAPSE => TRUE]) .
408
					'</div>';
409
			}
410
411
			if ($scheduled = $UoW->getScheduledEntityDeletions()) {
412
				$panel .= '<div class="inner"><p><b>Scheduled entity deletions</b></p>' .
413
					Dumper::toHtml($scheduled, [Dumper::COLLAPSE => TRUE]) .
414
					'</div>';
415
			}
416
417
			if ($scheduled = $UoW->getScheduledEntityUpdates()) {
418
				$panel .= '<div class="inner"><p><b>Scheduled entity updates</b></p>' .
419
					Dumper::toHtml($scheduled, [Dumper::COLLAPSE => TRUE]) .
420
					'</div>';
421
			}
422
423
			return [
424
				'tab' => 'Doctrine\\ORM\\UnitOfWork',
425
				'panel' => $panel,
426
			];
427
		}
428
	}
429
430
431
432
	/**
433
	 * @param \Exception|\Throwable $e
434
	 * @param \Nette\DI\Container $dic
435
	 * @return array
436
	 */
437
	public static function renderException($e, Nette\DI\Container $dic)
438
	{
439
		if ($e instanceof AnnotationException) {
0 ignored issues
show
Bug introduced by
The class Doctrine\Common\Annotations\AnnotationException 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...
440
			if ($dump = self::highlightAnnotationLine($e)) {
441
				return [
442
					'tab' => 'Annotation',
443
					'panel' => $dump,
444
				];
445
			}
446
447
		} elseif ($e instanceof Doctrine\ORM\Mapping\MappingException) {
0 ignored issues
show
Bug introduced by
The class Doctrine\ORM\Mapping\MappingException 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...
448
			if ($invalidEntity = Strings::match($e->getMessage(), '~^Class "([\\S]+)" .*? is not .*? valid~i')) {
449
				$refl = Nette\Reflection\ClassType::from($invalidEntity[1]);
450
				$file = $refl->getFileName();
451
				$errorLine = $refl->getStartLine();
452
453
				return [
454
					'tab' => 'Invalid entity',
455
					'panel' => '<p><b>File:</b> ' . self::editorLink($file, $errorLine) . '</p>' .
456
						BlueScreen::highlightFile($file, $errorLine),
457
				];
458
			}
459
460
		} elseif ($e instanceof Doctrine\DBAL\Schema\SchemaException && $dic && ($em = $dic->getByType('Kdyby\Doctrine\EntityManager', FALSE))) {
0 ignored issues
show
Bug introduced by
The class Doctrine\DBAL\Schema\SchemaException 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...
461
			/** @var Kdyby\Doctrine\EntityManager $em */
462
463
			if ($invalidTable = Strings::match($e->getMessage(), '~table \'(.*?)\'~i')) {
464
				foreach ($em->getMetadataFactory()->getAllMetadata() as $class) {
465
					/** @var Kdyby\Doctrine\Mapping\ClassMetadata $class */
466
					if ($class->getTableName() === $invalidTable[1]) {
467
						$refl = $class->getReflectionClass();
468
						break;
469
					}
470
				}
471
472
				if (!isset($refl)) {
473
					return NULL;
474
				}
475
476
				$file = $refl->getFileName();
477
				$errorLine = $refl->getStartLine();
478
479
				return [
480
					'tab' => 'Invalid schema',
481
					'panel' => '<p><b>File:</b> ' . self::editorLink($file, $errorLine) . '</p>' .
482
						BlueScreen::highlightFile($file, $errorLine),
483
				];
484
			}
485
486
		} elseif ($e instanceof Kdyby\Doctrine\DBALException && $e->query) {
487
			return [
488
				'tab' => 'SQL',
489
				'panel' => self::highlightQuery(static::formatQuery($e->query, $e->params, [])),
490
			];
491
492
		} elseif ($e instanceof Doctrine\DBAL\Exception\DriverException) {
0 ignored issues
show
Bug introduced by
The class Doctrine\DBAL\Exception\DriverException 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...
493
			if (($prev = $e->getPrevious()) && ($item = Helpers::findTrace($e->getTrace(), 'Doctrine\DBAL\DBALException::driverExceptionDuringQuery'))) {
494
				/** @var \Doctrine\DBAL\Driver $driver */
495
				$driver = $item['args'][0];
496
				$params = isset($item['args'][3]) ? $item['args'][3] : [];
497
498
				return [
499
					'tab' => 'SQL',
500
					'panel' => self::highlightQuery(static::formatQuery($item['args'][2], $params, [], $driver->getDatabasePlatform())),
501
				];
502
			}
503
504
		} elseif ($e instanceof Doctrine\ORM\Query\QueryException) {
0 ignored issues
show
Bug introduced by
The class Doctrine\ORM\Query\QueryException 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...
505
			if (($prev = $e->getPrevious()) && preg_match('~^(SELECT|INSERT|UPDATE|DELETE)\s+.*~i', $prev->getMessage())) {
506
				return [
507
					'tab' => 'DQL',
508
					'panel' => self::highlightQuery(static::formatQuery($prev->getMessage(), [], [])),
509
				];
510
			}
511
512
		} elseif ($e instanceof \PDOException) {
513
			$params = [];
514
515
			if (isset($e->queryString)) {
516
				$sql = $e->queryString;
0 ignored issues
show
Bug introduced by
The property queryString does not seem to exist. Did you mean string?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
517
518
			} elseif ($item = Helpers::findTrace($e->getTrace(), 'Doctrine\DBAL\Connection::executeQuery')) {
519
				$sql = $item['args'][0];
520
				$params = $item['args'][1];
521
522
			} elseif ($item = Helpers::findTrace($e->getTrace(), 'PDO::query')) {
523
				$sql = $item['args'][0];
524
525
			} elseif ($item = Helpers::findTrace($e->getTrace(), 'PDO::prepare')) {
526
				$sql = $item['args'][0];
527
			}
528
529
			return isset($sql) ? [
530
				'tab' => 'SQL',
531
				'panel' => self::highlightQuery(static::formatQuery($sql, $params, [])),
532
			] : NULL;
533
		}
534
535
		return NULL;
536
	}
537
538
539
540
	/**
541
	 * @param string $query
542
	 * @param array|Doctrine\Common\Collections\ArrayCollection $params
543
	 * @param array $types
544
	 * @param string $source
545
	 *
546
	 * @return array
547
	 */
548
	protected function dumpQuery($query, $params, array $types = [], $source = NULL)
549
	{
550
		if ($params instanceof ArrayCollection) {
0 ignored issues
show
Bug introduced by
The class Doctrine\Common\Collections\ArrayCollection 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...
551
			$tmp = [];
552
			$tmpTypes = [];
553
			foreach ($params as $key => $param) {
554
				if ($param instanceof Doctrine\ORM\Query\Parameter) {
0 ignored issues
show
Bug introduced by
The class Doctrine\ORM\Query\Parameter 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...
555
					$tmpTypes[$param->getName()] = $param->getType();
556
					$tmp[$param->getName()] = $param->getValue();
557
					continue;
558
				}
559
				$tmp[$key] = $param;
560
			}
561
			$params = $tmp;
562
			$types = $tmpTypes;
563
		}
564
565
		// query
566
		$s = '<p><b>Query</b></p><table><tr><td class="nette-Doctrine2Panel-sql">';
567
		$s .= self::highlightQuery(static::formatQuery($query, $params, $types, $this->connection ? $this->connection->getDatabasePlatform() : NULL));
568
		$s .= '</td></tr></table>';
569
570
		$e = NULL;
571
		if ($source && is_array($source)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $source of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
572
			list($file, $line) = $source;
573
			$e = '<p><b>File:</b> ' . self::editorLink($file, $line) . '</p>';
574
		}
575
576
		// styles and dump
577
		return $this->renderStyles() . '<div class="nette-inner tracy-inner nette-Doctrine2Panel">' . $e . $s . '</div>';
578
	}
579
580
581
582
	/**
583
	 * Returns syntax highlighted SQL command.
584
	 * This method is same as Nette\Database\Helpers::dumpSql except for parameters handling.
585
	 * @link https://github.com/nette/database/blob/667143b2d5b940f78c8dc9212f95b1bbc033c6a3/src/Database/Helpers.php#L75-L138
586
	 * @author David Grudl
587
	 * @param string $sql
588
	 * @return string
589
	 */
590
	public static function highlightQuery($sql)
591
	{
592
		static $keywords1 = 'SELECT|(?:ON\s+DUPLICATE\s+KEY)?UPDATE|INSERT(?:\s+INTO)?|REPLACE(?:\s+INTO)?|DELETE|CALL|UNION|FROM|WHERE|HAVING|GROUP\s+BY|ORDER\s+BY|LIMIT|OFFSET|SET|VALUES|LEFT\s+JOIN|INNER\s+JOIN|TRUNCATE';
593
		static $keywords2 = 'ALL|DISTINCT|DISTINCTROW|IGNORE|AS|USING|ON|AND|OR|IN|IS|NOT|NULL|[RI]?LIKE|REGEXP|TRUE|FALSE|WITH|INSTANCE\s+OF';
594
595
		// insert new lines
596
		$sql = " $sql ";
597
		$sql = preg_replace("#(?<=[\\s,(])($keywords1)(?=[\\s,)])#i", "\n\$1", $sql);
598
599
		// reduce spaces
600
		$sql = preg_replace('#[ \t]{2,}#', ' ', $sql);
601
602
		$sql = wordwrap($sql, 100);
603
		$sql = preg_replace('#([ \t]*\r?\n){2,}#', "\n", $sql);
604
605
		// syntax highlight
606
		$sql = htmlspecialchars($sql, ENT_IGNORE, 'UTF-8');
607
		$sql = preg_replace_callback("#(/\\*.+?\\*/)|(\\*\\*.+?\\*\\*)|(?<=[\\s,(])($keywords1)(?=[\\s,)])|(?<=[\\s,(=])($keywords2)(?=[\\s,)=])#is", function ($matches) {
608
			if (!empty($matches[1])) { // comment
609
				return '<em style="color:gray">' . $matches[1] . '</em>';
610
611
			} elseif (!empty($matches[2])) { // error
612
				return '<strong style="color:red">' . $matches[2] . '</strong>';
613
614
			} elseif (!empty($matches[3])) { // most important keywords
615
				return '<strong style="color:blue">' . $matches[3] . '</strong>';
616
617
			} elseif (!empty($matches[4])) { // other keywords
618
				return '<strong style="color:green">' . $matches[4] . '</strong>';
619
			}
620
		}, $sql);
621
622
		return '<pre class="dump">' . trim($sql) . "</pre>\n";
623
	}
624
625
626
627
	/**
628
	 * @param string $query
629
	 * @param array $params
630
	 * @param array $types
631
	 * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform
632
	 * @throws \Doctrine\DBAL\DBALException
633
	 * @throws \Nette\Utils\RegexpException
634
	 * @return string
635
	 */
636
	public static function formatQuery($query, $params, array $types = [], AbstractPlatform $platform = NULL)
637
	{
638
		if (!$platform) {
639
			$platform = new Doctrine\DBAL\Platforms\MySqlPlatform();
640
		}
641
642
		if (!$types) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $types of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
643
			foreach ($params as $key => $param) {
644
				if (is_array($param)) {
645
					$types[$key] = Doctrine\DBAL\Connection::PARAM_STR_ARRAY;
646
647
				} else {
648
					$types[$key] = 'string';
649
				}
650
			}
651
		}
652
653
		try {
654
			list($query, $params, $types) = \Doctrine\DBAL\SQLParserUtils::expandListParameters($query, $params, $types);
655
		} catch (Doctrine\DBAL\SQLParserUtilsException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
Bug introduced by
The class Doctrine\DBAL\SQLParserUtilsException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
656
		}
657
658
		$formattedParams = [];
659
		foreach ($params as $key => $param) {
660
			if (isset($types[$key])) {
661
				if (is_scalar($types[$key]) && array_key_exists($types[$key], Type::getTypesMap())) {
662
					$types[$key] = Type::getType($types[$key]);
663
				}
664
665
				/** @var Type[] $types */
666
				if ($types[$key] instanceof Type) {
0 ignored issues
show
Bug introduced by
The class Doctrine\DBAL\Types\Type 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...
667
					$param = $types[$key]->convertToDatabaseValue($param, $platform);
668
				}
669
			}
670
671
			$formattedParams[] = SimpleParameterFormatter::format($param);
672
		}
673
		$params = $formattedParams;
674
675
		if (Nette\Utils\Validators::isList($params)) {
676
			$parts = explode('?', $query);
677
			if (count($params) > $parts) {
678
				throw new Kdyby\Doctrine\InvalidStateException("Too mny parameters passed to query.");
679
			}
680
681
			return implode('', Kdyby\Doctrine\Helpers::zipper($parts, $params));
682
		}
683
684
		return Strings::replace($query, '~(\\:[a-z][a-z0-9]*|\\?[0-9]*)~i', function ($m) use (&$params) {
685
			if (substr($m[0], 0, 1) === '?') {
686
				if (strlen($m[0]) > 1) {
687
					if (isset($params[$k = substr($m[0], 1)])) {
688
						return $params[$k];
689
					}
690
691
				} else {
692
					return array_shift($params);
693
				}
694
695
			} else {
696
				if (isset($params[$k = substr($m[0], 1)])) {
697
					return $params[$k];
698
				}
699
			}
700
701
			return $m[0];
702
		});
703
	}
704
705
706
707
	/**
708
	 * @param \Doctrine\Common\Annotations\AnnotationException $e
709
	 * @return string|bool
710
	 */
711
	public static function highlightAnnotationLine(AnnotationException $e)
712
	{
713
		foreach ($e->getTrace() as $step) {
714
			if (@$step['class'] . @$step['type'] . @$step['function'] !== 'Doctrine\Common\Annotations\DocParser->parse') {
715
				continue;
716
			}
717
718
			$context = Strings::match($step['args'][1], '~^(?P<type>[^\s]+)\s*(?P<class>[^:]+)(?:::\$?(?P<property>[^\\(]+))?$~i');
719
			break;
720
		}
721
722
		if (!isset($context)) {
723
			return FALSE;
724
		}
725
726
		$refl = Nette\Reflection\ClassType::from($context['class']);
727
		$file = $refl->getFileName();
728
		$line = NULL;
729
730
		if ($context['type'] === 'property') {
731
			$refl = $refl->getProperty($context['property']);
732
			$line = Kdyby\Doctrine\Helpers::getPropertyLine($refl);
733
734
		} elseif ($context['type'] === 'method') {
735
			$refl = $refl->getProperty($context['method']);
736
		}
737
738
		if (($errorLine = self::calculateErrorLine($refl, $e, $line)) === NULL) {
739
			return FALSE;
740
		}
741
742
		$dump = BlueScreen::highlightFile($file, $errorLine);
743
744
		return '<p><b>File:</b> ' . self::editorLink($file, $errorLine) . '</p>' . $dump;
745
	}
746
747
748
749
	/**
750
	 * @param \Reflector|\Nette\Reflection\ClassType|\Nette\Reflection\Method|\Nette\Reflection\Property $refl
751
	 * @param \Exception|\Throwable $e
752
	 * @param int|NULL $startLine
753
	 * @return int|string|NULL
754
	 */
755
	public static function calculateErrorLine(\Reflector $refl, $e, $startLine = NULL)
756
	{
757
		if ($startLine === NULL && method_exists($refl, 'getStartLine')) {
758
			$startLine = $refl->getStartLine();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Reflector as the method getStartLine() does only exist in the following implementations of said interface: ReflectionClass, ReflectionFunction, ReflectionFunctionAbstract, ReflectionMethod, ReflectionObject.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
759
		}
760
		if ($startLine === NULL) {
761
			return NULL;
762
		}
763
764
		if ($pos = Strings::match($e->getMessage(), '~position\s*(\d+)~')) {
765
			$targetLine = self::calculateAffectedLine($refl, $pos[1]);
766
767
		} elseif ($notImported = Strings::match($e->getMessage(), '~^\[Semantical Error\]\s+The annotation "([^"]*?)"~i')) {
768
			$parts = explode(self::findRenamed($refl, $notImported[1]), self::cleanedPhpDoc($refl), 2);
769
			$targetLine = self::calculateAffectedLine($refl, strlen($parts[0]));
770
771
		} elseif ($notFound = Strings::match($e->getMessage(), '~^\[Semantical Error\]\s+Couldn\'t find\s+(.*?)\s+(.*?),\s+~')) {
772
			// this is just a guess
773
			$parts = explode(self::findRenamed($refl, $notFound[2]), self::cleanedPhpDoc($refl), 2);
774
			$targetLine = self::calculateAffectedLine($refl, strlen($parts[0]));
775
776
		} else {
777
			$targetLine = self::calculateAffectedLine($refl, 1);
778
		}
779
780
		$phpDocLines = count(Strings::split($refl->getDocComment(), '~[\n\r]+~'));
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Reflector as the method getDocComment() does only exist in the following implementations of said interface: ReflectionClass, ReflectionFunction, ReflectionFunctionAbstract, ReflectionMethod, ReflectionObject, ReflectionProperty.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
781
782
		return $startLine - ($phpDocLines - ($targetLine - 1));
783
	}
784
785
786
787
	/**
788
	 * @param \Reflector|\Nette\Reflection\ClassType|\Nette\Reflection\Method $refl
789
	 * @param int $symbolPos
790
	 *
791
	 * @return int
792
	 */
793
	protected static function calculateAffectedLine(\Reflector $refl, $symbolPos)
794
	{
795
		$doc = $refl->getDocComment();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Reflector as the method getDocComment() does only exist in the following implementations of said interface: ReflectionClass, ReflectionFunction, ReflectionFunctionAbstract, ReflectionMethod, ReflectionObject, ReflectionProperty.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
796
		$cleanedDoc = self::cleanedPhpDoc($refl, $atPos);
797
		$beforeCleanLines = count(Strings::split(substr($doc, 0, $atPos), '~[\n\r]+~'));
798
		$parsedDoc = substr($cleanedDoc, 0, $symbolPos + 1);
799
		$parsedLines = count(Strings::split($parsedDoc, '~[\n\r]+~'));
800
801
		return $parsedLines + max($beforeCleanLines - 1, 0);
802
	}
803
804
805
806
	/**
807
	 * @param \Reflector|Nette\Reflection\ClassType|Nette\Reflection\Method $refl
808
	 * @param $annotation
809
	 */
810
	private static function findRenamed(\Reflector $refl, $annotation)
811
	{
812
		$parser = new Doctrine\Common\Annotations\PhpParser();
813
		$imports = $parser->parseClass($refl instanceof \ReflectionClass ? $refl : $refl->getDeclaringClass());
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Reflector as the method getDeclaringClass() does only exist in the following implementations of said interface: ReflectionMethod, ReflectionParameter, ReflectionProperty.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
814
815
		$annotationClass = ltrim($annotation, '@');
816
		foreach ($imports as $alias => $import) {
817
			if (!Strings::startsWith($annotationClass, $import)) {
818
				continue;
819
			}
820
821
			$aliased = str_replace(Strings::lower($import), $alias, Strings::lower($annotationClass));
822
			$searchFor = preg_quote(Strings::lower($aliased));
823
824
			if (!$m = Strings::match($refl->getDocComment(), "~(?P<usage>@?$searchFor)~i")) {
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Reflector as the method getDocComment() does only exist in the following implementations of said interface: ReflectionClass, ReflectionFunction, ReflectionFunctionAbstract, ReflectionMethod, ReflectionObject, ReflectionProperty.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
825
				continue;
826
			}
827
828
			return $m['usage'];
829
		}
830
831
		return $annotation;
832
	}
833
834
835
836
	/**
837
	 * @param \Nette\Reflection\ClassType|\Nette\Reflection\Method|\Reflector $refl
838
	 * @param null $atPos
839
	 *
840
	 * @return string
841
	 */
842
	private static function cleanedPhpDoc(\Reflector $refl, &$atPos = NULL)
843
	{
844
		return trim(substr($doc = $refl->getDocComment(), $atPos = strpos($doc, '@') - 1), '* /');
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Reflector as the method getDocComment() does only exist in the following implementations of said interface: ReflectionClass, ReflectionFunction, ReflectionFunctionAbstract, ReflectionMethod, ReflectionObject, ReflectionProperty.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
845
	}
846
847
848
849
	/**
850
	 * Returns link to editor.
851
	 * @author David Grudl
852
	 * @param string $file
853
	 * @param string $line
854
	 * @param string $text
855
	 * @return Nette\Utils\Html
856
	 */
857
	private static function editorLink($file, $line, $text = NULL)
858
	{
859
		if (Debugger::$editor && is_file($file) && $text !== NULL) {
860
			return Nette\Utils\Html::el('a')
861
				->href(strtr(Debugger::$editor, ['%file' => rawurlencode($file), '%line' => $line]))
862
				->title("$file:$line")
863
				->setHtml($text);
864
865
		} else {
866
			return Helpers::editorLink($file, $line);
867
		}
868
	}
869
870
871
872
	/****************** Registration *********************/
873
874
875
876
	public function enableLogging()
877
	{
878
		if ($this->connection === NULL) {
879
			throw new Kdyby\Doctrine\InvalidStateException("Doctrine Panel is not bound to connection.");
880
		}
881
882
		$config = $this->connection->getConfiguration();
883
		$logger = $config->getSQLLogger();
884
885
		if ($logger instanceof Doctrine\DBAL\Logging\LoggerChain) {
0 ignored issues
show
Bug introduced by
The class Doctrine\DBAL\Logging\LoggerChain 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...
886
			$logger->addLogger($this);
887
888
		} else {
889
			$config->setSQLLogger($this);
890
		}
891
	}
892
893
894
895
	/**
896
	 * @param \Doctrine\DBAL\Connection $connection
897
	 * @return Panel
898
	 */
899
	public function bindConnection(Doctrine\DBAL\Connection $connection)
900
	{
901
		if ($this->connection !== NULL) {
902
			throw new Kdyby\Doctrine\InvalidStateException("Doctrine Panel is already bound to connection.");
903
		}
904
905
		$this->connection = $connection;
906
907
		// Tracy
908
		$this->registerBarPanel(Debugger::getBar());
909
		Debugger::getBlueScreen()->addPanel([$this, 'renderQueryException']);
910
911
		return $this;
912
	}
913
914
915
916
	/**
917
	 * @param Doctrine\ORM\EntityManager $em
918
	 * @return Panel
919
	 */
920
	public function bindEntityManager(Doctrine\ORM\EntityManager $em)
921
	{
922
		if ($this->em !== NULL) {
923
			throw new Kdyby\Doctrine\InvalidStateException("Doctrine Panel is already bound to entity manager.");
924
		}
925
926
		$this->em = $em;
927
		if ($this->em instanceof Kdyby\Doctrine\EntityManager) {
928
			$this->em->bindTracyPanel($this);
929
		}
930
931
		if ($this->connection === NULL) {
932
			$this->bindConnection($em->getConnection());
933
		}
934
935
		Debugger::getBlueScreen()->addPanel([$this, 'renderEntityManagerException']);
936
937
		return $this;
938
	}
939
940
941
942
	/**
943
	 * Registers panel to debugger
944
	 *
945
	 * @param \Tracy\Bar $bar
946
	 */
947
	public function registerBarPanel(Bar $bar)
948
	{
949
		$bar->addPanel($this);
950
	}
951
952
953
954
	/**
955
	 * Registers generic exception renderer
956
	 */
957
	public static function registerBluescreen(Nette\DI\Container $dic)
958
	{
959
		Debugger::getBlueScreen()->addPanel(function ($e) use ($dic) {
960
			return Panel::renderException($e, $dic);
961
		});
962
	}
963
964
}
965