Skip to content

Commit a641253

Browse files
authored
Strip query comments (#431)
1 parent 4db44bd commit a641253

11 files changed

+1442
-18
lines changed

.phpstan-dba-mysqli.cache

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.phpstan-dba-pdo-mysql.cache

Lines changed: 0 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Analyzer/QueryPlanQueryResolver.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public function resolve(Scope $scope, Expr $queryExpr, ?Type $parameterTypes): i
3434
}
3535

3636
foreach ($queryStrings as $queryString) {
37-
$normalizedQuery = QuerySimulation::stripTrailers($queryString);
37+
$normalizedQuery = QuerySimulation::stripTrailers(QuerySimulation::stripComments($queryString));
3838

3939
if (null !== $normalizedQuery) {
4040
yield $normalizedQuery;

src/QueryReflection/QueryReflection.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,15 +136,15 @@ public function resolveQueryStrings(Expr $queryExpr, Scope $scope): iterable
136136

137137
if ($type instanceof UnionType) {
138138
foreach (TypeUtils::getConstantStrings($type) as $constantString) {
139-
yield $this->normalizeQueryString($constantString->getValue());
139+
yield QuerySimulation::stripComments($this->normalizeQueryString($constantString->getValue()));
140140
}
141141

142142
return;
143143
}
144144

145145
$queryString = $this->resolveQueryExpr($queryExpr, $scope);
146146
if (null !== $queryString) {
147-
yield $this->normalizeQueryString($queryString);
147+
yield QuerySimulation::stripComments($this->normalizeQueryString($queryString));
148148
}
149149
}
150150

src/QueryReflection/QuerySimulation.php

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ public static function simulateParamValueType(Type $paramType, bool $preparedPar
124124

125125
public static function simulate(string $queryString): ?string
126126
{
127-
$queryString = self::stripTrailers($queryString);
127+
$queryString = self::stripTrailers(self::stripComments($queryString));
128128

129129
if (null === $queryString) {
130130
return null;
@@ -158,4 +158,28 @@ public static function stripTrailers(string $queryString): ?string
158158

159159
return preg_replace('/\s*LIMIT\s+["\']?\d+["\']?\s*(,\s*["\']?\d*["\']?)?\s*$/i', '', $queryString);
160160
}
161+
162+
/**
163+
* @see https://larrysteinle.com/2011/02/09/use-regular-expressions-to-clean-sql-statements/
164+
* @see https://github.com/decemberster/sql-strip-comments/blob/3bef3558211a6f6191d2ad0ceb8577eda39dd303/index.js
165+
*/
166+
public static function stripComments(string $query): string
167+
{
168+
return trim(preg_replace_callback(
169+
'/("(""|[^"])*")|(\'(\'\'|[^\'])*\')|(--[^\n\r]*)|(\/\*[\w\W]*?(?=\*\/)\*\/)/m',
170+
static function (array $matches): string {
171+
$match = $matches[0];
172+
$matchLength = \strlen($match);
173+
if (
174+
('"' === $match[0] && '"' === $match[$matchLength - 1])
175+
|| ('\'' === $match[0] && '\'' === $match[$matchLength - 1])
176+
) {
177+
return $match;
178+
}
179+
180+
return '';
181+
},
182+
$query
183+
) ?? $query);
184+
}
161185
}

src/Rules/PdoStatementExecuteMethodRule.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public function processNode(Node $methodCall, Scope $scope): array
4444
return [];
4545
}
4646

47-
if (PdoStatement::class !== $methodReflection->getDeclaringClass()->getName()) {
47+
if (PDOStatement::class !== $methodReflection->getDeclaringClass()->getName()) {
4848
return [];
4949
}
5050

tests/default/QuerySimulationTest.php

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,45 @@ public function testIntersectionTypeMix()
4141
$simulatedValue = QuerySimulation::simulateParamValueType($builder->getArray(), false);
4242
$this->assertNotNull($simulatedValue);
4343
}
44+
45+
/**
46+
* @dataProvider provideQueriesWithComments
47+
*/
48+
public function testStripComments(string $query, string $expectedQuery)
49+
{
50+
self::assertSame($expectedQuery, QuerySimulation::stripComments($query));
51+
}
52+
53+
/**
54+
* @return iterable<array{string, string}>
55+
*/
56+
public function provideQueriesWithComments(): iterable
57+
{
58+
yield 'Single line comment' => [
59+
'SELECT * FROM ada; -- ignore me',
60+
'SELECT * FROM ada;',
61+
];
62+
yield 'Single line block comment' => [
63+
'SELECT * FROM ada; /* ignore me */',
64+
'SELECT * FROM ada;',
65+
];
66+
yield 'Single line comment inside block comment' => [
67+
'SELECT * FROM ada; /* -- ignore me */',
68+
'SELECT * FROM ada;',
69+
];
70+
yield 'Select comment-like value' => [
71+
'SELECT "hello -- darkness my old friend -- /* ive come to talk with you again */ bye" FROM ada --thanks',
72+
'SELECT "hello -- darkness my old friend -- /* ive come to talk with you again */ bye" FROM ada',
73+
];
74+
yield 'Multi-line comment' => [
75+
'
76+
SELECT * FROM ada
77+
/*
78+
nice
79+
comment
80+
*/
81+
',
82+
'SELECT * FROM ada',
83+
];
84+
}
4485
}

tests/default/config/.phpunit-phpstan-dba-pdo-mysql.cache

Lines changed: 77 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)