From f8e1c4837de973fb7e6ded562a23b8d09b43d2a0 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Tue, 4 Jan 2022 10:16:46 +0100 Subject: [PATCH 1/6] added failling tests --- tests/data/mysqli.php | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/data/mysqli.php b/tests/data/mysqli.php index a8dbdbdbd..7396d77be 100644 --- a/tests/data/mysqli.php +++ b/tests/data/mysqli.php @@ -48,4 +48,26 @@ public function fnQuery(mysqli $mysqli, string $query) $result = mysqli_query($mysqli, $query); assertType('bool|mysqli_result', $result); } + + /** + * @param numeric $n + * @param non-empty-string $nonE + * @param numeric-string $numericString + */ + public function escape(myslqi $mysqli, int $i, float $f, $n, string $s, $nonE, string $numericString) + { + assertType('numeric-string', mysqli_real_escape_string($mysqli, $i)); + assertType('numeric-string', mysqli_real_escape_string($mysqli, $f)); + assertType('numeric-string', mysqli_real_escape_string($mysqli, $n)); + assertType('numeric-string', mysqli_real_escape_string($mysqli, $numericString)); + assertType('non-empty-string', mysqli_real_escape_string($mysqli, $nonE)); + assertType('string', mysqli_real_escape_string($mysqli, $s)); + + assertType('numeric-string', $mysqli->real_escape_string($i)); + assertType('numeric-string', $mysqli->real_escape_string($f)); + assertType('numeric-string', $mysqli->real_escape_string($n)); + assertType('numeric-string', $mysqli->real_escape_string($numericString)); + assertType('non-empty-string', $mysqli->real_escape_string($nonE)); + assertType('string', $mysqli->real_escape_string($s)); + } } From f0b4776accebd439309a93d14ba5e89de6716f24 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Tue, 4 Jan 2022 10:20:05 +0100 Subject: [PATCH 2/6] typo --- tests/data/mysqli.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/data/mysqli.php b/tests/data/mysqli.php index 7396d77be..a85294700 100644 --- a/tests/data/mysqli.php +++ b/tests/data/mysqli.php @@ -54,7 +54,7 @@ public function fnQuery(mysqli $mysqli, string $query) * @param non-empty-string $nonE * @param numeric-string $numericString */ - public function escape(myslqi $mysqli, int $i, float $f, $n, string $s, $nonE, string $numericString) + public function escape(mysqli $mysqli, int $i, float $f, $n, string $s, $nonE, string $numericString) { assertType('numeric-string', mysqli_real_escape_string($mysqli, $i)); assertType('numeric-string', mysqli_real_escape_string($mysqli, $f)); From 2b23b71e29ffb38561d514d4f48fa8f7c60b2f51 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Tue, 4 Jan 2022 10:26:21 +0100 Subject: [PATCH 3/6] Update mysqli.php --- tests/data/mysqli.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/data/mysqli.php b/tests/data/mysqli.php index a85294700..94122cb3f 100644 --- a/tests/data/mysqli.php +++ b/tests/data/mysqli.php @@ -56,16 +56,16 @@ public function fnQuery(mysqli $mysqli, string $query) */ public function escape(mysqli $mysqli, int $i, float $f, $n, string $s, $nonE, string $numericString) { - assertType('numeric-string', mysqli_real_escape_string($mysqli, $i)); - assertType('numeric-string', mysqli_real_escape_string($mysqli, $f)); - assertType('numeric-string', mysqli_real_escape_string($mysqli, $n)); + assertType('numeric-string', mysqli_real_escape_string($mysqli, (string) $i)); + assertType('numeric-string', mysqli_real_escape_string($mysqli, (string) $f)); + assertType('numeric-string', mysqli_real_escape_string($mysqli, (string) $n)); assertType('numeric-string', mysqli_real_escape_string($mysqli, $numericString)); assertType('non-empty-string', mysqli_real_escape_string($mysqli, $nonE)); assertType('string', mysqli_real_escape_string($mysqli, $s)); - assertType('numeric-string', $mysqli->real_escape_string($i)); - assertType('numeric-string', $mysqli->real_escape_string($f)); - assertType('numeric-string', $mysqli->real_escape_string($n)); + assertType('numeric-string', $mysqli->real_escape_string((string) $i)); + assertType('numeric-string', $mysqli->real_escape_string((string) $f)); + assertType('numeric-string', $mysqli->real_escape_string((string) $n)); assertType('numeric-string', $mysqli->real_escape_string($numericString)); assertType('non-empty-string', $mysqli->real_escape_string($nonE)); assertType('string', $mysqli->real_escape_string($s)); From 483b70895b42279f8a4862373bfc5c1efce41e0e Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Tue, 4 Jan 2022 10:27:33 +0100 Subject: [PATCH 4/6] added MysqliEscapeStringDynamicReturnTypeExtension --- config/extensions.neon | 6 ++ ...EscapeStringDynamicReturnTypeExtension.php | 81 +++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 src/Extensions/MysqliEscapeStringDynamicReturnTypeExtension.php diff --git a/config/extensions.neon b/config/extensions.neon index 52cf5578d..d030186db 100644 --- a/config/extensions.neon +++ b/config/extensions.neon @@ -14,3 +14,9 @@ services: tags: - phpstan.broker.dynamicMethodReturnTypeExtension - phpstan.broker.dynamicFunctionReturnTypeExtension + + - + class: staabm\PHPStanDba\Extensions\MysqliEscapeStringDynamicReturnTypeExtension + tags: + - phpstan.broker.dynamicMethodReturnTypeExtension + - phpstan.broker.dynamicFunctionReturnTypeExtension diff --git a/src/Extensions/MysqliEscapeStringDynamicReturnTypeExtension.php b/src/Extensions/MysqliEscapeStringDynamicReturnTypeExtension.php new file mode 100644 index 000000000..ca1d00ee7 --- /dev/null +++ b/src/Extensions/MysqliEscapeStringDynamicReturnTypeExtension.php @@ -0,0 +1,81 @@ +getName(); + } + + public function isFunctionSupported(FunctionReflection $functionReflection): bool + { + return 'mysqli_real_escape_string' === $functionReflection->getName(); + } + + public function getTypeFromFunctionCall(FunctionReflection $functionReflection, FuncCall $functionCall, Scope $scope): Type + { + $args = $functionCall->getArgs(); + if (\count($args) < 2) { + return ParametersAcceptorSelector::selectSingle($functionReflection->getVariants())->getReturnType(); + } + + $argType = $scope->getType($args[1]->value); + + return $this->inferType($argType); + } + + public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type + { + $args = $methodCall->getArgs(); + if (0 === \count($args)) { + return ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType(); + } + + $argType = $scope->getType($args[0]->value); + + return $this->inferType($argType); + } + + private function inferType(Type $argType): Type + { + $intersection = [new StringType()]; + + if ($argType->isNumericString()->yes()) { + // a numeric string is by definition non-empty. therefore don't combine the e accessories + $intersection[] = new AccessoryNumericStringType(); + } elseif ($argType->isNonEmptyString()->yes()) { + $intersection[] = new AccessoryNonEmptyStringType(); + } + + + if (\count($intersection) > 1) { + return new IntersectionType($intersection); + } + + return new StringType(); + } +} From a9896e3110289d3af7ec9faadc99b66d76d01aac Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Tue, 4 Jan 2022 10:29:10 +0100 Subject: [PATCH 5/6] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index a39b5af46..0b00915d0 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ This extension provides following features: * `PDO->query` knows the array shape of the returned results and therefore can return a generic `PDOStatement` * `mysqli->query` knows the array shape of the returned results and therefore can return a generic `mysqli_result` * `SyntaxErrorInQueryMethodRule` can inspect sql queries and detect syntax errors - `SyntaxErrorInQueryFunctionRule` can do the same for functions +* `mysqli_real_escape_string` and `mysqli->real_escape_string` dynamic return type extensions [see the unit-testsuite](https://github.com/staabm/phpstan-dba/tree/main/tests/data) to get a feeling about the current featureset. From c4dd2198e17fa3bc4b1f2464f8e50f176227c988 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Tue, 4 Jan 2022 10:29:49 +0100 Subject: [PATCH 6/6] cs --- .../MysqliEscapeStringDynamicReturnTypeExtension.php | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Extensions/MysqliEscapeStringDynamicReturnTypeExtension.php b/src/Extensions/MysqliEscapeStringDynamicReturnTypeExtension.php index ca1d00ee7..6f8be9daa 100644 --- a/src/Extensions/MysqliEscapeStringDynamicReturnTypeExtension.php +++ b/src/Extensions/MysqliEscapeStringDynamicReturnTypeExtension.php @@ -64,14 +64,13 @@ private function inferType(Type $argType): Type { $intersection = [new StringType()]; - if ($argType->isNumericString()->yes()) { - // a numeric string is by definition non-empty. therefore don't combine the e accessories - $intersection[] = new AccessoryNumericStringType(); - } elseif ($argType->isNonEmptyString()->yes()) { + if ($argType->isNumericString()->yes()) { + // a numeric string is by definition non-empty. therefore don't combine the e accessories + $intersection[] = new AccessoryNumericStringType(); + } elseif ($argType->isNonEmptyString()->yes()) { $intersection[] = new AccessoryNonEmptyStringType(); } - if (\count($intersection) > 1) { return new IntersectionType($intersection); }