Skip to content

Commit a8253f4

Browse files
staabmclxmstaab
andauthored
support conditional errors in PDOStatement->execute() analysis (#138)
Co-authored-by: Markus Staab <m.staab@complex-it.de>
1 parent a2f4995 commit a8253f4

File tree

4 files changed

+232
-8
lines changed

4 files changed

+232
-8
lines changed

.phpstan-dba.cache

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3377,6 +3377,30 @@ Simulated query: SELECT email, adaid, gesperrt, freigabe1u1 FROM ada . WHERE ema
33773377
)),
33783378
),
33793379
),
3380+
'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada WHERE adaid = :adaid' =>
3381+
array (
3382+
'error' =>
3383+
staabm\PHPStanDba\Error::__set_state(array(
3384+
'message' => 'You have an error in your SQL syntax; check the manual that corresponds to your MySQL/MariaDB server version for the right syntax to use near \':adaid LIMIT 0\' at line 1',
3385+
'code' => 1064,
3386+
)),
3387+
'result' =>
3388+
array (
3389+
3 => NULL,
3390+
),
3391+
),
3392+
'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada WHERE adaid = :asdsa' =>
3393+
array (
3394+
'error' =>
3395+
staabm\PHPStanDba\Error::__set_state(array(
3396+
'message' => 'You have an error in your SQL syntax; check the manual that corresponds to your MySQL/MariaDB server version for the right syntax to use near \':asdsa LIMIT 0\' at line 1',
3397+
'code' => 1064,
3398+
)),
3399+
'result' =>
3400+
array (
3401+
3 => NULL,
3402+
),
3403+
),
33803404
'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada WHERE adaid = ?' =>
33813405
array (
33823406
'error' =>
@@ -3512,6 +3536,30 @@ Simulated query: SELECT email, adaid, gesperrt, freigabe1u1 FROM ada . WHERE ema
35123536
3 => NULL,
35133537
),
35143538
),
3539+
'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada WHERE asdsa=\'1\'' =>
3540+
array (
3541+
'error' =>
3542+
staabm\PHPStanDba\Error::__set_state(array(
3543+
'message' => 'Unknown column \'asdsa\' in \'where clause\'',
3544+
'code' => 1054,
3545+
)),
3546+
'result' =>
3547+
array (
3548+
3 => NULL,
3549+
),
3550+
),
3551+
'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada WHERE asdsa=?' =>
3552+
array (
3553+
'error' =>
3554+
staabm\PHPStanDba\Error::__set_state(array(
3555+
'message' => 'You have an error in your SQL syntax; check the manual that corresponds to your MySQL/MariaDB server version for the right syntax to use near \'? LIMIT 0\' at line 1',
3556+
'code' => 1064,
3557+
)),
3558+
'result' =>
3559+
array (
3560+
3 => NULL,
3561+
),
3562+
),
35153563
'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada WHERE email = \'webmaster@example.org\'' =>
35163564
array (
35173565
'error' => NULL,
@@ -3833,6 +3881,155 @@ Simulated query: SELECT email, adaid, gesperrt, freigabe1u1 FROM ada . WHERE ema
38333881
)),
38343882
),
38353883
),
3884+
'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada WHERE gesperrt=\'1\'' =>
3885+
array (
3886+
'error' => NULL,
3887+
'result' =>
3888+
array (
3889+
3 =>
3890+
PHPStan\Type\Constant\ConstantArrayType::__set_state(array(
3891+
'keyType' =>
3892+
PHPStan\Type\UnionType::__set_state(array(
3893+
'types' =>
3894+
array (
3895+
0 =>
3896+
PHPStan\Type\Constant\ConstantIntegerType::__set_state(array(
3897+
'value' => 0,
3898+
)),
3899+
1 =>
3900+
PHPStan\Type\Constant\ConstantIntegerType::__set_state(array(
3901+
'value' => 1,
3902+
)),
3903+
2 =>
3904+
PHPStan\Type\Constant\ConstantIntegerType::__set_state(array(
3905+
'value' => 2,
3906+
)),
3907+
3 =>
3908+
PHPStan\Type\Constant\ConstantIntegerType::__set_state(array(
3909+
'value' => 3,
3910+
)),
3911+
4 =>
3912+
PHPStan\Type\Constant\ConstantStringType::__set_state(array(
3913+
'value' => 'adaid',
3914+
'isClassString' => false,
3915+
)),
3916+
5 =>
3917+
PHPStan\Type\Constant\ConstantStringType::__set_state(array(
3918+
'value' => 'email',
3919+
'isClassString' => false,
3920+
)),
3921+
6 =>
3922+
PHPStan\Type\Constant\ConstantStringType::__set_state(array(
3923+
'value' => 'freigabe1u1',
3924+
'isClassString' => false,
3925+
)),
3926+
7 =>
3927+
PHPStan\Type\Constant\ConstantStringType::__set_state(array(
3928+
'value' => 'gesperrt',
3929+
'isClassString' => false,
3930+
)),
3931+
),
3932+
)),
3933+
'itemType' =>
3934+
PHPStan\Type\UnionType::__set_state(array(
3935+
'types' =>
3936+
array (
3937+
0 =>
3938+
PHPStan\Type\IntegerRangeType::__set_state(array(
3939+
'min' => -128,
3940+
'max' => 4294967295,
3941+
)),
3942+
1 =>
3943+
PHPStan\Type\StringType::__set_state(array(
3944+
)),
3945+
),
3946+
)),
3947+
'allArrays' => NULL,
3948+
'keyTypes' =>
3949+
array (
3950+
0 =>
3951+
PHPStan\Type\Constant\ConstantStringType::__set_state(array(
3952+
'value' => 'email',
3953+
'isClassString' => false,
3954+
)),
3955+
1 =>
3956+
PHPStan\Type\Constant\ConstantIntegerType::__set_state(array(
3957+
'value' => 0,
3958+
)),
3959+
2 =>
3960+
PHPStan\Type\Constant\ConstantStringType::__set_state(array(
3961+
'value' => 'adaid',
3962+
'isClassString' => false,
3963+
)),
3964+
3 =>
3965+
PHPStan\Type\Constant\ConstantIntegerType::__set_state(array(
3966+
'value' => 1,
3967+
)),
3968+
4 =>
3969+
PHPStan\Type\Constant\ConstantStringType::__set_state(array(
3970+
'value' => 'gesperrt',
3971+
'isClassString' => false,
3972+
)),
3973+
5 =>
3974+
PHPStan\Type\Constant\ConstantIntegerType::__set_state(array(
3975+
'value' => 2,
3976+
)),
3977+
6 =>
3978+
PHPStan\Type\Constant\ConstantStringType::__set_state(array(
3979+
'value' => 'freigabe1u1',
3980+
'isClassString' => false,
3981+
)),
3982+
7 =>
3983+
PHPStan\Type\Constant\ConstantIntegerType::__set_state(array(
3984+
'value' => 3,
3985+
)),
3986+
),
3987+
'valueTypes' =>
3988+
array (
3989+
0 =>
3990+
PHPStan\Type\StringType::__set_state(array(
3991+
)),
3992+
1 =>
3993+
PHPStan\Type\StringType::__set_state(array(
3994+
)),
3995+
2 =>
3996+
PHPStan\Type\IntegerRangeType::__set_state(array(
3997+
'min' => 0,
3998+
'max' => 4294967295,
3999+
)),
4000+
3 =>
4001+
PHPStan\Type\IntegerRangeType::__set_state(array(
4002+
'min' => 0,
4003+
'max' => 4294967295,
4004+
)),
4005+
4 =>
4006+
PHPStan\Type\IntegerRangeType::__set_state(array(
4007+
'min' => -128,
4008+
'max' => 127,
4009+
)),
4010+
5 =>
4011+
PHPStan\Type\IntegerRangeType::__set_state(array(
4012+
'min' => -128,
4013+
'max' => 127,
4014+
)),
4015+
6 =>
4016+
PHPStan\Type\IntegerRangeType::__set_state(array(
4017+
'min' => -128,
4018+
'max' => 127,
4019+
)),
4020+
7 =>
4021+
PHPStan\Type\IntegerRangeType::__set_state(array(
4022+
'min' => -128,
4023+
'max' => 127,
4024+
)),
4025+
),
4026+
'nextAutoIndex' => 4,
4027+
'optionalKeys' =>
4028+
array (
4029+
),
4030+
)),
4031+
),
4032+
),
38364033
'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada WHERE gesperrt=1' =>
38374034
array (
38384035
'error' => NULL,

src/Rules/PdoStatementExecuteMethodRule.php

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -62,18 +62,21 @@ private function checkErrors(MethodReflection $methodReflection, MethodCall $met
6262
return [];
6363
}
6464

65+
$errors = [];
6566
$queryReflection = new QueryReflection();
66-
$queryString = $queryReflection->resolveQueryString($queryExpr, $scope);
67-
if (null === $queryString) {
68-
return [];
67+
$placeholderReflection = new PlaceholderValidation();
68+
foreach ($queryReflection->resolveQueryStrings($queryExpr, $scope) as $queryString) {
69+
foreach ($placeholderReflection->checkErrors($queryString, $methodCall, $scope) as $error) {
70+
// make error messages unique
71+
$errors[$error] = $error;
72+
}
6973
}
7074

71-
$placeholderReflection = new PlaceholderValidation();
72-
$errors = [];
73-
foreach ($placeholderReflection->checkErrors($queryString, $methodCall, $scope) as $error) {
74-
$errors[] = RuleErrorBuilder::message($error)->line($methodCall->getLine())->build();
75+
$ruleErrors = [];
76+
foreach ($errors as $error) {
77+
$ruleErrors[] = RuleErrorBuilder::message($error)->line($methodCall->getLine())->build();
7578
}
7679

77-
return $errors;
80+
return $ruleErrors;
7881
}
7982
}

tests/PdoStatementExecuteMethodRuleTest.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,14 @@ public function testParameterErrors(): void
7575
'Query expects 1 placeholder, but 2 values are given to execute().',
7676
38,
7777
],
78+
[
79+
'Query expects placeholder :asdsa, but it is missing from values given to execute().',
80+
54,
81+
],
82+
[
83+
'Value :adaid is given to execute(), but the query does not contain this placeholder.',
84+
54,
85+
],
7886
]);
7987
}
8088
}

tests/data/pdo-stmt-execute-error.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,20 @@ public function multi_execute(PDO $pdo)
3737
$stmt->execute(['adaid' => 1]); // everything correct
3838
$stmt->execute([':email' => 'email@example.org', 'adaid' => 1]); // wrong number of parameters
3939
}
40+
41+
public function conditionalSyntaxError(PDO $pdo)
42+
{
43+
$query = 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada';
44+
45+
if (rand(0, 1)) {
46+
// valid condition
47+
$query .= ' WHERE adaid = :adaid';
48+
} else {
49+
// wrong param name
50+
$query .= ' WHERE adaid = :asdsa';
51+
}
52+
53+
$stmt = $pdo->prepare($query);
54+
$stmt->execute(['adaid' => 1]);
55+
}
4056
}

0 commit comments

Comments
 (0)