Skip to content

Commit 2ff600f

Browse files
committed
fixed PHPStan errors WIP
1 parent dd4e1f2 commit 2ff600f

23 files changed

+409
-130
lines changed

phpstan.neon

Lines changed: 158 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,162 @@
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+
# Readonly lazy-loading via __get magic
17+
-
18+
identifier: property.uninitializedReadonly
19+
paths:
20+
- src/Database/Reflection.php
21+
- src/Database/Reflection/Table.php
22+
-
23+
identifier: unset.readOnlyProperty
24+
paths:
25+
- src/Database/Reflection.php
26+
- src/Database/Reflection/Table.php
27+
-
28+
identifier: property.readOnlyAssignNotInConstructor
29+
paths:
30+
- src/Database/Reflection.php
31+
- src/Database/Reflection/Table.php
32+
33+
# Deprecated interfaces without generic types
34+
-
35+
identifier: missingType.generics
36+
paths:
37+
- src/Database/IRow.php
38+
- src/Database/IRowContainer.php
39+
40+
# Generic conflicts from deprecated IRowContainer/IRow extending Traversable without type params
41+
-
42+
identifier: generics.interfaceConflict
43+
paths:
44+
- src/Database/ResultSet.php
45+
- src/Database/Row.php
46+
- src/Database/Table/GroupedSelection.php
47+
- src/Database/Table/ActiveRow.php
48+
- src/Database/Table/Selection.php
49+
50+
# Iterator/ArrayAccess covariance/contravariance (PHP interface limitation)
51+
-
52+
identifier: method.childReturnType
53+
paths:
54+
- src/Database/ResultSet.php
55+
- src/Database/Table/Selection.php
56+
-
57+
identifier: method.childParameterType
58+
paths:
59+
- src/Database/Row.php
60+
- src/Database/Table/Selection.php
61+
62+
# Intentional new static() in exception hierarchy
63+
-
64+
identifier: new.static
65+
path: src/Database/DriverException.php
66+
67+
# Closure variables consumed by require'd phtml template
68+
-
69+
identifier: closure.unusedUse
70+
path: src/Bridges/DatabaseTracy/ConnectionPanel.php
71+
72+
# Lazy-loading side effect via __get magic
73+
-
74+
identifier: expr.resultUnused
75+
path: src/Database/Reflection.php
76+
77+
# DI extension: $this->config is array|object from Nette Schema
78+
-
79+
identifier: foreach.nonIterable
80+
path: src/Bridges/DatabaseDI/DatabaseExtension.php
81+
82+
# PDOException::$queryString is set by PDO engine, not formally declared
83+
-
84+
identifier: property.notFound
85+
path: src/Bridges/DatabaseTracy/ConnectionPanel.php
86+
87+
# Dynamic callable construction: "is_$type"($value) where $type is always valid
88+
-
89+
identifier: callable.nonCallable
90+
path: src/Database/SqlPreprocessor.php
91+
92+
# Defensive runtime checks unreachable per @param type
93+
-
94+
identifier: instanceof.alwaysTrue
95+
path: src/Database/SqlPreprocessor.php
96+
-
97+
identifier: booleanAnd.alwaysFalse
98+
path: src/Database/SqlPreprocessor.php
99+
100+
# getPrimary() returns string for single-column PK (composite PK not supported here)
101+
-
102+
identifier: argument.type
103+
path: src/Database/Table/ActiveRow.php
104+
-
105+
identifier: array.invalidKey
106+
path: src/Database/Table/ActiveRow.php
107+
108+
# Internal SQL built from trusted components, generic covariance, getPrimary() type unions
109+
-
110+
identifier: argument.type
111+
paths:
112+
- src/Database/Table/Selection.php
113+
- src/Database/Table/GroupedSelection.php
114+
- src/Database/Table/SqlBuilder.php
115+
116+
# Generic T vs concrete ActiveRow assignments
117+
-
118+
identifier: assign.propertyType
119+
path: src/Database/Table/Selection.php
120+
121+
# Return type mismatches from generic covariance and internal caching
122+
-
123+
identifier: return.type
124+
paths:
125+
- src/Database/ResultSet.php
126+
- src/Database/Structure.php
127+
- src/Database/Table/Selection.php
128+
- src/Database/Table/GroupedSelection.php
129+
130+
# Array offset access on Row/ActiveRow objects and nullable arrays
131+
-
132+
identifier: offsetAccess.notFound
133+
paths:
134+
- src/Database/Helpers.php
135+
- src/Database/Table/Selection.php
136+
- src/Database/Table/SqlBuilder.php
137+
138+
# Defensive checks that are always true/false per PHPStan type narrowing
139+
-
140+
identifier: notIdentical.alwaysTrue
141+
path: src/Database/Helpers.php
142+
-
143+
identifier: isset.offset
144+
path: src/Database/Helpers.php
145+
-
146+
identifier: identical.alwaysFalse
147+
path: src/Database/Table/GroupedSelection.php
148+
149+
# Dynamic string concatenation in binary operations
150+
-
151+
identifier: binaryOp.invalid
152+
path: src/Database/Table/Selection.php
153+
154+
# isList() on iterable that could be array or Selection
155+
-
156+
identifier: staticMethod.impossibleType
157+
path: src/Database/Table/GroupedSelection.php
158+
159+
# Setting column value on iterable data
160+
-
161+
identifier: offsetAssign.valueType
162+
path: src/Database/Table/GroupedSelection.php

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: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,10 @@ private function logQuery(Connection $connection, $result): void
7777
: debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
7878

7979
foreach ($trace as $row) {
80-
if (preg_match('~\.(php.?|phtml)$~', $row['file'] ?? '') && !$this->blueScreen->isCollapsed($row['file'])) {
80+
if (is_string($row['file'] ?? null)
81+
&& preg_match('~\.(php.?|phtml)$~', $row['file'])
82+
&& !$this->blueScreen->isCollapsed($row['file'])
83+
) {
8184
break;
8285
}
8386
array_shift($trace);
@@ -88,7 +91,7 @@ private function logQuery(Connection $connection, $result): void
8891
if ($this->count < $this->maxQueries) {
8992
$this->queries[] = [$connection, $result->getQueryString(), $result->getParameters(), $trace, $result->getTime(), $result->getRowCount(), null];
9093
}
91-
} elseif ($result instanceof \PDOException && $this->count < $this->maxQueries) {
94+
} elseif ($this->count < $this->maxQueries) {
9295
$this->queries[] = [$connection, $result->queryString, null, $trace, null, null, $result->getMessage()];
9396
}
9497
}
@@ -133,6 +136,7 @@ public function getPanel(): ?string
133136
}
134137

135138
$queries = [];
139+
$connection = null;
136140
foreach ($this->queries as $query) {
137141
[$connection, $sql, $params, , , , $error] = $query;
138142
$explain = null;
@@ -144,7 +148,7 @@ public function getPanel(): ?string
144148
$cmd = is_string($this->explain)
145149
? $this->explain
146150
: 'EXPLAIN';
147-
$explain = (new Nette\Database\ResultSet($connection, "$cmd $sql", $params))->fetchAll();
151+
$explain = (new Nette\Database\ResultSet($connection, "$cmd $sql", $params ?? []))->fetchAll();
148152
} catch (\PDOException) {
149153
}
150154
}

src/Bridges/DatabaseTracy/dist/panel.phtml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<?php declare(strict_types=1); ?>
2-
<style class="tracy-debug">
2+
<?php /** @var Nette\Database\Connection $connection */ ?><?php /** @var list<array> $queries */ ?><?php /** @var string $name */ ?><?php /** @var int $count */ ?><?php /** @var float $totalTime */ ?><?php /** @var float $performanceScale */ ?><style class="tracy-debug">
33
#tracy-debug td.nette-DbConnectionPanel-sql { background: white !important; overflow-x: auto; max-width: 0; }
44
#tracy-debug .nette-DbConnectionPanel-source { color: #BBB !important }
55
#tracy-debug .nette-DbConnectionPanel-explain td { white-space: pre }

src/Bridges/DatabaseTracy/dist/tab.phtml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<?php declare(strict_types=1); ?>
2-
<span title="Nette\Database <?= Tracy\Helpers::escapeHtml($name) ?>
2+
<?php /** @var string $name */ ?><?php /** @var int $count */ ?><?php /** @var float $totalTime */ ?><span title="Nette\Database <?= Tracy\Helpers::escapeHtml($name) ?>
33
">
44
<svg viewBox="0 0 2048 2048">
55
<path fill="<?= Tracy\Helpers::escapeHtml($count ? '#b079d6' : '#aaa') ?>

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"/>

src/Database/Connection.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,9 @@ public function connect(): void
7070
$class = empty($this->options['driverClass'])
7171
? 'Nette\Database\Drivers\\' . ucfirst(str_replace('sql', 'Sql', $this->pdo->getAttribute(PDO::ATTR_DRIVER_NAME))) . 'Driver'
7272
: $this->options['driverClass'];
73-
$this->driver = new $class;
73+
$driver = new $class;
74+
\assert($driver instanceof Driver);
75+
$this->driver = $driver;
7476
$this->preprocessor = new SqlPreprocessor($this);
7577
$this->driver->initialize($this, $this->options);
7678
Arrays::invoke($this->onConnect, $this);
@@ -105,6 +107,7 @@ public function getDsn(): string
105107
public function getPdo(): PDO
106108
{
107109
$this->connect();
110+
assert($this->pdo !== null);
108111
return $this->pdo;
109112
}
110113

src/Database/Conventions/DiscoveredConventions.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
use Nette\Database\Conventions;
1111
use Nette\Database\IStructure;
12-
use function count, preg_replace, strcmp, stripos, strtolower;
12+
use function array_values, count, is_array, preg_replace, strcmp, stripos, strtolower;
1313

1414

1515
/**
@@ -25,14 +25,15 @@ public function __construct(
2525

2626
public function getPrimary(string $table): string|array|null
2727
{
28-
return $this->structure->getPrimaryKey($table);
28+
$result = $this->structure->getPrimaryKey($table);
29+
return is_array($result) ? array_values($result) : $result;
2930
}
3031

3132

3233
public function getHasManyReference(string $nsTable, string $key): ?array
3334
{
3435
$candidates = $columnCandidates = [];
35-
$targets = $this->structure->getHasManyReference($nsTable);
36+
$targets = $this->structure->getHasManyReference($nsTable) ?? [];
3637
$table = preg_replace('#^(.*\.)?(.*)$#', '$2', $nsTable);
3738

3839
foreach ($targets as $targetNsTable => $targetColumns) {
@@ -80,7 +81,7 @@ public function getHasManyReference(string $nsTable, string $key): ?array
8081

8182
public function getBelongsToReference(string $table, string $key): ?array
8283
{
83-
$tableColumns = $this->structure->getBelongsToReference($table);
84+
$tableColumns = $this->structure->getBelongsToReference($table) ?? [];
8485

8586
foreach ($tableColumns as $column => $targetTable) {
8687
if (stripos($column, $key) !== false) {

src/Database/Drivers/MsSqlDriver.php

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,10 @@ public function getTables(): array
105105

106106
while ($row = $rows->fetch()) {
107107
$tables[] = [
108-
'name' => $row['TABLE_SCHEMA'] . '.' . $row['TABLE_NAME'],
108+
'name' => (string) $row['TABLE_SCHEMA'] . '.' . (string) $row['TABLE_NAME'],
109+
'fullName' => null,
109110
'view' => ($row['TABLE_TYPE'] ?? null) === 'VIEW',
110-
'comment' => $row['comment'] ?? '',
111+
'comment' => (string) ($row['comment'] ?? ''),
111112
];
112113
}
113114

@@ -185,13 +186,23 @@ public function getIndexes(string $table): array
185186
X, $table_name);
186187

187188
while ($row = $rows->fetch()) {
188-
$id = $row['name_index'];
189-
$indexes[$id]['name'] = $id;
190-
$indexes[$id]['unique'] = $row['is_unique'] !== 'False';
191-
$indexes[$id]['primary'] = $row['is_primary_key'] !== 'False';
189+
$id = (string) $row['name_index'];
190+
if (!isset($indexes[$id])) {
191+
$indexes[$id] = [
192+
'name' => $id,
193+
'unique' => $row['is_unique'] !== 'False',
194+
'primary' => $row['is_primary_key'] !== 'False',
195+
'columns' => [],
196+
];
197+
}
198+
192199
$indexes[$id]['columns'][$row['id_column'] - 1] = $row['name_column'];
193200
}
194201

202+
foreach ($indexes as &$index) {
203+
$index['columns'] = array_values($index['columns']);
204+
}
205+
195206
return array_values($indexes);
196207
}
197208

@@ -225,15 +236,16 @@ public function getForeignKeys(string $table): array
225236
tab1.name = ?
226237
X, $table_name);
227238

228-
$id = 0;
229239
while ($row = $rows->fetch()) {
230-
$keys[$id]['name'] = $row['fk_name'];
231-
$keys[$id]['local'] = $row['column'];
232-
$keys[$id]['table'] = $table_schema . '.' . $row['referenced_table'];
233-
$keys[$id++]['foreign'] = $row['referenced_column'];
240+
$keys[] = [
241+
'name' => (string) $row['fk_name'],
242+
'local' => (string) $row['column'],
243+
'table' => $table_schema . '.' . (string) $row['referenced_table'],
244+
'foreign' => (string) $row['referenced_column'],
245+
];
234246
}
235247

236-
return array_values($keys);
248+
return $keys;
237249
}
238250

239251

0 commit comments

Comments
 (0)