Skip to content

Commit 8477d25

Browse files
staabmclxmstaab
andauthored
added PdoQuoteDynamicReturnTypeExtension (#30)
Co-authored-by: Markus Staab <[email protected]>
1 parent 742f19a commit 8477d25

File tree

5 files changed

+130
-1
lines changed

5 files changed

+130
-1
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ This extension provides following features:
66
* `mysqli->query` knows the array shape of the returned results and therefore can return a generic `mysqli_result`
77
* `SyntaxErrorInQueryMethodRule` can inspect sql queries and detect syntax errors - `SyntaxErrorInQueryFunctionRule` can do the same for functions
88
* `mysqli_real_escape_string` and `mysqli->real_escape_string` dynamic return type extensions
9+
* `pdo->quote` dynamic return type extension
910

1011
[see the unit-testsuite](https://github.com/staabm/phpstan-dba/tree/main/tests/data) to get a feeling about the current featureset.
1112

config/extensions.neon

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,8 @@ services:
2020
tags:
2121
- phpstan.broker.dynamicMethodReturnTypeExtension
2222
- phpstan.broker.dynamicFunctionReturnTypeExtension
23+
24+
-
25+
class: staabm\PHPStanDba\Extensions\PdoQuoteDynamicReturnTypeExtension
26+
tags:
27+
- phpstan.broker.dynamicMethodReturnTypeExtension

src/Extensions/MysqliEscapeStringDynamicReturnTypeExtension.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ private function inferType(Type $argType): Type
6565
$intersection = [new StringType()];
6666

6767
if ($argType->isNumericString()->yes()) {
68-
// a numeric string is by definition non-empty. therefore don't combine the e accessories
68+
// a numeric string is by definition non-empty. therefore don't combine the 2 accessories
6969
$intersection[] = new AccessoryNumericStringType();
7070
} elseif ($argType->isNonEmptyString()->yes()) {
7171
$intersection[] = new AccessoryNonEmptyStringType();
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace staabm\PHPStanDba\Extensions;
6+
7+
use PDO;
8+
use PhpParser\Node\Expr\MethodCall;
9+
use PHPStan\Analyser\Scope;
10+
use PHPStan\Reflection\MethodReflection;
11+
use PHPStan\Reflection\ParametersAcceptorSelector;
12+
use PHPStan\Type\Accessory\AccessoryNonEmptyStringType;
13+
use PHPStan\Type\Accessory\AccessoryNumericStringType;
14+
use PHPStan\Type\Constant\ConstantBooleanType;
15+
use PHPStan\Type\Constant\ConstantIntegerType;
16+
use PHPStan\Type\DynamicMethodReturnTypeExtension;
17+
use PHPStan\Type\IntersectionType;
18+
use PHPStan\Type\StringType;
19+
use PHPStan\Type\Type;
20+
use PHPStan\Type\TypeCombinator;
21+
22+
final class PdoQuoteDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
23+
{
24+
public function getClass(): string
25+
{
26+
return PDO::class;
27+
}
28+
29+
public function isMethodSupported(MethodReflection $methodReflection): bool
30+
{
31+
return 'quote' === $methodReflection->getName();
32+
}
33+
34+
public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type
35+
{
36+
$args = $methodCall->getArgs();
37+
if (\count($args) < 1) {
38+
return ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType();
39+
}
40+
41+
if (1 === \count($args)) {
42+
$type = PDO::PARAM_STR;
43+
} else {
44+
$typeType = $scope->getType($args[1]->value);
45+
if (!$typeType instanceof ConstantIntegerType) {
46+
return ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType();
47+
}
48+
$type = $typeType->getValue();
49+
}
50+
51+
$argType = $scope->getType($args[0]->value);
52+
$stringType = $this->inferStringType($argType);
53+
54+
// check for types which are supported by all drivers, therefore cannot return false.
55+
if (PDO::PARAM_STR === $type || PDO::PARAM_INT === $type || PDO::PARAM_BOOL === $type) {
56+
return $stringType;
57+
}
58+
59+
return TypeCombinator::union($stringType, new ConstantBooleanType(false));
60+
}
61+
62+
private function inferStringType(Type $argType): Type
63+
{
64+
$intersection = [new StringType()];
65+
66+
if ($argType->isNumericString()->yes()) {
67+
// a numeric string is by definition non-empty. therefore don't combine the 2 accessories
68+
$intersection[] = new AccessoryNumericStringType();
69+
} elseif ($argType->isNonEmptyString()->yes()) {
70+
$intersection[] = new AccessoryNonEmptyStringType();
71+
}
72+
73+
if (\count($intersection) > 1) {
74+
return new IntersectionType($intersection);
75+
}
76+
77+
return new StringType();
78+
}
79+
}

tests/data/pdo.php

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,4 +128,48 @@ public function unsupportedFetchTypes(PDO $pdo)
128128
$stmt = $pdo->query('SELECT email, adaid, gesperrt, freigabe1u1 FROM ada', PDO::FETCH_OBJ);
129129
assertType('PDOStatement<array>|false', $stmt);
130130
}
131+
132+
/**
133+
* @param numeric $n
134+
* @param non-empty-string $nonE
135+
* @param numeric-string $numericString
136+
*/
137+
public function quote(PDO $pdo, int $i, float $f, $n, string $s, $nonE, string $numericString)
138+
{
139+
assertType('numeric-string', $pdo->quote((string) $i));
140+
assertType('numeric-string', $pdo->quote((string) $f));
141+
assertType('numeric-string', $pdo->quote((string) $n));
142+
assertType('numeric-string', $pdo->quote($numericString));
143+
assertType('non-empty-string', $pdo->quote($nonE));
144+
assertType('string', $pdo->quote($s));
145+
146+
assertType('numeric-string', $pdo->quote((string) $i, PDO::PARAM_STR));
147+
assertType('numeric-string', $pdo->quote((string) $f, PDO::PARAM_STR));
148+
assertType('numeric-string', $pdo->quote((string) $n, PDO::PARAM_STR));
149+
assertType('numeric-string', $pdo->quote($numericString, PDO::PARAM_STR));
150+
assertType('non-empty-string', $pdo->quote($nonE, PDO::PARAM_STR));
151+
assertType('string', $pdo->quote($s, PDO::PARAM_STR));
152+
153+
assertType('numeric-string', $pdo->quote((string) $i, PDO::PARAM_INT));
154+
assertType('numeric-string', $pdo->quote((string) $f, PDO::PARAM_INT));
155+
assertType('numeric-string', $pdo->quote((string) $n, PDO::PARAM_INT));
156+
assertType('numeric-string', $pdo->quote($numericString, PDO::PARAM_INT));
157+
assertType('non-empty-string', $pdo->quote($nonE, PDO::PARAM_INT));
158+
assertType('string', $pdo->quote($s, PDO::PARAM_INT));
159+
160+
assertType('numeric-string', $pdo->quote((string) $i, PDO::PARAM_BOOL));
161+
assertType('numeric-string', $pdo->quote((string) $f, PDO::PARAM_BOOL));
162+
assertType('numeric-string', $pdo->quote((string) $n, PDO::PARAM_BOOL));
163+
assertType('numeric-string', $pdo->quote($numericString, PDO::PARAM_BOOL));
164+
assertType('non-empty-string', $pdo->quote($nonE, PDO::PARAM_BOOL));
165+
assertType('string', $pdo->quote($s, PDO::PARAM_BOOL));
166+
167+
// not 100% sure, whether LOB is really not supported across the board
168+
assertType('numeric-string|false', $pdo->quote((string) $i, PDO::PARAM_LOB));
169+
assertType('numeric-string|false', $pdo->quote((string) $f, PDO::PARAM_LOB));
170+
assertType('numeric-string|false', $pdo->quote((string) $n, PDO::PARAM_LOB));
171+
assertType('numeric-string|false', $pdo->quote($numericString, PDO::PARAM_LOB));
172+
assertType('non-empty-string|false', $pdo->quote($nonE, PDO::PARAM_LOB));
173+
assertType('string|false', $pdo->quote($s, PDO::PARAM_LOB));
174+
}
131175
}

0 commit comments

Comments
 (0)