From 767bad8dc1d2a1963b68408158abe99530856079 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Fri, 4 Feb 2022 15:16:29 +0100 Subject: [PATCH] doctrine dbal: support statements without args (#245) Co-authored-by: Markus Staab --- .phpstan-dba.cache | 317 ++++++++++++++++++ ...ExecuteQueryDynamicReturnTypeExtension.php | 29 +- ...nectionFetchDynamicReturnTypeExtension.php | 29 +- tests/default/data/doctrine-dbal.php | 47 +++ tests/stringify/config/.phpstan-dba.cache | 1 + .../config/.phpunit-phpstan-dba.cache | 1 + 6 files changed, 408 insertions(+), 16 deletions(-) diff --git a/.phpstan-dba.cache b/.phpstan-dba.cache index de2767c82..b24f62554 100644 --- a/.phpstan-dba.cache +++ b/.phpstan-dba.cache @@ -2,6 +2,60 @@ 'schemaVersion' => 'v5-runtime-config-bugfix', 'records' => array ( + 'SELECT count(*) FROM typemix' => + array ( + 'error' => NULL, + 'result' => + array ( + 5 => + PHPStan\Type\Constant\ConstantArrayType::__set_state(array( + 'allArrays' => NULL, + 'keyTypes' => + array ( + 0 => + PHPStan\Type\Constant\ConstantStringType::__set_state(array( + 'value' => 'count(*)', + 'isClassString' => false, + )), + 1 => + PHPStan\Type\Constant\ConstantIntegerType::__set_state(array( + 'value' => 0, + )), + ), + 'valueTypes' => + array ( + 0 => + PHPStan\Type\IntegerType::__set_state(array( + )), + 1 => + PHPStan\Type\IntegerType::__set_state(array( + )), + ), + 'nextAutoIndex' => 1, + 'optionalKeys' => + array ( + ), + 'keyType' => + PHPStan\Type\UnionType::__set_state(array( + 'types' => + array ( + 0 => + PHPStan\Type\Constant\ConstantIntegerType::__set_state(array( + 'value' => 0, + )), + 1 => + PHPStan\Type\Constant\ConstantStringType::__set_state(array( + 'value' => 'count(*)', + 'isClassString' => false, + )), + ), + )), + 'itemType' => + PHPStan\Type\IntegerType::__set_state(array( + )), + )), + ), + ), 'SELECT count(*) FROM typemix WHERE c_date = \'1\'' => array ( 'error' => NULL, @@ -10,6 +64,60 @@ 5 => NULL, ), ), + 'SELECT count(*) FROM typemix WHERE c_date = \'2022-02-04\'' => + array ( + 'error' => NULL, + 'result' => + array ( + 5 => + PHPStan\Type\Constant\ConstantArrayType::__set_state(array( + 'allArrays' => NULL, + 'keyTypes' => + array ( + 0 => + PHPStan\Type\Constant\ConstantStringType::__set_state(array( + 'value' => 'count(*)', + 'isClassString' => false, + )), + 1 => + PHPStan\Type\Constant\ConstantIntegerType::__set_state(array( + 'value' => 0, + )), + ), + 'valueTypes' => + array ( + 0 => + PHPStan\Type\IntegerType::__set_state(array( + )), + 1 => + PHPStan\Type\IntegerType::__set_state(array( + )), + ), + 'nextAutoIndex' => 1, + 'optionalKeys' => + array ( + ), + 'keyType' => + PHPStan\Type\UnionType::__set_state(array( + 'types' => + array ( + 0 => + PHPStan\Type\Constant\ConstantIntegerType::__set_state(array( + 'value' => 0, + )), + 1 => + PHPStan\Type\Constant\ConstantStringType::__set_state(array( + 'value' => 'count(*)', + 'isClassString' => false, + )), + ), + )), + 'itemType' => + PHPStan\Type\IntegerType::__set_state(array( + )), + )), + ), + ), 'SELECT count(*) FROM typemix WHERE c_datetime = \'1\'' => array ( 'error' => NULL, @@ -18,8 +126,63 @@ 5 => NULL, ), ), + 'SELECT count(*) FROM typemix WHERE c_datetime = \'2022-02-04\'' => + array ( + 'error' => NULL, + 'result' => + array ( + 5 => + PHPStan\Type\Constant\ConstantArrayType::__set_state(array( + 'allArrays' => NULL, + 'keyTypes' => + array ( + 0 => + PHPStan\Type\Constant\ConstantStringType::__set_state(array( + 'value' => 'count(*)', + 'isClassString' => false, + )), + 1 => + PHPStan\Type\Constant\ConstantIntegerType::__set_state(array( + 'value' => 0, + )), + ), + 'valueTypes' => + array ( + 0 => + PHPStan\Type\IntegerType::__set_state(array( + )), + 1 => + PHPStan\Type\IntegerType::__set_state(array( + )), + ), + 'nextAutoIndex' => 1, + 'optionalKeys' => + array ( + ), + 'keyType' => + PHPStan\Type\UnionType::__set_state(array( + 'types' => + array ( + 0 => + PHPStan\Type\Constant\ConstantIntegerType::__set_state(array( + 'value' => 0, + )), + 1 => + PHPStan\Type\Constant\ConstantStringType::__set_state(array( + 'value' => 'count(*)', + 'isClassString' => false, + )), + ), + )), + 'itemType' => + PHPStan\Type\IntegerType::__set_state(array( + )), + )), + ), + ), 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada' => array ( + 'error' => NULL, 'result' => array ( 5 => @@ -168,6 +331,7 @@ ), 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada WHERE adaid = \'1\'' => array ( + 'error' => NULL, 'result' => array ( 5 => @@ -321,6 +485,10 @@ 'message' => 'You have an error in your SQL syntax; check the manual that corresponds to your MySQL/MariaDB server version for the right syntax to use near \'? LIMIT 0\' at line 1', 'code' => 1064, )), + 'result' => + array ( + 5 => NULL, + ), ), 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada WHERE email = \'1\'' => array ( @@ -470,6 +638,155 @@ )), ), ), + 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada WHERE email = \'2022-02-04\'' => + array ( + 'error' => NULL, + 'result' => + array ( + 5 => + PHPStan\Type\Constant\ConstantArrayType::__set_state(array( + 'allArrays' => NULL, + 'keyTypes' => + array ( + 0 => + PHPStan\Type\Constant\ConstantStringType::__set_state(array( + 'value' => 'email', + 'isClassString' => false, + )), + 1 => + PHPStan\Type\Constant\ConstantIntegerType::__set_state(array( + 'value' => 0, + )), + 2 => + PHPStan\Type\Constant\ConstantStringType::__set_state(array( + 'value' => 'adaid', + 'isClassString' => false, + )), + 3 => + PHPStan\Type\Constant\ConstantIntegerType::__set_state(array( + 'value' => 1, + )), + 4 => + PHPStan\Type\Constant\ConstantStringType::__set_state(array( + 'value' => 'gesperrt', + 'isClassString' => false, + )), + 5 => + PHPStan\Type\Constant\ConstantIntegerType::__set_state(array( + 'value' => 2, + )), + 6 => + PHPStan\Type\Constant\ConstantStringType::__set_state(array( + 'value' => 'freigabe1u1', + 'isClassString' => false, + )), + 7 => + PHPStan\Type\Constant\ConstantIntegerType::__set_state(array( + 'value' => 3, + )), + ), + 'valueTypes' => + array ( + 0 => + PHPStan\Type\StringType::__set_state(array( + )), + 1 => + PHPStan\Type\StringType::__set_state(array( + )), + 2 => + PHPStan\Type\IntegerRangeType::__set_state(array( + 'min' => 0, + 'max' => 4294967295, + )), + 3 => + PHPStan\Type\IntegerRangeType::__set_state(array( + 'min' => 0, + 'max' => 4294967295, + )), + 4 => + PHPStan\Type\IntegerRangeType::__set_state(array( + 'min' => -128, + 'max' => 127, + )), + 5 => + PHPStan\Type\IntegerRangeType::__set_state(array( + 'min' => -128, + 'max' => 127, + )), + 6 => + PHPStan\Type\IntegerRangeType::__set_state(array( + 'min' => -128, + 'max' => 127, + )), + 7 => + PHPStan\Type\IntegerRangeType::__set_state(array( + 'min' => -128, + 'max' => 127, + )), + ), + 'nextAutoIndex' => 4, + 'optionalKeys' => + array ( + ), + 'keyType' => + PHPStan\Type\UnionType::__set_state(array( + 'types' => + array ( + 0 => + PHPStan\Type\Constant\ConstantIntegerType::__set_state(array( + 'value' => 0, + )), + 1 => + PHPStan\Type\Constant\ConstantIntegerType::__set_state(array( + 'value' => 1, + )), + 2 => + PHPStan\Type\Constant\ConstantIntegerType::__set_state(array( + 'value' => 2, + )), + 3 => + PHPStan\Type\Constant\ConstantIntegerType::__set_state(array( + 'value' => 3, + )), + 4 => + PHPStan\Type\Constant\ConstantStringType::__set_state(array( + 'value' => 'adaid', + 'isClassString' => false, + )), + 5 => + PHPStan\Type\Constant\ConstantStringType::__set_state(array( + 'value' => 'email', + 'isClassString' => false, + )), + 6 => + PHPStan\Type\Constant\ConstantStringType::__set_state(array( + 'value' => 'freigabe1u1', + 'isClassString' => false, + )), + 7 => + PHPStan\Type\Constant\ConstantStringType::__set_state(array( + 'value' => 'gesperrt', + 'isClassString' => false, + )), + ), + )), + 'itemType' => + PHPStan\Type\UnionType::__set_state(array( + 'types' => + array ( + 0 => + PHPStan\Type\IntegerRangeType::__set_state(array( + 'min' => -128, + 'max' => 4294967295, + )), + 1 => + PHPStan\Type\StringType::__set_state(array( + )), + ), + )), + )), + ), + ), ), 'runtimeConfig' => array ( diff --git a/src/Extensions/DoctrineConnectionExecuteQueryDynamicReturnTypeExtension.php b/src/Extensions/DoctrineConnectionExecuteQueryDynamicReturnTypeExtension.php index 679f304dc..0a20ff0fb 100644 --- a/src/Extensions/DoctrineConnectionExecuteQueryDynamicReturnTypeExtension.php +++ b/src/Extensions/DoctrineConnectionExecuteQueryDynamicReturnTypeExtension.php @@ -41,7 +41,7 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method $methodReflection->getVariants(), )->getReturnType(); - if (\count($args) < 2) { + if (\count($args) < 1) { return $defaultReturn; } @@ -54,7 +54,12 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method return $defaultReturn; } - $resultType = $this->inferType($args[0]->value, $args[1]->value, $scope); + $params = null; + if (\count($args) > 1) { + $params = $args[1]->value; + } + + $resultType = $this->inferType($args[0]->value, $params, $scope); if (null !== $resultType) { return $resultType; } @@ -62,14 +67,22 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method return $defaultReturn; } - private function inferType(Expr $queryExpr, Expr $paramsExpr, Scope $scope): ?Type + private function inferType(Expr $queryExpr, ?Expr $paramsExpr, Scope $scope): ?Type { - $parameterTypes = $scope->getType($paramsExpr); + if (null === $paramsExpr) { + $queryReflection = new QueryReflection(); + $queryString = $queryReflection->resolveQueryString($queryExpr, $scope); + if (null === $queryString) { + return null; + } + } else { + $parameterTypes = $scope->getType($paramsExpr); - $queryReflection = new QueryReflection(); - $queryString = $queryReflection->resolvePreparedQueryString($queryExpr, $parameterTypes, $scope); - if (null === $queryString) { - return null; + $queryReflection = new QueryReflection(); + $queryString = $queryReflection->resolvePreparedQueryString($queryExpr, $parameterTypes, $scope); + if (null === $queryString) { + return null; + } } $resultType = $queryReflection->getResultType($queryString, QueryReflector::FETCH_TYPE_BOTH); diff --git a/src/Extensions/DoctrineConnectionFetchDynamicReturnTypeExtension.php b/src/Extensions/DoctrineConnectionFetchDynamicReturnTypeExtension.php index ed6c04271..0db7386e8 100644 --- a/src/Extensions/DoctrineConnectionFetchDynamicReturnTypeExtension.php +++ b/src/Extensions/DoctrineConnectionFetchDynamicReturnTypeExtension.php @@ -54,7 +54,7 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method $methodReflection->getVariants(), )->getReturnType(); - if (\count($args) < 2) { + if (\count($args) < 1) { return $defaultReturn; } @@ -67,7 +67,12 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method return $defaultReturn; } - $resultType = $this->inferType($methodReflection, $args[0]->value, $args[1]->value, $scope); + $params = null; + if (\count($args) > 1) { + $params = $args[1]->value; + } + + $resultType = $this->inferType($methodReflection, $args[0]->value, $params, $scope); if (null !== $resultType) { return $resultType; } @@ -75,14 +80,22 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method return $defaultReturn; } - private function inferType(MethodReflection $methodReflection, Expr $queryExpr, Expr $paramsExpr, Scope $scope): ?Type + private function inferType(MethodReflection $methodReflection, Expr $queryExpr, ?Expr $paramsExpr, Scope $scope): ?Type { - $parameterTypes = $scope->getType($paramsExpr); + if (null === $paramsExpr) { + $queryReflection = new QueryReflection(); + $queryString = $queryReflection->resolveQueryString($queryExpr, $scope); + if (null === $queryString) { + return null; + } + } else { + $parameterTypes = $scope->getType($paramsExpr); - $queryReflection = new QueryReflection(); - $queryString = $queryReflection->resolvePreparedQueryString($queryExpr, $parameterTypes, $scope); - if (null === $queryString) { - return null; + $queryReflection = new QueryReflection(); + $queryString = $queryReflection->resolvePreparedQueryString($queryExpr, $parameterTypes, $scope); + if (null === $queryString) { + return null; + } } $resultType = $queryReflection->getResultType($queryString, QueryReflector::FETCH_TYPE_BOTH); diff --git a/tests/default/data/doctrine-dbal.php b/tests/default/data/doctrine-dbal.php index 793be6a6d..7efa981ab 100644 --- a/tests/default/data/doctrine-dbal.php +++ b/tests/default/data/doctrine-dbal.php @@ -58,6 +58,9 @@ public function executeQuery(Connection $conn, array $types, QueryCacheProfile $ $stmt = $conn->executeCacheQuery('SELECT email, adaid, gesperrt, freigabe1u1 FROM ada WHERE adaid = ?', [1], $types, $qcp); assertType('Doctrine\DBAL\Result, 1: int<0, 4294967295>, gesperrt: int<-128, 127>, 2: int<-128, 127>, freigabe1u1: int<-128, 127>, 3: int<-128, 127>}>', $stmt); + + $stmt = $conn->executeQuery('SELECT email, adaid, gesperrt, freigabe1u1 FROM ada'); + assertType('Doctrine\DBAL\Result, 1: int<0, 4294967295>, gesperrt: int<-128, 127>, 2: int<-128, 127>, freigabe1u1: int<-128, 127>, 3: int<-128, 127>}>', $stmt); } public function executeStatement(Connection $conn, int $adaid) @@ -79,6 +82,10 @@ public function fetchAssociative(Connection $conn) $query = 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada WHERE adaid = ?'; $fetchResult = $conn->fetchAssociative($query, [1]); assertType('array{email: string, adaid: int<0, 4294967295>, gesperrt: int<-128, 127>, freigabe1u1: int<-128, 127>}|false', $fetchResult); + + $query = 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada'; + $fetchResult = $conn->fetchAssociative($query); + assertType('array{email: string, adaid: int<0, 4294967295>, gesperrt: int<-128, 127>, freigabe1u1: int<-128, 127>}|false', $fetchResult); } public function fetchNumeric(Connection $conn) @@ -86,6 +93,10 @@ public function fetchNumeric(Connection $conn) $query = 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada WHERE adaid = ?'; $fetchResult = $conn->fetchNumeric($query, [1]); assertType('array{string, int<0, 4294967295>, int<-128, 127>, int<-128, 127>}|false', $fetchResult); + + $query = 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada'; + $fetchResult = $conn->fetchNumeric($query); + assertType('array{string, int<0, 4294967295>, int<-128, 127>, int<-128, 127>}|false', $fetchResult); } public function iterateAssociative(Connection $conn) @@ -93,6 +104,10 @@ public function iterateAssociative(Connection $conn) $query = 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada WHERE adaid = ?'; $fetchResult = $conn->iterateAssociative($query, [1]); assertType('Traversable, gesperrt: int<-128, 127>, freigabe1u1: int<-128, 127>}>', $fetchResult); + + $query = 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada'; + $fetchResult = $conn->iterateAssociative($query); + assertType('Traversable, gesperrt: int<-128, 127>, freigabe1u1: int<-128, 127>}>', $fetchResult); } public function iterateNumeric(Connection $conn) @@ -100,6 +115,10 @@ public function iterateNumeric(Connection $conn) $query = 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada WHERE adaid = ?'; $fetchResult = $conn->iterateNumeric($query, [1]); assertType('Traversable, int<-128, 127>, int<-128, 127>}>', $fetchResult); + + $query = 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada'; + $fetchResult = $conn->iterateNumeric($query); + assertType('Traversable, int<-128, 127>, int<-128, 127>}>', $fetchResult); } public function iterateColumn(Connection $conn) @@ -107,6 +126,10 @@ public function iterateColumn(Connection $conn) $query = 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada WHERE adaid = ?'; $fetchResult = $conn->iterateColumn($query, [1]); assertType('Traversable', $fetchResult); + + $query = 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada'; + $fetchResult = $conn->iterateColumn($query); + assertType('Traversable', $fetchResult); } public function iterateKeyValue(Connection $conn) @@ -114,6 +137,10 @@ public function iterateKeyValue(Connection $conn) $query = 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada WHERE adaid = ?'; $fetchResult = $conn->iterateKeyValue($query, [1]); assertType('Traversable>', $fetchResult); + + $query = 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada'; + $fetchResult = $conn->iterateKeyValue($query); + assertType('Traversable>', $fetchResult); } public function fetchOne(Connection $conn) @@ -121,6 +148,10 @@ public function fetchOne(Connection $conn) $query = 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada WHERE adaid = ?'; $fetchResult = $conn->fetchOne($query, [1]); assertType('string|false', $fetchResult); + + $query = 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada'; + $fetchResult = $conn->fetchOne($query); + assertType('string|false', $fetchResult); } public function fetchFirstColumn(Connection $conn) @@ -128,6 +159,10 @@ public function fetchFirstColumn(Connection $conn) $query = 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada WHERE adaid = ?'; $fetchResult = $conn->fetchFirstColumn($query, [1]); assertType('array, string>', $fetchResult); + + $query = 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada'; + $fetchResult = $conn->fetchFirstColumn($query); + assertType('array, string>', $fetchResult); } public function fetchAllNumeric(Connection $conn) @@ -135,6 +170,10 @@ public function fetchAllNumeric(Connection $conn) $query = 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada WHERE adaid = ?'; $fetchResult = $conn->fetchAllNumeric($query, [1]); assertType('array, array{string, int<0, 4294967295>, int<-128, 127>, int<-128, 127>}>', $fetchResult); + + $query = 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada'; + $fetchResult = $conn->fetchAllNumeric($query); + assertType('array, array{string, int<0, 4294967295>, int<-128, 127>, int<-128, 127>}>', $fetchResult); } public function fetchAllAssociative(Connection $conn) @@ -142,6 +181,10 @@ public function fetchAllAssociative(Connection $conn) $query = 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada WHERE adaid = ?'; $fetchResult = $conn->fetchAllAssociative($query, [1]); assertType('array, array{email: string, adaid: int<0, 4294967295>, gesperrt: int<-128, 127>, freigabe1u1: int<-128, 127>}>', $fetchResult); + + $query = 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada'; + $fetchResult = $conn->fetchAllAssociative($query); + assertType('array, array{email: string, adaid: int<0, 4294967295>, gesperrt: int<-128, 127>, freigabe1u1: int<-128, 127>}>', $fetchResult); } public function fetchAllKeyValue(Connection $conn) @@ -149,6 +192,10 @@ public function fetchAllKeyValue(Connection $conn) $query = 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada WHERE adaid = ?'; $fetchResult = $conn->fetchAllKeyValue($query, [1]); assertType('array>', $fetchResult); + + $query = 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada'; + $fetchResult = $conn->fetchAllKeyValue($query); + assertType('array>', $fetchResult); } public function fetchStringable(Connection $conn) diff --git a/tests/stringify/config/.phpstan-dba.cache b/tests/stringify/config/.phpstan-dba.cache index 2c3c111b7..4c39b5c8b 100644 --- a/tests/stringify/config/.phpstan-dba.cache +++ b/tests/stringify/config/.phpstan-dba.cache @@ -4,6 +4,7 @@ array ( 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada' => array ( + 'error' => NULL, 'result' => array ( 3 => diff --git a/tests/stringify/config/.phpunit-phpstan-dba.cache b/tests/stringify/config/.phpunit-phpstan-dba.cache index 2c3c111b7..4c39b5c8b 100644 --- a/tests/stringify/config/.phpunit-phpstan-dba.cache +++ b/tests/stringify/config/.phpunit-phpstan-dba.cache @@ -4,6 +4,7 @@ array ( 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada' => array ( + 'error' => NULL, 'result' => array ( 3 =>