Skip to content

Commit c28ca17

Browse files
authored
Adds new placeholders (#473)
Co-authored-by: Jakub Vojacek <jakub@motv.eu>
1 parent dc579c0 commit c28ca17

File tree

3 files changed

+35
-9
lines changed

3 files changed

+35
-9
lines changed

src/DibiReflection/DibiReflection.php

+14-8
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,25 @@ final class DibiReflection
88
{
99
public function rewriteQuery(string $queryString): ?string
1010
{
11-
$queryString = str_replace('%in', '(1)', $queryString);
11+
// see https://dibiphp.com/en/documentation#toc-modifiers
1212
$queryString = str_replace('%lmt', 'LIMIT 1', $queryString);
1313
$queryString = str_replace('%ofs', ', 1', $queryString);
14-
$queryString = preg_replace('#%(i|s)#', "'1'", $queryString) ?? '';
15-
$queryString = preg_replace('#%(t|d)#', '"2000-1-1"', $queryString) ?? '';
14+
$queryString = str_replace('%in', '(1)', $queryString);
15+
$queryString = str_replace('%l', '(1)', $queryString);
16+
$queryString = preg_replace('#%(bin|sN|iN|f|i|s)#', "'1'", $queryString) ?? '';
17+
$queryString = preg_replace('#%(t|d|dt)#', '"2000-1-1"', $queryString) ?? '';
1618
$queryString = preg_replace('#%(and|or)#', '(1 = 1)', $queryString) ?? '';
1719
$queryString = preg_replace('#%~?like~?#', '"%1%"', $queryString) ?? '';
1820

19-
if (strpos($queryString, '%n') > 0) {
20-
$queryString = null;
21-
} elseif (strpos($queryString, '%ex') > 0) {
22-
$queryString = null;
23-
} elseif (0 !== preg_match('#^\s*(START|ROLLBACK|SET|SAVEPOINT|SHOW)#i', $queryString)) {
21+
$unsupportedPlaceholders = ['%ex', '%n', '%by', '%sql', '%m', '%N'];
22+
23+
foreach ($unsupportedPlaceholders as $unsupportedPlaceholder) {
24+
if (strpos($queryString, $unsupportedPlaceholder) > 0) {
25+
return null;
26+
}
27+
}
28+
29+
if (0 !== preg_match('#^\s*(START|ROLLBACK|SET|SAVEPOINT|SHOW)#i', $queryString)) {
2430
$queryString = null;
2531
}
2632

src/Rules/SyntaxErrorInDibiPreparedStatementMethodRule.php

+12-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use PHPStan\Type\Constant\ConstantArrayType;
1919
use PHPStan\Type\ObjectType;
2020
use PHPStan\Type\StringType;
21+
use staabm\PHPStanDba\DibiReflection\DibiReflection;
2122
use staabm\PHPStanDba\QueryReflection\QueryReflection;
2223
use staabm\PHPStanDba\QueryReflection\QueryReflector;
2324

@@ -143,10 +144,13 @@ private function checkErrors(CallLike $callLike, Scope $scope, MethodReflection
143144
}
144145

145146
$placeholders = [];
146-
preg_match_all('#%(ex|in|i|and|or|s|t|d|~?like~?|n|lmt|ofs)#', $queryParameters[0], $placeholders, \PREG_SET_ORDER);
147+
// see https://dibiphp.com/en/documentation#toc-modifiers
148+
preg_match_all('#%(sN|bin|by|lmt|b|iN|f|dt|sql|ex|in|i|l|m|and|or|s|t|d|~?like~?|n|ofs|N)#', $queryParameters[0], $placeholders, \PREG_SET_ORDER);
147149
$placeholderCount = \count($placeholders);
148150
$parameterCount = \count($queryParameters) - 1;
149151

152+
// check that it's not the dibi magic insert statement $this->connection->query('INSERT into apps', ['xx' => ...])
153+
// in that case it does not make sense to validate placeholder count because we know it won't match
150154
if (1 === $stringParameterCount && 'INSERT' !== QueryReflection::getQueryType($queryParameters[0])) {
151155
if ($parameterCount !== $placeholderCount) {
152156
$placeholderExpectation = sprintf('Query expects %s placeholder', $placeholderCount);
@@ -179,6 +183,13 @@ private function checkErrors(CallLike $callLike, Scope $scope, MethodReflection
179183
return [];
180184
}
181185

186+
$dibiReflection = new DibiReflection();
187+
$queryParameters[0] = $dibiReflection->rewriteQuery($queryParameters[0]);
188+
189+
if (null === $queryParameters[0]) {
190+
return [];
191+
}
192+
182193
$validity = $queryReflection->validateQueryString($queryParameters[0]);
183194

184195
if (null !== $validity) {

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

+9
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,15 @@ public function testPlaceholders(\Dibi\Connection $conn)
4141
$conn->fetchPairs('SELECT email FROM ada where email = %s', 'email@github.com');
4242
}
4343

44+
public function testIgnoredPlaceholders(\Dibi\Connection $conn)
45+
{
46+
$conn->query('SELECT %n, %n from %n', 'email', 'dadid', 'ada');
47+
$conn->query('SELECT email, adaid FROM ada group by %by', 'email');
48+
$conn->query('SELECT email, adaid FROM ada where email = %bin group by %by', 1, 'email');
49+
// has syntax error but will not be caught due to unsupported placeholder
50+
$conn->query('SELECT %n, %n frommmm %n', 'email', 'dadid', 'ada');
51+
}
52+
4453
/* phpstan-dba does not yet support writable queries
4554
public function testDeleteUpdateInsert(\Dibi\Connection $conn)
4655
{

0 commit comments

Comments
 (0)