Skip to content

Commit fe48f9b

Browse files
committed
fixed PHPStan errors
- Selection<T of ActiveRow>, GroupedSelection<T>: generic type tracking across fetch/insert/offsetGet/iterator methods - replaced assert() with ?? throw ShouldNotHappenException for unreachable-in-practice branches - narrowed phpstan.neon ignoreErrors to truly systematic patterns
1 parent e71ac3b commit fe48f9b

16 files changed

Lines changed: 341 additions & 112 deletions

phpstan.neon

Lines changed: 145 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,149 @@
11
parameters:
2-
level: 5
2+
level: 8
33

44
paths:
55
- src
6+
7+
excludePaths:
8+
- src/compatibility.php
9+
- src/compatibility-intf.php
10+
11+
fileExtensions:
12+
- php
13+
- phtml
14+
15+
ignoreErrors:
16+
# $refPath is populated via @param-out by getRefTable() — PHPStan doesn't track this
17+
-
18+
identifier: variable.undefined
19+
path: src/Database/Table/Selection.php
20+
21+
# Readonly lazy-loading via __get magic
22+
-
23+
identifier: property.uninitializedReadonly
24+
paths:
25+
- src/Database/Reflection.php
26+
- src/Database/Reflection/Table.php
27+
-
28+
identifier: unset.readOnlyProperty
29+
paths:
30+
- src/Database/Reflection.php
31+
- src/Database/Reflection/Table.php
32+
-
33+
identifier: property.readOnlyAssignNotInConstructor
34+
paths:
35+
- src/Database/Reflection.php
36+
- src/Database/Reflection/Table.php
37+
38+
# Deprecated interfaces without generic types
39+
-
40+
identifier: missingType.generics
41+
paths:
42+
- src/Database/IRow.php
43+
- src/Database/IRowContainer.php
44+
45+
# Generic conflicts from deprecated IRowContainer/IRow extending Traversable without type params
46+
-
47+
identifier: generics.interfaceConflict
48+
paths:
49+
- src/Database/ResultSet.php
50+
- src/Database/Row.php
51+
- src/Database/Table/GroupedSelection.php
52+
- src/Database/Table/ActiveRow.php
53+
- src/Database/Table/Selection.php
54+
55+
# Iterator/ArrayAccess covariance/contravariance (PHP interface limitation)
56+
-
57+
identifier: method.childReturnType
58+
paths:
59+
- src/Database/ResultSet.php
60+
- src/Database/Table/Selection.php
61+
-
62+
identifier: method.childParameterType
63+
paths:
64+
- src/Database/Row.php
65+
- src/Database/Table/Selection.php
66+
67+
# Intentional new static() in exception hierarchy
68+
-
69+
identifier: new.static
70+
path: src/Database/DriverException.php
71+
72+
# Closure variables consumed by require'd phtml template
73+
-
74+
identifier: closure.unusedUse
75+
path: src/Bridges/DatabaseTracy/ConnectionPanel.php
76+
77+
# Defensive instanceof check in elseif branch for readability
78+
-
79+
identifier: instanceof.alwaysTrue
80+
path: src/Bridges/DatabaseTracy/ConnectionPanel.php
81+
82+
# Lazy-loading side effect via __get magic
83+
-
84+
identifier: expr.resultUnused
85+
path: src/Database/Reflection.php
86+
87+
# DI extension: $this->config is array|object from Nette Schema
88+
-
89+
identifier: foreach.nonIterable
90+
path: src/Bridges/DatabaseDI/DatabaseExtension.php
91+
92+
# PDOException::$queryString is set by PDO engine, not formally declared
93+
-
94+
identifier: property.notFound
95+
path: src/Bridges/DatabaseTracy/ConnectionPanel.php
96+
97+
# Dynamic callable construction: "is_$type"($value) where $type is always valid
98+
-
99+
identifier: callable.nonCallable
100+
path: src/Database/SqlPreprocessor.php
101+
102+
# Defensive runtime checks unreachable per @param type
103+
-
104+
identifier: instanceof.alwaysTrue
105+
path: src/Database/SqlPreprocessor.php
106+
-
107+
identifier: booleanAnd.alwaysFalse
108+
path: src/Database/SqlPreprocessor.php
109+
110+
# getPrimary() returns string for single-column PK (composite PK not supported here)
111+
-
112+
identifier: argument.type
113+
path: src/Database/Table/ActiveRow.php
114+
-
115+
identifier: array.invalidKey
116+
path: src/Database/Table/ActiveRow.php
117+
118+
# Return type mismatches from generic covariance and internal caching
119+
-
120+
identifier: return.type
121+
paths:
122+
- src/Database/ResultSet.php
123+
- src/Database/Structure.php
124+
125+
# Internal SQL is dynamically assembled from trusted components, not literal-string
126+
-
127+
message: '#expects literal-string, .+ given#'
128+
paths:
129+
- src/Database/Table/Selection.php
130+
- src/Database/Table/GroupedSelection.php
131+
132+
# Array offset access on Row/ActiveRow objects and nullable arrays
133+
-
134+
identifier: offsetAccess.notFound
135+
path: src/Database/Helpers.php
136+
137+
# Defensive checks that are always true/false per PHPStan type narrowing
138+
-
139+
identifier: empty.offset
140+
path: src/Database/Helpers.php
141+
-
142+
identifier: isset.offset
143+
path: src/Database/Helpers.php
144+
# Latte-generated n:attr idiom: ($tmp = expr) === null ? '' : ...
145+
-
146+
identifier: identical.alwaysFalse
147+
paths:
148+
- src/Bridges/DatabaseTracy/dist/panel.phtml
149+
- src/Bridges/DatabaseTracy/dist/tab.phtml

src/Bridges/DatabaseDI/DatabaseExtension.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ public function beforeCompile(): void
6262
foreach ($this->config as $name => $config) {
6363
if ($config->debugger ?? $builder->getByType(Tracy\BlueScreen::class)) {
6464
$connection = $builder->getDefinition($this->prefix("$name.connection"));
65+
assert($connection instanceof Nette\DI\Definitions\ServiceDefinition);
6566
$connection->addSetup(
6667
[Nette\Bridges\DatabaseTracy\ConnectionPanel::class, 'initialize'],
6768
[$connection, $this->debugMode, $name, !empty($config->explain)],

src/Bridges/DatabaseTracy/ConnectionPanel.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ public static function renderException(?\Throwable $e): ?array
106106
if (isset($e->queryString)) {
107107
$sql = $e->queryString;
108108

109-
} elseif ($item = Tracy\Helpers::findTrace($e->getTrace(), 'PDO::prepare')) {
109+
} elseif (($item = Tracy\Helpers::findTrace($e->getTrace(), 'PDO::prepare')) && isset($item['args'][0])) {
110110
$sql = $item['args'][0];
111111
}
112112

@@ -135,6 +135,7 @@ public function getPanel(): ?string
135135
}
136136

137137
$queries = [];
138+
$connection = null;
138139
foreach ($this->queries as $query) {
139140
[$connection, $sql, $params, , , , $error] = $query;
140141
$explain = null;
@@ -146,7 +147,7 @@ public function getPanel(): ?string
146147
$cmd = is_string($this->explain)
147148
? $this->explain
148149
: 'EXPLAIN';
149-
$explain = (new Nette\Database\ResultSet($connection, "$cmd $sql", $params))->fetchAll();
150+
$explain = (new Nette\Database\ResultSet($connection, "$cmd $sql", $params ?? []))->fetchAll();
150151
} catch (\PDOException) {
151152
}
152153
}

src/Bridges/DatabaseTracy/dist/panel.phtml

Lines changed: 36 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
<?php declare(strict_types=1);
22

3+
/** @var Nette\Database\Connection $connection */
4+
/** @var list<array> $queries */
5+
/** @var string $name */
6+
/** @var int $count */
7+
/** @var float $totalTime */
8+
/** @var float $performanceScale */
39
echo '<style class="tracy-debug">
410
#tracy-debug td.nette-DbConnectionPanel-sql { background: white !important; overflow-x: auto; max-width: 0; }
511
#tracy-debug .nette-DbConnectionPanel-source { color: #BBB !important }
@@ -10,12 +16,12 @@ echo '<style class="tracy-debug">
1016
</style>
1117
1218
<h1';
13-
echo ($ʟ_tmp = ($connection->getDsn())) === null ? '' : ' title="' . Tracy\Helpers::escapeHtml($ʟ_tmp) . '"' /* pos 10:13 */;
19+
echo ($ʟ_tmp = ($connection->getDsn())) === null ? '' : ' title="' . Tracy\Helpers::escapeHtml($ʟ_tmp) . '"' /* pos 16:13 */;
1420
echo '>Queries: ';
15-
echo Tracy\Helpers::escapeHtml($count) /* pos 10:46 */;
16-
echo Tracy\Helpers::escapeHtml($totalTime ? sprintf(', time: %0.3f ms', $totalTime * 1000) : '') /* pos 10:54 */;
21+
echo Tracy\Helpers::escapeHtml($count) /* pos 16:46 */;
22+
echo Tracy\Helpers::escapeHtml($totalTime ? sprintf(', time: %0.3f ms', $totalTime * 1000) : '') /* pos 16:54 */;
1723
echo ', ';
18-
echo Tracy\Helpers::escapeHtml($name) /* pos 10:122 */;
24+
echo Tracy\Helpers::escapeHtml($name) /* pos 16:122 */;
1925
echo '</h1>
2026
2127
<div class="tracy-inner" style="min-width: 600px">
@@ -27,63 +33,63 @@ echo '</h1>
2733
</tr>
2834
2935
';
30-
foreach ($queries as [$connection, $sql, $params, $trace, $time, $rows, $error, $command, $explain]) /* pos 20:3 */ {
36+
foreach ($queries as [$connection, $sql, $params, $trace, $time, $rows, $error, $command, $explain]) /* pos 26:3 */ {
3137
echo ' <tr>
3238
<td style="background:rgba(255, 95, 23, ';
33-
echo Tracy\Helpers::escapeHtml(sprintf('%0.3f', log($time * 1000 + 1, 10) * $performanceScale)) /* pos 22:45 */;
39+
echo Tracy\Helpers::escapeHtml(sprintf('%0.3f', log($time * 1000 + 1, 10) * $performanceScale)) /* pos 28:45 */;
3440
echo ')"';
35-
echo ($ʟ_tmp = ((float) $time)) === null ? '' : ' data-order="' . Tracy\Helpers::escapeHtml($ʟ_tmp) . '"' /* pos 22:127 */;
41+
echo ($ʟ_tmp = ((float) $time)) === null ? '' : ' data-order="' . Tracy\Helpers::escapeHtml($ʟ_tmp) . '"' /* pos 28:127 */;
3642
echo '>
3743
';
38-
if ($error) /* pos 23:6 */ {
44+
if ($error) /* pos 29:6 */ {
3945
echo ' <span';
40-
echo ($ʟ_tmp = ($error)) === null ? '' : ' title="' . Tracy\Helpers::escapeHtml($ʟ_tmp) . '"' /* pos 24:21 */;
46+
echo ($ʟ_tmp = ($error)) === null ? '' : ' title="' . Tracy\Helpers::escapeHtml($ʟ_tmp) . '"' /* pos 30:21 */;
4147
echo '>ERROR</span>
4248
';
43-
} elseif ($time !== null) /* pos 25:6 */ {
44-
echo Tracy\Helpers::escapeHtml(sprintf('%0.3f', $time * 1000)) /* pos 25:29 */;
49+
} elseif ($time !== null) /* pos 31:6 */ {
50+
echo Tracy\Helpers::escapeHtml(sprintf('%0.3f', $time * 1000)) /* pos 31:29 */;
4551
echo "\n";
4652
}
4753

4854
echo "\n";
49-
if ($explain) /* pos 28:6 */ {
55+
if ($explain) /* pos 34:6 */ {
5056
echo ' <br><a class="tracy-toggle tracy-collapsed" data-tracy-ref="^tr .nette-DbConnectionPanel-explain">explain</a>';
5157
}
5258
echo "\n";
53-
if ($trace) /* pos 29:6 */ {
59+
if ($trace) /* pos 35:6 */ {
5460
echo ' <br><a class="tracy-toggle tracy-collapsed" data-tracy-ref="^tr .nette-DbConnectionPanel-trace">trace</a>';
5561
}
5662
echo '
5763
</td>
5864
5965
<td class="nette-DbConnectionPanel-sql nette-DbConnectionPanel-sql-';
60-
echo Tracy\Helpers::escapeHtml($command) /* pos 32:72 */;
66+
echo Tracy\Helpers::escapeHtml($command) /* pos 38:72 */;
6167
echo '">
6268
';
63-
echo Nette\Database\Helpers::dumpSql($sql, $params, $connection) /* pos 33:6 */;
69+
echo Nette\Database\Helpers::dumpSql($sql, $params, $connection) /* pos 39:6 */;
6470
echo '
6571
6672
';
67-
if ($explain) /* pos 35:6 */ {
73+
if ($explain) /* pos 41:6 */ {
6874
echo ' <table class="tracy-collapsed nette-DbConnectionPanel-explain">
6975
<tr>
7076
';
71-
foreach ($explain[0] as $col => $foo) /* pos 38:9 */ {
77+
foreach ($explain[0] as $col => $foo) /* pos 44:9 */ {
7278
echo ' <th>';
73-
echo Tracy\Helpers::escapeHtml($col) /* pos 39:14 */;
79+
echo Tracy\Helpers::escapeHtml($col) /* pos 45:14 */;
7480
echo '</th>
7581
';
7682

7783
}
7884

7985
echo ' </tr>
8086
';
81-
foreach ($explain as $row) /* pos 42:8 */ {
87+
foreach ($explain as $row) /* pos 48:8 */ {
8288
echo ' <tr>
8389
';
84-
foreach ($row as $col) /* pos 44:10 */ {
90+
foreach ($row as $col) /* pos 50:10 */ {
8591
echo ' <td>';
86-
echo Tracy\Helpers::escapeHtml($col) /* pos 45:15 */;
92+
echo Tracy\Helpers::escapeHtml($col) /* pos 51:15 */;
8793
echo '</td>
8894
';
8995

@@ -98,21 +104,21 @@ foreach ($queries as [$connection, $sql, $params, $trace, $time, $rows, $error,
98104
';
99105
}
100106
echo "\n";
101-
if ($trace) /* pos 52:6 */ {
107+
if ($trace) /* pos 58:6 */ {
102108
echo ' ';
103-
echo substr_replace(Tracy\Helpers::editorLink($trace[0]['file'], $trace[0]['line']), ' class="nette-DbConnectionPanel-source"', 2, 0) /* pos 53:7 */;
109+
echo substr_replace(Tracy\Helpers::editorLink($trace[0]['file'], $trace[0]['line']), ' class="nette-DbConnectionPanel-source"', 2, 0) /* pos 59:7 */;
104110
echo '
105111
<table class="tracy-collapsed nette-DbConnectionPanel-trace">
106112
';
107-
foreach ($trace as $row) /* pos 55:8 */ {
113+
foreach ($trace as $row) /* pos 61:8 */ {
108114
echo ' <tr>
109115
<td>';
110-
echo isset($row['file']) ? Tracy\Helpers::editorLink($row['file'], $row['line']) : '' /* pos 57:14 */;
116+
echo isset($row['file']) ? Tracy\Helpers::editorLink($row['file'], $row['line']) : '' /* pos 63:14 */;
111117
echo '</td>
112118
<td>';
113-
echo Tracy\Helpers::escapeHtml($row['class'] ?? '') /* pos 58:14 */;
114-
echo Tracy\Helpers::escapeHtml($row['type'] ?? '') /* pos 58:33 */;
115-
echo Tracy\Helpers::escapeHtml($row['function']) /* pos 58:51 */;
119+
echo Tracy\Helpers::escapeHtml($row['class'] ?? '') /* pos 64:14 */;
120+
echo Tracy\Helpers::escapeHtml($row['type'] ?? '') /* pos 64:33 */;
121+
echo Tracy\Helpers::escapeHtml($row['function']) /* pos 64:51 */;
116122
echo '()</td>
117123
</tr>
118124
';
@@ -125,7 +131,7 @@ foreach ($queries as [$connection, $sql, $params, $trace, $time, $rows, $error,
125131
echo ' </td>
126132
127133
<td>';
128-
echo Tracy\Helpers::escapeHtml($rows) /* pos 65:9 */;
134+
echo Tracy\Helpers::escapeHtml($rows) /* pos 71:9 */;
129135
echo '</td>
130136
</tr>
131137
';
@@ -135,7 +141,7 @@ foreach ($queries as [$connection, $sql, $params, $trace, $time, $rows, $error,
135141
echo ' </table>
136142
137143
';
138-
if (count($queries) < $count) /* pos 70:2 */ {
144+
if (count($queries) < $count) /* pos 76:2 */ {
139145
echo ' <p>...and more</p>';
140146
}
141147
echo '
Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
<?php declare(strict_types=1);
22

3+
/** @var string $name */
4+
/** @var int $count */
5+
/** @var float $totalTime */
36
echo '<span title="Nette\\Database ';
4-
echo Tracy\Helpers::escapeHtml($name) /* pos 1:29 */;
7+
echo Tracy\Helpers::escapeHtml($name) /* pos 4:29 */;
58
echo '">
69
<svg viewBox="0 0 2048 2048">
710
<path';
8-
echo ($ʟ_tmp = ($count ? '#b079d6' : '#aaa')) === null ? '' : ' fill="' . Tracy\Helpers::escapeHtml($ʟ_tmp) . '"' /* pos 3:16 */;
11+
echo ($ʟ_tmp = ($count ? '#b079d6' : '#aaa')) === null ? '' : ' fill="' . Tracy\Helpers::escapeHtml($ʟ_tmp) . '"' /* pos 6:16 */;
912
echo ' 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>
1013
</svg><span class="tracy-label">';
11-
echo Tracy\Helpers::escapeHtml($totalTime ? sprintf('%0.1f ms / ', $totalTime * 1000) : '') /* pos 5:29 */;
12-
echo Tracy\Helpers::escapeHtml($count) /* pos 5:96 */;
14+
echo Tracy\Helpers::escapeHtml($totalTime ? sprintf('%0.1f ms / ', $totalTime * 1000) : '') /* pos 8:29 */;
15+
echo Tracy\Helpers::escapeHtml($count) /* pos 8:96 */;
1316
echo '</span>
1417
</span>
1518
';

src/Bridges/DatabaseTracy/panel.latte

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
{varType Nette\Database\Connection $connection}
2+
{varType list<array> $queries}
3+
{varType string $name}
4+
{varType int $count}
5+
{varType float $totalTime}
6+
{varType float $performanceScale}
17
<style class="tracy-debug">
28
#tracy-debug td.nette-DbConnectionPanel-sql { background: white !important; overflow-x: auto; max-width: 0; }
39
#tracy-debug .nette-DbConnectionPanel-source { color: #BBB !important }

src/Bridges/DatabaseTracy/tab.latte

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
{varType string $name}
2+
{varType int $count}
3+
{varType float $totalTime}
14
<span title="Nette\Database {$name}">
25
<svg viewBox="0 0 2048 2048">
36
<path fill="{$count ? '#b079d6' : '#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"/>

0 commit comments

Comments
 (0)