Skip to content

Commit 8fa75b3

Browse files
staabmclxmstaab
andauthored
added placeholder validation to SyntaxErrorInPreparedStatementMethodRule (#140)
Co-authored-by: Markus Staab <m.staab@complex-it.de>
1 parent 6f438a9 commit 8fa75b3

5 files changed

+67
-33
lines changed

src/QueryReflection/PlaceholderValidation.php

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public function checkErrors(string $queryString, array $parameters): iterable
2626
$placeholderExpectation = sprintf('Query expects %s placeholders', $placeholderCount);
2727
}
2828

29-
yield sprintf($placeholderExpectation.', but no values are given to execute().', $placeholderCount);
29+
yield sprintf($placeholderExpectation.', but no values are given.', $placeholderCount);
3030

3131
return;
3232
}
@@ -51,9 +51,9 @@ private function checkParameterValues(string $queryString, array $parameters, in
5151
$placeholderExpectation = sprintf('Query expects %s placeholders', $placeholderCount);
5252
}
5353

54-
$parameterActual = sprintf('but %s value is given to execute()', $parameterCount);
54+
$parameterActual = sprintf('but %s value is given', $parameterCount);
5555
if ($parameterCount > 1) {
56-
$parameterActual = sprintf('but %s values are given to execute()', $parameterCount);
56+
$parameterActual = sprintf('but %s values are given', $parameterCount);
5757
}
5858

5959
yield $placeholderExpectation.', '.$parameterActual.'.';
@@ -62,17 +62,18 @@ private function checkParameterValues(string $queryString, array $parameters, in
6262
}
6363

6464
$namedPlaceholders = $queryReflection->extractNamedPlaceholders($queryString);
65-
if (\count($namedPlaceholders) > 0) {
66-
foreach ($namedPlaceholders as $namedPlaceholder) {
67-
if (!\array_key_exists($namedPlaceholder, $parameters)) {
68-
yield sprintf('Query expects placeholder %s, but it is missing from values given to execute().', $namedPlaceholder);
69-
}
65+
foreach ($namedPlaceholders as $namedPlaceholder) {
66+
if (!\array_key_exists($namedPlaceholder, $parameters)) {
67+
yield sprintf('Query expects placeholder %s, but it is missing from values given.', $namedPlaceholder);
7068
}
69+
}
7170

72-
foreach ($parameters as $placeholderKey => $value) {
73-
if (!\in_array($placeholderKey, $namedPlaceholders)) {
74-
yield sprintf('Value %s is given to execute(), but the query does not contain this placeholder.', $placeholderKey);
75-
}
71+
foreach ($parameters as $placeholderKey => $value) {
72+
if (\is_int($placeholderKey)) {
73+
continue;
74+
}
75+
if (!\in_array($placeholderKey, $namedPlaceholders)) {
76+
yield sprintf('Value %s is given, but the query does not contain this placeholder.', $placeholderKey);
7677
}
7778
}
7879
}

src/Rules/SyntaxErrorInPreparedStatementMethodRule.php

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use PHPStan\Rules\RuleError;
1515
use PHPStan\Rules\RuleErrorBuilder;
1616
use PHPStan\Type\ObjectType;
17+
use staabm\PHPStanDba\QueryReflection\PlaceholderValidation;
1718
use staabm\PHPStanDba\QueryReflection\QueryReflection;
1819

1920
/**
@@ -96,16 +97,29 @@ private function checkErrors(CallLike $callLike, Scope $scope): array
9697
$parameterTypes = $scope->getType($args[1]->value);
9798

9899
$queryReflection = new QueryReflection();
100+
$parameters = $queryReflection->resolveParameters($parameterTypes) ?? [];
101+
102+
$errors = [];
103+
$placeholderReflection = new PlaceholderValidation();
99104
foreach ($queryReflection->resolvePreparedQueryStrings($queryExpr, $parameterTypes, $scope) as $queryString) {
100-
$error = $queryReflection->validateQueryString($queryString);
105+
$queryError = $queryReflection->validateQueryString($queryString);
106+
if (null !== $queryError) {
107+
$error = 'Query error: '.$queryError->getMessage().' ('.$queryError->getCode().').';
108+
$errors[$error] = $error;
109+
}
110+
}
101111

102-
if (null !== $error) {
103-
return [
104-
RuleErrorBuilder::message('Query error: '.$error->getMessage().' ('.$error->getCode().').')->line($callLike->getLine())->build(),
105-
];
112+
foreach ($queryReflection->resolveQueryStrings($queryExpr, $scope) as $queryString) {
113+
foreach ($placeholderReflection->checkErrors($queryString, $parameters) as $error) {
114+
$errors[$error] = $error;
106115
}
107116
}
108117

109-
return [];
118+
$ruleErrors = [];
119+
foreach ($errors as $error) {
120+
$ruleErrors[] = RuleErrorBuilder::message($error)->line($callLike->getLine())->build();
121+
}
122+
123+
return $ruleErrors;
110124
}
111125
}

tests/PdoStatementExecuteMethodRuleTest.php

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,65 +22,65 @@ public function testParameterErrors(): void
2222

2323
$this->analyse([__DIR__.'/data/pdo-stmt-execute-error.php'], [
2424
[
25-
'Query expects placeholder :adaid, but it is missing from values given to execute().',
25+
'Query expects placeholder :adaid, but it is missing from values given.',
2626
12,
2727
],
2828
[
29-
'Value :wrongParamName is given to execute(), but the query does not contain this placeholder.',
29+
'Value :wrongParamName is given, but the query does not contain this placeholder.',
3030
12,
3131
],
3232
[
33-
'Query expects placeholder :adaid, but it is missing from values given to execute().',
33+
'Query expects placeholder :adaid, but it is missing from values given.',
3434
15,
3535
],
3636
[
37-
'Value :wrongParamName is given to execute(), but the query does not contain this placeholder.',
37+
'Value :wrongParamName is given, but the query does not contain this placeholder.',
3838
15,
3939
],
4040
/*
4141
[
42-
'Query expects placeholder :adaid, but it is missing from values given to execute().',
42+
'Query expects placeholder :adaid, but it is missing from values given.',
4343
18,
4444
],
4545
[
46-
'Value :wrongParamValue is given to execute(), but the query does not contain this placeholder.',
46+
'Value :wrongParamValue is given, but the query does not contain this placeholder.',
4747
18,
4848
],
4949
*/
5050
[
51-
'Query expects 1 placeholder, but no values are given to execute().',
51+
'Query expects 1 placeholder, but no values are given.',
5252
21,
5353
],
5454
[
55-
'Query expects 2 placeholders, but 1 value is given to execute().',
55+
'Query expects 2 placeholders, but 1 value is given.',
5656
24,
5757
],
5858
[
59-
'Query expects 2 placeholders, but 1 value is given to execute().',
59+
'Query expects 2 placeholders, but 1 value is given.',
6060
27,
6161
],
6262
[
63-
'Query expects 2 placeholders, but 1 value is given to execute().',
63+
'Query expects 2 placeholders, but 1 value is given.',
6464
30,
6565
],
6666
[
67-
'Query expects placeholder :adaid, but it is missing from values given to execute().',
67+
'Query expects placeholder :adaid, but it is missing from values given.',
6868
36,
6969
],
7070
[
71-
'Value :wrongParamName is given to execute(), but the query does not contain this placeholder.',
71+
'Value :wrongParamName is given, but the query does not contain this placeholder.',
7272
36,
7373
],
7474
[
75-
'Query expects 1 placeholder, but 2 values are given to execute().',
75+
'Query expects 1 placeholder, but 2 values are given.',
7676
38,
7777
],
7878
[
79-
'Query expects placeholder :asdsa, but it is missing from values given to execute().',
79+
'Query expects placeholder :asdsa, but it is missing from values given.',
8080
54,
8181
],
8282
[
83-
'Value :adaid is given to execute(), but the query does not contain this placeholder.',
83+
'Value :adaid is given, but the query does not contain this placeholder.',
8484
54,
8585
],
8686
]);

tests/SyntaxErrorInPreparedStatementMethodRuleTest.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ public function testSyntaxErrorInQueryRule(): void
5353
"Query error: Unknown column 'asdsa' in 'where clause' (1054).",
5454
122,
5555
],
56+
[
57+
'Value :gesperrt is given, but the query does not contain this placeholder.',
58+
137,
59+
],
5660
]);
5761
}
5862
}

tests/data/syntax-error-in-prepared-statement.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,4 +121,19 @@ public function conditionalSyntaxError(Connection $connection)
121121

122122
$connection->preparedQuery($query, [1]);
123123
}
124+
125+
public function placeholderValidation(Connection $connection)
126+
{
127+
$query = 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada';
128+
129+
if (rand(0, 1)) {
130+
// valid condition
131+
$query .= ' WHERE gesperrt=?';
132+
} else {
133+
// unknown column
134+
$query .= ' WHERE asdsa=?';
135+
}
136+
137+
$connection->preparedQuery($query, [':gesperrt' => 1]);
138+
}
124139
}

0 commit comments

Comments
 (0)