Skip to content

Commit dcb43d3

Browse files
vaielabEtienne V. Labellestaabm
authored
fix: support PDOStatement subclasses in PdoStatementExecuteMethodRule (#773)
Co-authored-by: Etienne V. Labelle <evaillancourt@vaielab.com> Co-authored-by: Markus Staab <markus.staab@redaxo.de>
1 parent 8cdd7b8 commit dcb43d3

File tree

4 files changed

+63
-3
lines changed

4 files changed

+63
-3
lines changed

src/Rules/PdoStatementExecuteMethodRule.php

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use PHPStan\Type\Constant\ConstantArrayType;
1616
use PHPStan\Type\Constant\ConstantIntegerType;
1717
use PHPStan\Type\Constant\ConstantStringType;
18+
use PHPStan\Type\ObjectType;
1819
use staabm\PHPStanDba\PdoReflection\PdoStatementReflection;
1920
use staabm\PHPStanDba\QueryReflection\PlaceholderValidation;
2021
use staabm\PHPStanDba\QueryReflection\QueryReflection;
@@ -38,16 +39,17 @@ public function processNode(Node $methodCall, Scope $scope): array
3839
return [];
3940
}
4041

41-
$methodReflection = $scope->getMethodReflection($scope->getType($methodCall->var), $methodCall->name->toString());
42+
$varType = $scope->getType($methodCall->var);
43+
$methodReflection = $scope->getMethodReflection($varType, $methodCall->name->toString());
4244
if (null === $methodReflection) {
4345
return [];
4446
}
4547

46-
if (PDOStatement::class !== $methodReflection->getDeclaringClass()->getName()) {
48+
if ('execute' !== strtolower($methodReflection->getName())) {
4749
return [];
4850
}
4951

50-
if ('execute' !== strtolower($methodReflection->getName())) {
52+
if (! (new ObjectType(PDOStatement::class))->isSuperTypeOf($varType)->yes()) {
5153
return [];
5254
}
5355

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace staabm\PHPStanDba\Tests\Fixture;
6+
7+
class MyPdoStatement extends \PDOStatement
8+
{
9+
public function execute(?array $params = null): bool
10+
{
11+
return parent::execute($params);
12+
}
13+
}

tests/rules/PdoStatementExecuteMethodRuleTest.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,4 +131,22 @@ public function testPlaceholderBug(): void
131131
],
132132
]);
133133
}
134+
135+
public function testSubclassedPdoStatement(): void
136+
{
137+
$this->analyse([__DIR__ . '/data/pdo-stmt-execute-subclassed.php'], [
138+
[
139+
'Query expects placeholder :adaid, but it is missing from values given.',
140+
14,
141+
],
142+
[
143+
'Value :wrongParamName is given, but the query does not contain this placeholder.',
144+
14,
145+
],
146+
[
147+
'Query expects placeholder :adaid, but it is missing from values given.',
148+
18,
149+
],
150+
]);
151+
}
134152
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
namespace PdoStatementExecuteSubclassedMethodRuleTest;
4+
5+
use PDO;
6+
use staabm\PHPStanDba\Tests\Fixture\MyPdoStatement;
7+
8+
class Foo
9+
{
10+
public function errors(PDO $pdo): void
11+
{
12+
$stmt = $pdo->prepare('SELECT email, adaid FROM ada WHERE adaid = :adaid');
13+
assert($stmt instanceof MyPdoStatement);
14+
$stmt->execute(['wrongParamName' => 1]); // error: wrong param name
15+
16+
$stmt = $pdo->prepare('SELECT email, adaid FROM ada WHERE adaid = :adaid');
17+
assert($stmt instanceof MyPdoStatement);
18+
$stmt->execute(); // error: missing parameter
19+
}
20+
21+
public function noErrors(PDO $pdo): void
22+
{
23+
$stmt = $pdo->prepare('SELECT email, adaid FROM ada WHERE adaid = :adaid');
24+
assert($stmt instanceof MyPdoStatement);
25+
$stmt->execute(['adaid' => 1]); // correct
26+
}
27+
}

0 commit comments

Comments
 (0)