Skip to content

Commit b413303

Browse files
authored
Named placeholders only need to be contained in one of the union queries (#625)
1 parent dcadb25 commit b413303

File tree

3 files changed

+54
-25
lines changed

3 files changed

+54
-25
lines changed

src/QueryReflection/PlaceholderValidation.php

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -18,29 +18,26 @@ public function checkQuery(Expr $queryExpr, Scope $scope, array $parameters): it
1818
{
1919
$queryReflection = new QueryReflection();
2020

21+
$queryStrings = [];
22+
$namedPlaceholders = false;
2123
foreach ($queryReflection->resolveQueryStrings($queryExpr, $scope) as $queryString) {
22-
foreach ($this->checkErrors($queryString, $parameters) as $error) {
23-
yield $error;
24+
$queryStrings[] = $queryString;
25+
26+
if ($queryReflection->containsNamedPlaceholders($queryString, $parameters)) {
27+
$namedPlaceholders = true;
2428
}
2529
}
26-
}
2730

28-
/**
29-
* @param array<string|int, Parameter> $parameters
30-
*
31-
* @return iterable<string>
32-
*/
33-
private function checkErrors(string $queryString, array $parameters): iterable
34-
{
35-
$queryReflection = new QueryReflection();
36-
if ($queryReflection->containsNamedPlaceholders($queryString, $parameters)) {
37-
yield from $this->validateNamedPlaceholders($queryString, $parameters);
31+
if ($namedPlaceholders) {
32+
yield from $this->validateNamedPlaceholders($queryStrings, $parameters);
3833

3934
return;
4035
}
4136

42-
$placeholderCount = $queryReflection->countPlaceholders($queryString);
43-
yield from $this->validateUnnamedPlaceholders($parameters, $placeholderCount);
37+
foreach ($queryStrings as $queryString) {
38+
$placeholderCount = $queryReflection->countPlaceholders($queryString);
39+
yield from $this->validateUnnamedPlaceholders($parameters, $placeholderCount);
40+
}
4441
}
4542

4643
/**
@@ -85,18 +82,25 @@ private function validateUnnamedPlaceholders(array $parameters, int $placeholder
8582
}
8683

8784
/**
85+
* @param list<string> $queryStrings
8886
* @param array<string|int, Parameter> $parameters
8987
*
9088
* @return iterable<string>
9189
*/
92-
private function validateNamedPlaceholders(string $queryString, array $parameters): iterable
90+
private function validateNamedPlaceholders(array $queryStrings, array $parameters): iterable
9391
{
9492
$queryReflection = new QueryReflection();
95-
$namedPlaceholders = $queryReflection->extractNamedPlaceholders($queryString);
9693

97-
foreach ($namedPlaceholders as $namedPlaceholder) {
98-
if (! \array_key_exists($namedPlaceholder, $parameters)) {
99-
yield sprintf('Query expects placeholder %s, but it is missing from values given.', $namedPlaceholder);
94+
$allNamedPlaceholders = [];
95+
foreach ($queryStrings as $queryString) {
96+
$namedPlaceholders = $queryReflection->extractNamedPlaceholders($queryString);
97+
98+
foreach ($namedPlaceholders as $namedPlaceholder) {
99+
if (! \array_key_exists($namedPlaceholder, $parameters)) {
100+
yield sprintf('Query expects placeholder %s, but it is missing from values given.', $namedPlaceholder);
101+
}
102+
103+
$allNamedPlaceholders[] = $namedPlaceholder;
100104
}
101105
}
102106

@@ -107,7 +111,7 @@ private function validateNamedPlaceholders(string $queryString, array $parameter
107111
if ($parameter->isOptional) {
108112
continue;
109113
}
110-
if (! \in_array($placeholderKey, $namedPlaceholders, true)) {
114+
if (! \in_array($placeholderKey, $allNamedPlaceholders, true)) {
111115
yield sprintf('Value %s is given, but the query does not contain this placeholder.', $placeholderKey);
112116
}
113117
}

tests/rules/PdoStatementExecuteMethodRuleTest.php

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,10 +86,6 @@ public function testParameterErrors(): void
8686
'Query expects placeholder :asdsa, but it is missing from values given.',
8787
54,
8888
],
89-
[
90-
'Value :adaid is given, but the query does not contain this placeholder.',
91-
54,
92-
],
9389
[
9490
'Query expects placeholder :adaid, but it is missing from values given.',
9591
61,
@@ -116,4 +112,10 @@ public function testParameterErrors(): void
116112
],
117113
]);
118114
}
115+
116+
public function testNamedPlaceholderBug(): void
117+
{
118+
$this->analyse([__DIR__ . '/data/named-placeholder-bug.php'], [
119+
]);
120+
}
119121
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
namespace NamedPlaceholderBug;
4+
5+
6+
use PDO;
7+
8+
class Foo
9+
{
10+
public function errors(PDO $pdo, $vkFrom)
11+
{
12+
$fromCondition = '';
13+
if ('0' !== $vkFrom) {
14+
$fromCondition = 'and email = :vkFrom';
15+
}
16+
17+
$stmt = $pdo->prepare('SELECT email, adaid FROM ada WHERE adaid = :adaid ' . $fromCondition);
18+
$stmt->execute([
19+
'adaid' => 1,
20+
'vkFrom' => 'hello world'
21+
]);
22+
}
23+
}

0 commit comments

Comments
 (0)