Skip to content

Commit a2f4995

Browse files
staabmclxmstaab
andauthored
extracted placeholder validation into separate class (#137)
Co-authored-by: Markus Staab <m.staab@complex-it.de>
1 parent 8aba84f commit a2f4995

File tree

2 files changed

+89
-65
lines changed

2 files changed

+89
-65
lines changed
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace staabm\PHPStanDba\QueryReflection;
6+
7+
use PhpParser\Node\Expr\MethodCall;
8+
use PHPStan\Analyser\Scope;
9+
10+
final class PlaceholderValidation
11+
{
12+
/**
13+
* @return iterable<string>
14+
*/
15+
public function checkErrors(string $queryString, MethodCall $methodCall, Scope $scope): iterable
16+
{
17+
$queryReflection = new QueryReflection();
18+
$args = $methodCall->getArgs();
19+
$placeholderCount = $queryReflection->countPlaceholders($queryString);
20+
21+
if (0 === \count($args)) {
22+
if (0 === $placeholderCount) {
23+
return;
24+
}
25+
26+
$placeholderExpectation = sprintf('Query expects %s placeholder', $placeholderCount);
27+
if ($placeholderCount > 1) {
28+
$placeholderExpectation = sprintf('Query expects %s placeholders', $placeholderCount);
29+
}
30+
31+
yield sprintf($placeholderExpectation.', but no values are given to execute().', $placeholderCount);
32+
33+
return;
34+
}
35+
36+
yield from $this->checkParameterValues($methodCall, $scope, $queryString, $placeholderCount);
37+
}
38+
39+
/**
40+
* @return iterable<string>
41+
*/
42+
private function checkParameterValues(MethodCall $methodCall, Scope $scope, string $queryString, int $placeholderCount): iterable
43+
{
44+
$queryReflection = new QueryReflection();
45+
$args = $methodCall->getArgs();
46+
47+
$parameterTypes = $scope->getType($args[0]->value);
48+
$parameters = $queryReflection->resolveParameters($parameterTypes);
49+
if (null === $parameters) {
50+
return;
51+
}
52+
$parameterCount = \count($parameters);
53+
54+
if ($parameterCount !== $placeholderCount) {
55+
$placeholderExpectation = sprintf('Query expects %s placeholder', $placeholderCount);
56+
if ($placeholderCount > 1) {
57+
$placeholderExpectation = sprintf('Query expects %s placeholders', $placeholderCount);
58+
}
59+
60+
$parameterActual = sprintf('but %s value is given to execute()', $parameterCount);
61+
if ($parameterCount > 1) {
62+
$parameterActual = sprintf('but %s values are given to execute()', $parameterCount);
63+
}
64+
65+
yield $placeholderExpectation.', '.$parameterActual.'.';
66+
67+
return;
68+
}
69+
70+
$namedPlaceholders = $queryReflection->extractNamedPlaceholders($queryString);
71+
if (\count($namedPlaceholders) > 0) {
72+
foreach ($namedPlaceholders as $namedPlaceholder) {
73+
if (!\array_key_exists($namedPlaceholder, $parameters)) {
74+
yield sprintf('Query expects placeholder %s, but it is missing from values given to execute().', $namedPlaceholder);
75+
}
76+
}
77+
78+
foreach ($parameters as $placeholderKey => $value) {
79+
if (!\in_array($placeholderKey, $namedPlaceholders)) {
80+
yield sprintf('Value %s is given to execute(), but the query does not contain this placeholder.', $placeholderKey);
81+
}
82+
}
83+
}
84+
}
85+
}

src/Rules/PdoStatementExecuteMethodRule.php

Lines changed: 4 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use PHPStan\Rules\RuleError;
1414
use PHPStan\Rules\RuleErrorBuilder;
1515
use staabm\PHPStanDba\PdoReflection\PdoStatementReflection;
16+
use staabm\PHPStanDba\QueryReflection\PlaceholderValidation;
1617
use staabm\PHPStanDba\QueryReflection\QueryReflection;
1718

1819
/**
@@ -67,72 +68,10 @@ private function checkErrors(MethodReflection $methodReflection, MethodCall $met
6768
return [];
6869
}
6970

70-
$args = $methodCall->getArgs();
71-
$placeholderCount = $queryReflection->countPlaceholders($queryString);
72-
73-
if (0 === \count($args)) {
74-
if (0 === $placeholderCount) {
75-
return [];
76-
}
77-
78-
$placeholderExpectation = sprintf('Query expects %s placeholder', $placeholderCount);
79-
if ($placeholderCount > 1) {
80-
$placeholderExpectation = sprintf('Query expects %s placeholders', $placeholderCount);
81-
}
82-
83-
return [
84-
RuleErrorBuilder::message(sprintf($placeholderExpectation.', but no values are given to execute().', $placeholderCount))->line($methodCall->getLine())->build(),
85-
];
86-
}
87-
88-
return $this->checkParameterValues($methodCall, $scope, $queryString, $placeholderCount);
89-
}
90-
91-
/**
92-
* @return RuleError[]
93-
*/
94-
private function checkParameterValues(MethodCall $methodCall, Scope $scope, string $queryString, int $placeholderCount): array
95-
{
96-
$queryReflection = new QueryReflection();
97-
$args = $methodCall->getArgs();
98-
99-
$parameterTypes = $scope->getType($args[0]->value);
100-
$parameters = $queryReflection->resolveParameters($parameterTypes);
101-
if (null === $parameters) {
102-
return [];
103-
}
104-
$parameterCount = \count($parameters);
105-
106-
if ($parameterCount !== $placeholderCount) {
107-
$placeholderExpectation = sprintf('Query expects %s placeholder', $placeholderCount);
108-
if ($placeholderCount > 1) {
109-
$placeholderExpectation = sprintf('Query expects %s placeholders', $placeholderCount);
110-
}
111-
112-
$parameterActual = sprintf('but %s value is given to execute()', $parameterCount);
113-
if ($parameterCount > 1) {
114-
$parameterActual = sprintf('but %s values are given to execute()', $parameterCount);
115-
}
116-
117-
return [
118-
RuleErrorBuilder::message($placeholderExpectation.', '.$parameterActual.'.')->line($methodCall->getLine())->build(),
119-
];
120-
}
121-
71+
$placeholderReflection = new PlaceholderValidation();
12272
$errors = [];
123-
$namedPlaceholders = $queryReflection->extractNamedPlaceholders($queryString);
124-
if (\count($namedPlaceholders) > 0) {
125-
foreach ($namedPlaceholders as $namedPlaceholder) {
126-
if (!\array_key_exists($namedPlaceholder, $parameters)) {
127-
$errors[] = RuleErrorBuilder::message(sprintf('Query expects placeholder %s, but it is missing from values given to execute().', $namedPlaceholder))->line($methodCall->getLine())->build();
128-
}
129-
}
130-
131-
foreach ($parameters as $placeholderKey => $value) {
132-
if (!\in_array($placeholderKey, $namedPlaceholders)) {
133-
$errors[] = RuleErrorBuilder::message(sprintf('Value %s is given to execute(), but the query does not contain this placeholder.', $placeholderKey))->line($methodCall->getLine())->build();
134-
}
135-
}
73+
foreach ($placeholderReflection->checkErrors($queryString, $methodCall, $scope) as $error) {
74+
$errors[] = RuleErrorBuilder::message($error)->line($methodCall->getLine())->build();
13675
}
13776

13877
return $errors;

0 commit comments

Comments
 (0)