From e76a5de300feef45ef9222874edb38885d63cbaa Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sun, 20 Feb 2022 14:09:51 +0100 Subject: [PATCH 01/31] Create SchemaHasherMysql.php --- src/DbSchema/SchemaHasherMysql.php | 63 ++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 src/DbSchema/SchemaHasherMysql.php diff --git a/src/DbSchema/SchemaHasherMysql.php b/src/DbSchema/SchemaHasherMysql.php new file mode 100644 index 000000000..7b05fdbe3 --- /dev/null +++ b/src/DbSchema/SchemaHasherMysql.php @@ -0,0 +1,63 @@ +connection = $connection; + } + + public function hash(string $databaseName): string { + $query = ' + SELECT + MD5( + GROUP_CONCAT( + CONCAT( + COALESCE(COLUMN_NAME, ""), + COALESCE(EXTRA, ""), + COLUMN_TYPE, + IS_NULLABLE + ) + ) + ) AS dbsignature, + 1 AS grouper + FROM + information_schema.columns + WHERE + table_schema = ? + GROUP BY + grouper'; + + if ($this->connection instanceof PDO) { + $stmt = $this->connection->prepare($query); + $stmt->execute([$databaseName]); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + return isset($result[0]) ? $result[0]['dbsignature'] : ''; + } else { + $stmt = $this->connection->prepare($query); + if ($stmt === false) { + throw new ShouldNotHappenException('unable to prepare query'); + } + + $stmt->bind_param('s', $databaseName); + $stmt->execute(); + $result = $stmt->get_result(); + $row = $result->fetch_assoc(); + + return $row['dbsignature'] ?? ''; + } + } +} From f282895aa81091f7b080aa8f94ba1a48ae27f87b Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sun, 20 Feb 2022 14:43:55 +0100 Subject: [PATCH 02/31] Update SchemaHasherMysql.php --- src/DbSchema/SchemaHasherMysql.php | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/src/DbSchema/SchemaHasherMysql.php b/src/DbSchema/SchemaHasherMysql.php index 7b05fdbe3..739232f59 100644 --- a/src/DbSchema/SchemaHasherMysql.php +++ b/src/DbSchema/SchemaHasherMysql.php @@ -6,13 +6,13 @@ use mysqli; use PDO; -use PHPStan\ShouldNotHappenException; final class SchemaHasherMysql { /** * @var PDO|mysqli $connection */ private $connection; + /** * @param PDO|mysqli $connection */ @@ -20,7 +20,7 @@ public function __construct($connection) { $this->connection = $connection; } - public function hash(string $databaseName): string { + public function hash(): string { $query = ' SELECT MD5( @@ -37,24 +37,16 @@ public function hash(string $databaseName): string { FROM information_schema.columns WHERE - table_schema = ? + table_schema = DATABASE() GROUP BY grouper'; if ($this->connection instanceof PDO) { - $stmt = $this->connection->prepare($query); - $stmt->execute([$databaseName]); - $result = $stmt->fetch(PDO::FETCH_ASSOC); + $result = $this->connection->query($query); + return isset($result[0]) ? $result[0]['dbsignature'] : ''; } else { - $stmt = $this->connection->prepare($query); - if ($stmt === false) { - throw new ShouldNotHappenException('unable to prepare query'); - } - - $stmt->bind_param('s', $databaseName); - $stmt->execute(); - $result = $stmt->get_result(); + $result = $this->connection->query($query); $row = $result->fetch_assoc(); return $row['dbsignature'] ?? ''; From acd1918bc546e415e3578e6de659b694284598ab Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sun, 20 Feb 2022 14:47:25 +0100 Subject: [PATCH 03/31] generate hash just once --- src/DbSchema/SchemaHasherMysql.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/DbSchema/SchemaHasherMysql.php b/src/DbSchema/SchemaHasherMysql.php index 739232f59..d9d561784 100644 --- a/src/DbSchema/SchemaHasherMysql.php +++ b/src/DbSchema/SchemaHasherMysql.php @@ -13,6 +13,8 @@ final class SchemaHasherMysql { */ private $connection; + private ?string $hash; + /** * @param PDO|mysqli $connection */ @@ -21,6 +23,10 @@ public function __construct($connection) { } public function hash(): string { + if ($this->hash !== null) { + return $this->hash; + } + $query = ' SELECT MD5( @@ -44,12 +50,14 @@ public function hash(): string { if ($this->connection instanceof PDO) { $result = $this->connection->query($query); - return isset($result[0]) ? $result[0]['dbsignature'] : ''; + $hash = isset($result[0]) ? $result[0]['dbsignature'] : ''; } else { $result = $this->connection->query($query); $row = $result->fetch_assoc(); - return $row['dbsignature'] ?? ''; + $hash = $row['dbsignature'] ?? ''; } + + return $this->hash = $hash; } } From 4ca579b7e83fee93797c8b0d3832e6e7d4cfe77d Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sun, 20 Feb 2022 15:03:44 +0100 Subject: [PATCH 04/31] Update SchemaHasherMysql.php --- src/DbSchema/SchemaHasherMysql.php | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/DbSchema/SchemaHasherMysql.php b/src/DbSchema/SchemaHasherMysql.php index d9d561784..49548df27 100644 --- a/src/DbSchema/SchemaHasherMysql.php +++ b/src/DbSchema/SchemaHasherMysql.php @@ -6,6 +6,7 @@ use mysqli; use PDO; +use PHPStan\ShouldNotHappenException; final class SchemaHasherMysql { /** @@ -48,9 +49,10 @@ public function hash(): string { grouper'; if ($this->connection instanceof PDO) { - $result = $this->connection->query($query); - - $hash = isset($result[0]) ? $result[0]['dbsignature'] : ''; + $stmt = $this->connection->query($query); + foreach($stmt as $row) { + $hash = $row['dbsignature'] ?? ''; + } } else { $result = $this->connection->query($query); $row = $result->fetch_assoc(); @@ -58,6 +60,10 @@ public function hash(): string { $hash = $row['dbsignature'] ?? ''; } + if ($hash === '') { + throw new ShouldNotHappenException(); + } + return $this->hash = $hash; } } From 0319a44fa00cfdb4c8de4955e111109c329e0b11 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sun, 20 Feb 2022 15:17:21 +0100 Subject: [PATCH 05/31] Update SchemaHasherMysql.php --- src/DbSchema/SchemaHasherMysql.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DbSchema/SchemaHasherMysql.php b/src/DbSchema/SchemaHasherMysql.php index 49548df27..369121c47 100644 --- a/src/DbSchema/SchemaHasherMysql.php +++ b/src/DbSchema/SchemaHasherMysql.php @@ -14,7 +14,7 @@ final class SchemaHasherMysql { */ private $connection; - private ?string $hash; + private ?string $hash = null; /** * @param PDO|mysqli $connection From 07f83968ffc23c2633d1e64d84af9715edee591c Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sun, 20 Feb 2022 15:21:28 +0100 Subject: [PATCH 06/31] Update SchemaHasherMysql.php --- src/DbSchema/SchemaHasherMysql.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/DbSchema/SchemaHasherMysql.php b/src/DbSchema/SchemaHasherMysql.php index 369121c47..3d47dc37f 100644 --- a/src/DbSchema/SchemaHasherMysql.php +++ b/src/DbSchema/SchemaHasherMysql.php @@ -48,6 +48,7 @@ public function hash(): string { GROUP BY grouper'; + $hash = ''; if ($this->connection instanceof PDO) { $stmt = $this->connection->query($query); foreach($stmt as $row) { From cbec3159dcf6f15cf62092186610390bc881bb10 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sun, 20 Feb 2022 15:23:23 +0100 Subject: [PATCH 07/31] Update SchemaHasherMysql.php --- src/DbSchema/SchemaHasherMysql.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DbSchema/SchemaHasherMysql.php b/src/DbSchema/SchemaHasherMysql.php index 3d47dc37f..b6764e366 100644 --- a/src/DbSchema/SchemaHasherMysql.php +++ b/src/DbSchema/SchemaHasherMysql.php @@ -23,7 +23,7 @@ public function __construct($connection) { $this->connection = $connection; } - public function hash(): string { + public function hashDb(): string { if ($this->hash !== null) { return $this->hash; } From 243555daaac89b16e45a2fb686dd8aea2aff213e Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sun, 20 Feb 2022 15:41:33 +0100 Subject: [PATCH 08/31] implement ReplayAndRecordingQueryReflector --- .github/workflows/phpstan.yml | 3 + .github/workflows/tests.yml | 3 + .phpstan-dba-mysqli.cache | 1661 +++-------------- bootstrap.php | 4 + src/DbSchema/SchemaHasherMysql.php | 17 +- src/QueryReflection/ReflectionCache.php | 37 +- .../ReplayAndRecordingQueryReflector.php | 59 + tests/ReflectorFactory.php | 32 +- .../default/config/.phpstan-dba-mysqli.cache | 162 +- .../config/.phpunit-phpstan-dba-mysqli.cache | 3 +- .../config/.phpstan-dba-mysqli.cache | 3 +- .../config/.phpunit-phpstan-dba-mysqli.cache | 3 +- .../config/.phpstan-dba-mysqli.cache | 3 +- .../config/.phpunit-phpstan-dba-mysqli.cache | 3 +- tests/rules/config/.phpstan-dba-mysqli.cache | 11 +- .../config/.phpunit-phpstan-dba-mysqli.cache | 135 +- .../config/.phpstan-dba-mysqli.cache | 3 +- .../config/.phpunit-phpstan-dba-mysqli.cache | 3 +- 18 files changed, 367 insertions(+), 1778 deletions(-) create mode 100644 src/QueryReflection/ReplayAndRecordingQueryReflector.php diff --git a/.github/workflows/phpstan.yml b/.github/workflows/phpstan.yml index 564ac258d..f8f3f3b44 100644 --- a/.github/workflows/phpstan.yml +++ b/.github/workflows/phpstan.yml @@ -73,6 +73,9 @@ jobs: fail-fast: false matrix: include: + - php-version: "8.1" + reflector: "mysqli" + mode: "replay-and-recording" - php-version: "8.1" reflector: "mysqli" mode: "replay" diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 824f61eea..a48bbad6e 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -75,6 +75,9 @@ jobs: fail-fast: false matrix: include: + - php-version: "8.1" + reflector: "pdo" + mode: "replay-and-recording" - php-version: "8.1" reflector: "pdo" mode: "replay" diff --git a/.phpstan-dba-mysqli.cache b/.phpstan-dba-mysqli.cache index 2e18e8a6d..ff8d0e56a 100644 --- a/.phpstan-dba-mysqli.cache +++ b/.phpstan-dba-mysqli.cache @@ -1,13 +1,27 @@ 'v5-runtime-config-bugfix', + 'schemaVersion' => 'v6-schema-hash', + 'schemaHash' => '134ec79b27e531b457d91e12a24830db', 'records' => array ( - 'SELECT - coalesce(COLUMN_NAME, "") as COLUMN_NAME, - coalesce(EXTRA, "") as EXTRA, - COLUMN_TYPE - FROM information_schema.columns - WHERE table_name = \'1970-01-01\' AND table_schema = DATABASE()' => + ' + SELECT + MD5( + GROUP_CONCAT( + CONCAT( + COALESCE(COLUMN_NAME, ""), + COALESCE(EXTRA, ""), + COLUMN_TYPE, + IS_NULLABLE + ) + ) + ) AS dbsignature, + 1 AS grouper + FROM + information_schema.columns + WHERE + table_schema = DATABASE() + GROUP BY + grouper' => array ( 'result' => array ( @@ -18,7 +32,7 @@ array ( 0 => PHPStan\Type\Constant\ConstantStringType::__set_state(array( - 'value' => 'COLUMN_NAME', + 'value' => 'dbsignature', 'isClassString' => false, )), 1 => @@ -27,45 +41,48 @@ )), 2 => PHPStan\Type\Constant\ConstantStringType::__set_state(array( - 'value' => 'EXTRA', + 'value' => 'grouper', 'isClassString' => false, )), 3 => PHPStan\Type\Constant\ConstantIntegerType::__set_state(array( 'value' => 1, )), - 4 => - PHPStan\Type\Constant\ConstantStringType::__set_state(array( - 'value' => 'COLUMN_TYPE', - 'isClassString' => false, - )), - 5 => - PHPStan\Type\Constant\ConstantIntegerType::__set_state(array( - 'value' => 2, - )), ), 'valueTypes' => array ( 0 => - PHPStan\Type\StringType::__set_state(array( + PHPStan\Type\UnionType::__set_state(array( + 'types' => + array ( + 0 => + PHPStan\Type\StringType::__set_state(array( + )), + 1 => + PHPStan\Type\NullType::__set_state(array( + )), + ), )), 1 => - PHPStan\Type\StringType::__set_state(array( + PHPStan\Type\UnionType::__set_state(array( + 'types' => + array ( + 0 => + PHPStan\Type\StringType::__set_state(array( + )), + 1 => + PHPStan\Type\NullType::__set_state(array( + )), + ), )), 2 => - PHPStan\Type\StringType::__set_state(array( + PHPStan\Type\IntegerType::__set_state(array( )), 3 => - PHPStan\Type\StringType::__set_state(array( - )), - 4 => - PHPStan\Type\StringType::__set_state(array( - )), - 5 => - PHPStan\Type\StringType::__set_state(array( + PHPStan\Type\IntegerType::__set_state(array( )), ), - 'nextAutoIndex' => 3, + 'nextAutoIndex' => 2, 'optionalKeys' => array ( ), @@ -82,44 +99,105 @@ 'value' => 1, )), 2 => - PHPStan\Type\Constant\ConstantIntegerType::__set_state(array( - 'value' => 2, + PHPStan\Type\Constant\ConstantStringType::__set_state(array( + 'value' => 'dbsignature', + 'isClassString' => false, )), 3 => PHPStan\Type\Constant\ConstantStringType::__set_state(array( - 'value' => 'COLUMN_NAME', + 'value' => 'grouper', 'isClassString' => false, )), - 4 => + ), + )), + 'itemType' => + PHPStan\Type\UnionType::__set_state(array( + 'types' => + array ( + 0 => + PHPStan\Type\IntegerType::__set_state(array( + )), + 1 => + PHPStan\Type\StringType::__set_state(array( + )), + 2 => + PHPStan\Type\NullType::__set_state(array( + )), + ), + )), + )), + 3 => + PHPStan\Type\Constant\ConstantArrayType::__set_state(array( + 'allArrays' => NULL, + 'keyTypes' => + array ( + 0 => + PHPStan\Type\Constant\ConstantStringType::__set_state(array( + 'value' => 'dbsignature', + 'isClassString' => false, + )), + 1 => + PHPStan\Type\Constant\ConstantStringType::__set_state(array( + 'value' => 'grouper', + 'isClassString' => false, + )), + ), + 'valueTypes' => + array ( + 0 => + PHPStan\Type\UnionType::__set_state(array( + 'types' => + array ( + 0 => + PHPStan\Type\StringType::__set_state(array( + )), + 1 => + PHPStan\Type\NullType::__set_state(array( + )), + ), + )), + 1 => + PHPStan\Type\IntegerType::__set_state(array( + )), + ), + 'nextAutoIndex' => 0, + 'optionalKeys' => + array ( + ), + 'keyType' => + PHPStan\Type\UnionType::__set_state(array( + 'types' => + array ( + 0 => PHPStan\Type\Constant\ConstantStringType::__set_state(array( - 'value' => 'COLUMN_TYPE', + 'value' => 'dbsignature', 'isClassString' => false, )), - 5 => + 1 => PHPStan\Type\Constant\ConstantStringType::__set_state(array( - 'value' => 'EXTRA', + 'value' => 'grouper', 'isClassString' => false, )), ), )), 'itemType' => - PHPStan\Type\StringType::__set_state(array( + PHPStan\Type\UnionType::__set_state(array( + 'types' => + array ( + 0 => + PHPStan\Type\IntegerType::__set_state(array( + )), + 1 => + PHPStan\Type\StringType::__set_state(array( + )), + 2 => + PHPStan\Type\NullType::__set_state(array( + )), + ), )), )), ), ), - 'SELECT - coalesce(COLUMN_NAME, "") as COLUMN_NAME, - coalesce(EXTRA, "") as EXTRA, - COLUMN_TYPE - FROM information_schema.columns - WHERE table_name = ? AND table_schema = DATABASE()' => - array ( - 'result' => - array ( - 5 => NULL, - ), - ), 'SELECT coalesce(COLUMN_NAME, "") as COLUMN_NAME, coalesce(EXTRA, "") as EXTRA, @@ -1585,7 +1663,7 @@ )), ), ), - 'SELECT a.email, b.adaid, b.gesperrt FROM ada a LEFT JOIN ada b ON a.adaid=b.adaid' => + 'SELECT adaid FROM ada LIMIT 1 FOR SHARE' => array ( 'result' => array ( @@ -1596,246 +1674,28 @@ array ( 0 => PHPStan\Type\Constant\ConstantStringType::__set_state(array( - 'value' => 'email', + 'value' => 'adaid', '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, - )), ), 'valueTypes' => array ( 0 => - PHPStan\Type\StringType::__set_state(array( + PHPStan\Type\IntegerRangeType::__set_state(array( + 'min' => 0, + 'max' => 4294967295, )), 1 => - PHPStan\Type\StringType::__set_state(array( - )), - 2 => - PHPStan\Type\UnionType::__set_state(array( - 'types' => - array ( - 0 => - PHPStan\Type\IntegerRangeType::__set_state(array( - 'min' => 0, - 'max' => 4294967295, - )), - 1 => - PHPStan\Type\NullType::__set_state(array( - )), - ), - )), - 3 => - PHPStan\Type\UnionType::__set_state(array( - 'types' => - array ( - 0 => - PHPStan\Type\IntegerRangeType::__set_state(array( - 'min' => 0, - 'max' => 4294967295, - )), - 1 => - PHPStan\Type\NullType::__set_state(array( - )), - ), - )), - 4 => - PHPStan\Type\UnionType::__set_state(array( - 'types' => - array ( - 0 => - PHPStan\Type\IntegerRangeType::__set_state(array( - 'min' => -128, - 'max' => 127, - )), - 1 => - PHPStan\Type\NullType::__set_state(array( - )), - ), - )), - 5 => - PHPStan\Type\UnionType::__set_state(array( - 'types' => - array ( - 0 => - PHPStan\Type\IntegerRangeType::__set_state(array( - 'min' => -128, - 'max' => 127, - )), - 1 => - PHPStan\Type\NullType::__set_state(array( - )), - ), + PHPStan\Type\IntegerRangeType::__set_state(array( + 'min' => 0, + 'max' => 4294967295, )), ), - 'nextAutoIndex' => 3, - '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\ConstantStringType::__set_state(array( - 'value' => 'adaid', - 'isClassString' => false, - )), - 4 => - PHPStan\Type\Constant\ConstantStringType::__set_state(array( - 'value' => 'email', - 'isClassString' => false, - )), - 5 => - 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( - )), - 2 => - PHPStan\Type\NullType::__set_state(array( - )), - ), - )), - )), - ), - ), - 'SELECT adaid FROM ada' => - array ( - 'result' => - array ( - 5 => - PHPStan\Type\Constant\ConstantArrayType::__set_state(array( - 'allArrays' => NULL, - 'keyTypes' => - array ( - 0 => - PHPStan\Type\Constant\ConstantStringType::__set_state(array( - 'value' => 'adaid', - 'isClassString' => false, - )), - 1 => - PHPStan\Type\Constant\ConstantIntegerType::__set_state(array( - 'value' => 0, - )), - ), - 'valueTypes' => - array ( - 0 => - PHPStan\Type\IntegerRangeType::__set_state(array( - 'min' => 0, - 'max' => 4294967295, - )), - 1 => - PHPStan\Type\IntegerRangeType::__set_state(array( - 'min' => 0, - 'max' => 4294967295, - )), - ), - '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' => 'adaid', - 'isClassString' => false, - )), - ), - )), - 'itemType' => - PHPStan\Type\IntegerRangeType::__set_state(array( - 'min' => 0, - 'max' => 4294967295, - )), - )), - ), - ), - 'SELECT adaid FROM ada LIMIT 1 FOR SHARE' => - array ( - 'result' => - array ( - 5 => - PHPStan\Type\Constant\ConstantArrayType::__set_state(array( - 'allArrays' => NULL, - 'keyTypes' => - array ( - 0 => - PHPStan\Type\Constant\ConstantStringType::__set_state(array( - 'value' => 'adaid', - 'isClassString' => false, - )), - 1 => - PHPStan\Type\Constant\ConstantIntegerType::__set_state(array( - 'value' => 0, - )), - ), - 'valueTypes' => - array ( - 0 => - PHPStan\Type\IntegerRangeType::__set_state(array( - 'min' => 0, - 'max' => 4294967295, - )), - 1 => - PHPStan\Type\IntegerRangeType::__set_state(array( - 'min' => 0, - 'max' => 4294967295, - )), - ), - 'nextAutoIndex' => 1, + 'nextAutoIndex' => 1, 'optionalKeys' => array ( ), @@ -1980,66 +1840,7 @@ )), ), ), - 'SELECT adaid FROM ada WHERE adaid IN (\'1\')' => - array ( - 'result' => - array ( - 5 => - PHPStan\Type\Constant\ConstantArrayType::__set_state(array( - 'allArrays' => NULL, - 'keyTypes' => - array ( - 0 => - PHPStan\Type\Constant\ConstantStringType::__set_state(array( - 'value' => 'adaid', - 'isClassString' => false, - )), - 1 => - PHPStan\Type\Constant\ConstantIntegerType::__set_state(array( - 'value' => 0, - )), - ), - 'valueTypes' => - array ( - 0 => - PHPStan\Type\IntegerRangeType::__set_state(array( - 'min' => 0, - 'max' => 4294967295, - )), - 1 => - PHPStan\Type\IntegerRangeType::__set_state(array( - 'min' => 0, - 'max' => 4294967295, - )), - ), - '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' => 'adaid', - 'isClassString' => false, - )), - ), - )), - 'itemType' => - PHPStan\Type\IntegerRangeType::__set_state(array( - 'min' => 0, - 'max' => 4294967295, - )), - )), - ), - ), - 'SELECT adaid FROM ada WHERE adaid IN (\'1\') AND email LIKE \'1970-01-01\'' => + 'SELECT adaid FROM ada WHERE email LIKE ":gesperrt%"' => array ( 'result' => array ( @@ -2098,7 +1899,7 @@ )), ), ), - 'SELECT adaid FROM ada WHERE adaid IN (\'1.0\')' => + 'SELECT adaid FROM ada WHERE email LIKE "hello?%"' => array ( 'result' => array ( @@ -2157,28 +1958,7 @@ )), ), ), - 'SELECT adaid FROM ada WHERE adaid IN (:adaids)' => - array ( - 'result' => - array ( - 5 => NULL, - ), - ), - 'SELECT adaid FROM ada WHERE adaid IN (:ids)' => - array ( - 'result' => - array ( - 5 => NULL, - ), - ), - 'SELECT adaid FROM ada WHERE adaid IN (:ids) AND email LIKE :time' => - array ( - 'result' => - array ( - 5 => NULL, - ), - ), - 'SELECT adaid FROM ada WHERE adaid IN (NULL) AND email LIKE \'1970-01-01\'' => + 'SELECT adaid FROM ada WHERE email LIKE \'%questions ?%\'' => array ( 'result' => array ( @@ -2237,7 +2017,7 @@ )), ), ), - 'SELECT adaid FROM ada WHERE email LIKE ":gesperrt%"' => + 'SELECT adaid FROM ada WHERE email LIKE \':gesperrt%\'' => array ( 'result' => array ( @@ -2296,7 +2076,7 @@ )), ), ), - 'SELECT adaid FROM ada WHERE email LIKE "hello?%"' => + 'SELECT akid FROM ak WHERE eadavk>1.0' => array ( 'result' => array ( @@ -2307,7 +2087,7 @@ array ( 0 => PHPStan\Type\Constant\ConstantStringType::__set_state(array( - 'value' => 'adaid', + 'value' => 'akid', 'isClassString' => false, )), 1 => @@ -2319,13 +2099,13 @@ array ( 0 => PHPStan\Type\IntegerRangeType::__set_state(array( - 'min' => 0, - 'max' => 4294967295, + 'min' => -2147483648, + 'max' => 2147483647, )), 1 => PHPStan\Type\IntegerRangeType::__set_state(array( - 'min' => 0, - 'max' => 4294967295, + 'min' => -2147483648, + 'max' => 2147483647, )), ), 'nextAutoIndex' => 1, @@ -2342,20 +2122,20 @@ )), 1 => PHPStan\Type\Constant\ConstantStringType::__set_state(array( - 'value' => 'adaid', + 'value' => 'akid', 'isClassString' => false, )), ), )), 'itemType' => PHPStan\Type\IntegerRangeType::__set_state(array( - 'min' => 0, - 'max' => 4294967295, + 'min' => -2147483648, + 'max' => 2147483647, )), )), ), ), - 'SELECT adaid FROM ada WHERE email LIKE \'%questions ?%\'' => + 'SELECT akid FROM ak WHERE eadavk>1.1' => array ( 'result' => array ( @@ -2366,913 +2146,28 @@ array ( 0 => PHPStan\Type\Constant\ConstantStringType::__set_state(array( - 'value' => 'adaid', + 'value' => 'akid', 'isClassString' => false, )), 1 => PHPStan\Type\Constant\ConstantIntegerType::__set_state(array( - 'value' => 0, - )), - ), - 'valueTypes' => - array ( - 0 => - PHPStan\Type\IntegerRangeType::__set_state(array( - 'min' => 0, - 'max' => 4294967295, - )), - 1 => - PHPStan\Type\IntegerRangeType::__set_state(array( - 'min' => 0, - 'max' => 4294967295, - )), - ), - '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' => 'adaid', - 'isClassString' => false, - )), - ), - )), - 'itemType' => - PHPStan\Type\IntegerRangeType::__set_state(array( - 'min' => 0, - 'max' => 4294967295, - )), - )), - ), - ), - 'SELECT adaid FROM ada WHERE email LIKE \':gesperrt%\'' => - array ( - 'result' => - array ( - 5 => - PHPStan\Type\Constant\ConstantArrayType::__set_state(array( - 'allArrays' => NULL, - 'keyTypes' => - array ( - 0 => - PHPStan\Type\Constant\ConstantStringType::__set_state(array( - 'value' => 'adaid', - 'isClassString' => false, - )), - 1 => - PHPStan\Type\Constant\ConstantIntegerType::__set_state(array( - 'value' => 0, - )), - ), - 'valueTypes' => - array ( - 0 => - PHPStan\Type\IntegerRangeType::__set_state(array( - 'min' => 0, - 'max' => 4294967295, - )), - 1 => - PHPStan\Type\IntegerRangeType::__set_state(array( - 'min' => 0, - 'max' => 4294967295, - )), - ), - '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' => 'adaid', - 'isClassString' => false, - )), - ), - )), - 'itemType' => - PHPStan\Type\IntegerRangeType::__set_state(array( - 'min' => 0, - 'max' => 4294967295, - )), - )), - ), - ), - 'SELECT akid FROM ak WHERE eadavk>1.0' => - array ( - 'result' => - array ( - 5 => - PHPStan\Type\Constant\ConstantArrayType::__set_state(array( - 'allArrays' => NULL, - 'keyTypes' => - array ( - 0 => - PHPStan\Type\Constant\ConstantStringType::__set_state(array( - 'value' => 'akid', - 'isClassString' => false, - )), - 1 => - PHPStan\Type\Constant\ConstantIntegerType::__set_state(array( - 'value' => 0, - )), - ), - 'valueTypes' => - array ( - 0 => - PHPStan\Type\IntegerRangeType::__set_state(array( - 'min' => -2147483648, - 'max' => 2147483647, - )), - 1 => - PHPStan\Type\IntegerRangeType::__set_state(array( - 'min' => -2147483648, - 'max' => 2147483647, - )), - ), - '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' => 'akid', - 'isClassString' => false, - )), - ), - )), - 'itemType' => - PHPStan\Type\IntegerRangeType::__set_state(array( - 'min' => -2147483648, - 'max' => 2147483647, - )), - )), - ), - ), - 'SELECT akid FROM ak WHERE eadavk>1.1' => - array ( - 'result' => - array ( - 5 => - PHPStan\Type\Constant\ConstantArrayType::__set_state(array( - 'allArrays' => NULL, - 'keyTypes' => - array ( - 0 => - PHPStan\Type\Constant\ConstantStringType::__set_state(array( - 'value' => 'akid', - 'isClassString' => false, - )), - 1 => - PHPStan\Type\Constant\ConstantIntegerType::__set_state(array( - 'value' => 0, - )), - ), - 'valueTypes' => - array ( - 0 => - PHPStan\Type\IntegerRangeType::__set_state(array( - 'min' => -2147483648, - 'max' => 2147483647, - )), - 1 => - PHPStan\Type\IntegerRangeType::__set_state(array( - 'min' => -2147483648, - 'max' => 2147483647, - )), - ), - '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' => 'akid', - 'isClassString' => false, - )), - ), - )), - 'itemType' => - PHPStan\Type\IntegerRangeType::__set_state(array( - 'min' => -2147483648, - 'max' => 2147483647, - )), - )), - ), - ), - 'SELECT count(*) FROM typemix WHERE c_date = \'1970-01-01\'' => - array ( - '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 = \'1970-01-01\'' => - array ( - '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 eladaid FROM ak' => - array ( - 'result' => - array ( - 5 => - PHPStan\Type\Constant\ConstantArrayType::__set_state(array( - 'allArrays' => NULL, - 'keyTypes' => - array ( - 0 => - PHPStan\Type\Constant\ConstantStringType::__set_state(array( - 'value' => 'eladaid', - 'isClassString' => false, - )), - 1 => - PHPStan\Type\Constant\ConstantIntegerType::__set_state(array( - 'value' => 0, - )), - ), - 'valueTypes' => - array ( - 0 => - PHPStan\Type\UnionType::__set_state(array( - 'types' => - array ( - 0 => - PHPStan\Type\IntegerRangeType::__set_state(array( - 'min' => -2147483648, - 'max' => 2147483647, - )), - 1 => - PHPStan\Type\NullType::__set_state(array( - )), - ), - )), - 1 => - PHPStan\Type\UnionType::__set_state(array( - 'types' => - array ( - 0 => - PHPStan\Type\IntegerRangeType::__set_state(array( - 'min' => -2147483648, - 'max' => 2147483647, - )), - 1 => - PHPStan\Type\NullType::__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' => 'eladaid', - 'isClassString' => false, - )), - ), - )), - 'itemType' => - PHPStan\Type\UnionType::__set_state(array( - 'types' => - array ( - 0 => - PHPStan\Type\IntegerRangeType::__set_state(array( - 'min' => -2147483648, - 'max' => 2147483647, - )), - 1 => - PHPStan\Type\NullType::__set_state(array( - )), - ), - )), - )), - ), - ), - 'SELECT email FROM ada' => - array ( - '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, - )), - ), - 'valueTypes' => - array ( - 0 => - PHPStan\Type\StringType::__set_state(array( - )), - 1 => - PHPStan\Type\StringType::__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' => 'email', - 'isClassString' => false, - )), - ), - )), - 'itemType' => - PHPStan\Type\StringType::__set_state(array( - )), - )), - ), - ), - 'SELECT email adaid WHERE gesperrt freigabe1u1 FROM ada' => - array ( - 'result' => - array ( - 5 => NULL, - ), - ), - 'SELECT email, adaid FROM ada' => - array ( - '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, - )), - ), - '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, - )), - ), - 'nextAutoIndex' => 2, - '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\ConstantStringType::__set_state(array( - 'value' => 'adaid', - 'isClassString' => false, - )), - 3 => - PHPStan\Type\Constant\ConstantStringType::__set_state(array( - 'value' => 'email', - 'isClassString' => false, - )), - ), - )), - 'itemType' => - PHPStan\Type\UnionType::__set_state(array( - 'types' => - array ( - 0 => - PHPStan\Type\IntegerRangeType::__set_state(array( - 'min' => 0, - 'max' => 4294967295, - )), - 1 => - PHPStan\Type\StringType::__set_state(array( - )), - ), - )), - )), - ), - ), - 'SELECT email, adaid FROM ada WHERE adaid = \'1\'' => - array ( - '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, - )), - ), - '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, - )), - ), - 'nextAutoIndex' => 2, - '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\ConstantStringType::__set_state(array( - 'value' => 'adaid', - 'isClassString' => false, - )), - 3 => - PHPStan\Type\Constant\ConstantStringType::__set_state(array( - 'value' => 'email', - 'isClassString' => false, - )), - ), - )), - 'itemType' => - PHPStan\Type\UnionType::__set_state(array( - 'types' => - array ( - 0 => - PHPStan\Type\IntegerRangeType::__set_state(array( - 'min' => 0, - 'max' => 4294967295, - )), - 1 => - PHPStan\Type\StringType::__set_state(array( - )), - ), - )), - )), - ), - ), - 'SELECT email, adaid FROM ada WHERE adaid = ?' => - array ( - 'result' => - array ( - 5 => NULL, - ), - ), - 'SELECT email, adaid FROM ada WHERE adaid=1' => - array ( - '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, - )), - ), - '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, - )), - ), - 'nextAutoIndex' => 2, - '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\ConstantStringType::__set_state(array( - 'value' => 'adaid', - 'isClassString' => false, - )), - 3 => - PHPStan\Type\Constant\ConstantStringType::__set_state(array( - 'value' => 'email', - 'isClassString' => false, - )), - ), - )), - 'itemType' => - PHPStan\Type\UnionType::__set_state(array( - 'types' => - array ( - 0 => - PHPStan\Type\IntegerRangeType::__set_state(array( - 'min' => 0, - 'max' => 4294967295, - )), - 1 => - PHPStan\Type\StringType::__set_state(array( - )), - ), - )), - )), - ), - ), - 'SELECT email, adaid FROM ada WHERE email = \'test@example.org\'' => - array ( - '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, - )), - ), - '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, - )), - ), - 'nextAutoIndex' => 2, - '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\ConstantStringType::__set_state(array( - 'value' => 'adaid', - 'isClassString' => false, - )), - 3 => - PHPStan\Type\Constant\ConstantStringType::__set_state(array( - 'value' => 'email', - 'isClassString' => false, - )), - ), - )), - 'itemType' => - PHPStan\Type\UnionType::__set_state(array( - 'types' => - array ( - 0 => - PHPStan\Type\IntegerRangeType::__set_state(array( - 'min' => 0, - 'max' => 4294967295, - )), - 1 => - PHPStan\Type\StringType::__set_state(array( - )), - ), - )), - )), - ), - ), - 'SELECT email, adaid FROM ada WHERE email = ?' => - array ( - 'result' => - array ( - 5 => NULL, - ), - ), - 'SELECT email, adaid FROM ada WHERE email=\'test@example.org\'' => - array ( - '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, + 'value' => 0, )), ), '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, + 'min' => -2147483648, + 'max' => 2147483647, )), - 3 => + 1 => PHPStan\Type\IntegerRangeType::__set_state(array( - 'min' => 0, - 'max' => 4294967295, + 'min' => -2147483648, + 'max' => 2147483647, )), ), - 'nextAutoIndex' => 2, + 'nextAutoIndex' => 1, 'optionalKeys' => array ( ), @@ -3285,39 +2180,21 @@ 'value' => 0, )), 1 => - PHPStan\Type\Constant\ConstantIntegerType::__set_state(array( - 'value' => 1, - )), - 2 => - PHPStan\Type\Constant\ConstantStringType::__set_state(array( - 'value' => 'adaid', - 'isClassString' => false, - )), - 3 => PHPStan\Type\Constant\ConstantStringType::__set_state(array( - 'value' => 'email', + 'value' => 'akid', 'isClassString' => false, )), ), )), 'itemType' => - PHPStan\Type\UnionType::__set_state(array( - 'types' => - array ( - 0 => - PHPStan\Type\IntegerRangeType::__set_state(array( - 'min' => 0, - 'max' => 4294967295, - )), - 1 => - PHPStan\Type\StringType::__set_state(array( - )), - ), + PHPStan\Type\IntegerRangeType::__set_state(array( + 'min' => -2147483648, + 'max' => 2147483647, )), )), ), ), - 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada' => + 'SELECT eladaid FROM ak' => array ( 'result' => array ( @@ -3328,81 +2205,46 @@ array ( 0 => PHPStan\Type\Constant\ConstantStringType::__set_state(array( - 'value' => 'email', + 'value' => 'eladaid', '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( + PHPStan\Type\UnionType::__set_state(array( + 'types' => + array ( + 0 => + PHPStan\Type\IntegerRangeType::__set_state(array( + 'min' => -2147483648, + 'max' => 2147483647, + )), + 1 => + PHPStan\Type\NullType::__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, + PHPStan\Type\UnionType::__set_state(array( + 'types' => + array ( + 0 => + PHPStan\Type\IntegerRangeType::__set_state(array( + 'min' => -2147483648, + 'max' => 2147483647, + )), + 1 => + PHPStan\Type\NullType::__set_state(array( + )), + ), )), ), - 'nextAutoIndex' => 4, + 'nextAutoIndex' => 1, 'optionalKeys' => array ( ), @@ -3415,35 +2257,8 @@ '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', + 'value' => 'eladaid', 'isClassString' => false, )), ), @@ -3454,18 +2269,25 @@ array ( 0 => PHPStan\Type\IntegerRangeType::__set_state(array( - 'min' => -128, - 'max' => 4294967295, + 'min' => -2147483648, + 'max' => 2147483647, )), 1 => - PHPStan\Type\StringType::__set_state(array( + PHPStan\Type\NullType::__set_state(array( )), ), )), )), ), ), - 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada LIMIT 1' => + 'SELECT email adaid WHERE gesperrt freigabe1u1 FROM ada' => + array ( + 'result' => + array ( + 5 => NULL, + ), + ), + 'SELECT email, adaid FROM ada' => array ( 'result' => array ( @@ -3492,24 +2314,6 @@ 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 ( @@ -3529,28 +2333,8 @@ '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, + 'nextAutoIndex' => 2, 'optionalKeys' => array ( ), @@ -3567,33 +2351,15 @@ '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 => + 3 => 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' => @@ -3602,7 +2368,7 @@ array ( 0 => PHPStan\Type\IntegerRangeType::__set_state(array( - 'min' => -128, + 'min' => 0, 'max' => 4294967295, )), 1 => @@ -3613,7 +2379,7 @@ )), ), ), - 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada LIMIT 1, 10' => + 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada' => array ( 'result' => array ( @@ -3761,7 +2527,7 @@ )), ), ), - 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada WHERE adaid = \'1\'' => + 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada LIMIT 1' => array ( 'result' => array ( @@ -3909,7 +2675,7 @@ )), ), ), - 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada WHERE adaid = 1' => + 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada LIMIT 1, 10' => array ( 'result' => array ( @@ -4057,14 +2823,7 @@ )), ), ), - 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada WHERE adaid = ?' => - array ( - 'result' => - array ( - 5 => NULL, - ), - ), - 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada WHERE adaid IN(1,3)' => + 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada WHERE adaid = 1' => array ( 'result' => array ( @@ -4212,7 +2971,7 @@ )), ), ), - 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada WHERE adaid=1' => + 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada WHERE adaid IN(1,3)' => array ( 'result' => array ( @@ -4360,7 +3119,7 @@ )), ), ), - 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada WHERE email = \'1970-01-01\'' => + 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada WHERE adaid=1' => array ( 'result' => array ( diff --git a/bootstrap.php b/bootstrap.php index d657e68cf..f462a79f6 100644 --- a/bootstrap.php +++ b/bootstrap.php @@ -10,6 +10,10 @@ $config->errorMode(RuntimeConfiguration::ERROR_MODE_EXCEPTION); // $config->debugMode(true); +if (false === getenv('GITHUB_ACTION')) { + putenv('DBA_MODE=replay-and-recording'); +} + $reflector = ReflectorFactory::create(__DIR__); QueryReflection::setupReflector( diff --git a/src/DbSchema/SchemaHasherMysql.php b/src/DbSchema/SchemaHasherMysql.php index b6764e366..5bc83d74d 100644 --- a/src/DbSchema/SchemaHasherMysql.php +++ b/src/DbSchema/SchemaHasherMysql.php @@ -8,9 +8,10 @@ use PDO; use PHPStan\ShouldNotHappenException; -final class SchemaHasherMysql { +final class SchemaHasherMysql +{ /** - * @var PDO|mysqli $connection + * @var PDO|mysqli */ private $connection; @@ -19,12 +20,14 @@ final class SchemaHasherMysql { /** * @param PDO|mysqli $connection */ - public function __construct($connection) { + public function __construct($connection) + { $this->connection = $connection; } - public function hashDb(): string { - if ($this->hash !== null) { + public function hashDb(): string + { + if (null !== $this->hash) { return $this->hash; } @@ -51,7 +54,7 @@ public function hashDb(): string { $hash = ''; if ($this->connection instanceof PDO) { $stmt = $this->connection->query($query); - foreach($stmt as $row) { + foreach ($stmt as $row) { $hash = $row['dbsignature'] ?? ''; } } else { @@ -61,7 +64,7 @@ public function hashDb(): string { $hash = $row['dbsignature'] ?? ''; } - if ($hash === '') { + if ('' === $hash) { throw new ShouldNotHappenException(); } diff --git a/src/QueryReflection/ReflectionCache.php b/src/QueryReflection/ReflectionCache.php index 20877ccb2..d1ad0d5ab 100644 --- a/src/QueryReflection/ReflectionCache.php +++ b/src/QueryReflection/ReflectionCache.php @@ -12,7 +12,7 @@ final class ReflectionCache { - public const SCHEMA_VERSION = 'v5-runtime-config-bugfix'; + public const SCHEMA_VERSION = 'v6-schema-hash'; /** * @var string @@ -29,6 +29,11 @@ final class ReflectionCache */ private $changes = []; + /** + * @var string|null + */ + private $schemaHash; + /** * @var bool */ @@ -69,6 +74,26 @@ public static function load(string $cacheFile): self return new self($cacheFile); } + /** + * @return string|null + */ + public function getSchemaHash() + { + if (null === $this->schemaHash) { + $this->lazyReadRecords(); + } + + return $this->schemaHash; + } + + /** + * @return void + */ + public function setSchemaHash(string $hash) + { + $this->schemaHash = $hash; + } + /** * @return array}> */ @@ -111,7 +136,10 @@ private function readCachedRecords(bool $useReadLock): ?array } } - if (!\is_array($cache) || !\array_key_exists('schemaVersion', $cache) || self::SCHEMA_VERSION !== $cache['schemaVersion']) { + if (!\is_array($cache) || + !\array_key_exists('schemaVersion', $cache) || + !\array_key_exists('schemaHash', $cache) || + self::SCHEMA_VERSION !== $cache['schemaVersion']) { return null; } @@ -119,6 +147,10 @@ private function readCachedRecords(bool $useReadLock): ?array return null; } + if (null === $this->schemaHash) { + $this->schemaHash = $cache['schemaHash']; + } + if (!\is_array($cache['records'])) { throw new ShouldNotHappenException(); } @@ -156,6 +188,7 @@ public function persist(): void $cacheContent = ' self::SCHEMA_VERSION, + 'schemaHash' => $this->schemaHash, 'records' => $newRecords, 'runtimeConfig' => QueryReflection::getRuntimeConfiguration()->toArray(), ], true).';'; diff --git a/src/QueryReflection/ReplayAndRecordingQueryReflector.php b/src/QueryReflection/ReplayAndRecordingQueryReflector.php new file mode 100644 index 000000000..20efceedb --- /dev/null +++ b/src/QueryReflection/ReplayAndRecordingQueryReflector.php @@ -0,0 +1,59 @@ +replayReflector = new ReplayQueryReflector($reflectionCache); + + $this->queryReflector = $queryReflector; + $this->schemaHasher = $schemaHasher; + $this->reflectionCache = $reflectionCache; + } + + private function getRecordingReflector(): ?RecordingQueryReflector + { + if (null === $this->recordingReflector && $this->reflectionCache->getSchemaHash() !== $this->schemaHasher->hashDb()) { + $this->reflectionCache->setSchemaHash($this->schemaHasher->hashDb()); + + $this->recordingReflector = new RecordingQueryReflector($this->reflectionCache, $this->queryReflector); + } + + return $this->recordingReflector; + } + + public function validateQueryString(string $queryString): ?Error + { + $recordingReflector = $this->getRecordingReflector(); + if (null !== $recordingReflector) { + return $recordingReflector->validateQueryString($queryString); + } + + return $this->replayReflector->validateQueryString($queryString); + } + + public function getResultType(string $queryString, int $fetchType): ?Type + { + $recordingReflector = $this->getRecordingReflector(); + if (null !== $recordingReflector) { + return $recordingReflector->getResultType($queryString, $fetchType); + } + + return $this->replayReflector->getResultType($queryString, $fetchType); + } +} diff --git a/tests/ReflectorFactory.php b/tests/ReflectorFactory.php index 757e87a0e..18c8f890b 100644 --- a/tests/ReflectorFactory.php +++ b/tests/ReflectorFactory.php @@ -4,11 +4,13 @@ use mysqli; use PDO; +use staabm\PHPStanDba\DbSchema\SchemaHasherMysql; use staabm\PHPStanDba\QueryReflection\MysqliQueryReflector; use staabm\PHPStanDba\QueryReflection\PdoQueryReflector; use staabm\PHPStanDba\QueryReflection\QueryReflector; use staabm\PHPStanDba\QueryReflection\RecordingQueryReflector; use staabm\PHPStanDba\QueryReflection\ReflectionCache; +use staabm\PHPStanDba\QueryReflection\ReplayAndRecordingQueryReflector; use staabm\PHPStanDba\QueryReflection\ReplayQueryReflector; final class ReflectorFactory @@ -42,28 +44,38 @@ public static function create(string $cacheDir): QueryReflector $cacheFile = $cacheDir.'/.phpstan-dba-'.$reflector.'.cache'; } - if ('recording' === $mode) { + $reflectionCache = ReflectionCache::create( + $cacheFile + ); + + if ('recording' === $mode || 'replay-and-recording' === $mode) { if ('mysqli' === $reflector) { $mysqli = new mysqli($host, $user, $password, $dbname); $reflector = new MysqliQueryReflector($mysqli); + $schemaHasher = new SchemaHasherMysql($mysqli); } elseif ('pdo' === $reflector) { $pdo = new PDO(sprintf('mysql:dbname=%s;host=%s', $dbname, $host), $user, $password); $reflector = new PdoQueryReflector($pdo); + $schemaHasher = new SchemaHasherMysql($pdo); } else { throw new \RuntimeException('Unknown reflector: '.$reflector); } - $reflector = new RecordingQueryReflector( - ReflectionCache::create( - $cacheFile - ), - $reflector - ); + if ('replay-and-recording' === $mode) { + $reflector = new ReplayAndRecordingQueryReflector( + $reflectionCache, + $reflector, + $schemaHasher + ); + } else { + $reflector = new RecordingQueryReflector( + $reflectionCache, + $reflector + ); + } } elseif ('replay' === $mode) { $reflector = new ReplayQueryReflector( - ReflectionCache::create( - $cacheFile - ) + $reflectionCache ); } else { throw new \RuntimeException('Unknown mode: '.$mode); diff --git a/tests/default/config/.phpstan-dba-mysqli.cache b/tests/default/config/.phpstan-dba-mysqli.cache index 8c671ee01..ffc4f73d5 100644 --- a/tests/default/config/.phpstan-dba-mysqli.cache +++ b/tests/default/config/.phpstan-dba-mysqli.cache @@ -1,5 +1,6 @@ 'v5-runtime-config-bugfix', + 'schemaVersion' => 'v6-schema-hash', + 'schemaHash' => NULL, 'records' => array ( 'SELECT * FROM typemix' => @@ -1349,165 +1350,6 @@ )), ), ), - 'SELECT a.email, b.adaid, b.gesperrt FROM ada a LEFT JOIN ada b ON a.adaid=b.adaid' => - array ( - '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, - )), - ), - 'valueTypes' => - array ( - 0 => - PHPStan\Type\StringType::__set_state(array( - )), - 1 => - PHPStan\Type\StringType::__set_state(array( - )), - 2 => - PHPStan\Type\UnionType::__set_state(array( - 'types' => - array ( - 0 => - PHPStan\Type\IntegerRangeType::__set_state(array( - 'min' => 0, - 'max' => 4294967295, - )), - 1 => - PHPStan\Type\NullType::__set_state(array( - )), - ), - )), - 3 => - PHPStan\Type\UnionType::__set_state(array( - 'types' => - array ( - 0 => - PHPStan\Type\IntegerRangeType::__set_state(array( - 'min' => 0, - 'max' => 4294967295, - )), - 1 => - PHPStan\Type\NullType::__set_state(array( - )), - ), - )), - 4 => - PHPStan\Type\UnionType::__set_state(array( - 'types' => - array ( - 0 => - PHPStan\Type\IntegerRangeType::__set_state(array( - 'min' => -128, - 'max' => 127, - )), - 1 => - PHPStan\Type\NullType::__set_state(array( - )), - ), - )), - 5 => - PHPStan\Type\UnionType::__set_state(array( - 'types' => - array ( - 0 => - PHPStan\Type\IntegerRangeType::__set_state(array( - 'min' => -128, - 'max' => 127, - )), - 1 => - PHPStan\Type\NullType::__set_state(array( - )), - ), - )), - ), - 'nextAutoIndex' => 3, - '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\ConstantStringType::__set_state(array( - 'value' => 'adaid', - 'isClassString' => false, - )), - 4 => - PHPStan\Type\Constant\ConstantStringType::__set_state(array( - 'value' => 'email', - 'isClassString' => false, - )), - 5 => - 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( - )), - 2 => - PHPStan\Type\NullType::__set_state(array( - )), - ), - )), - )), - ), - ), 'SELECT adaid FROM ada' => array ( 'result' => diff --git a/tests/default/config/.phpunit-phpstan-dba-mysqli.cache b/tests/default/config/.phpunit-phpstan-dba-mysqli.cache index 5a05ff32c..2b05f8473 100644 --- a/tests/default/config/.phpunit-phpstan-dba-mysqli.cache +++ b/tests/default/config/.phpunit-phpstan-dba-mysqli.cache @@ -1,5 +1,6 @@ 'v5-runtime-config-bugfix', + 'schemaVersion' => 'v6-schema-hash', + 'schemaHash' => NULL, 'records' => array ( 'SELECT * FROM typemix' => diff --git a/tests/defaultFetchAssoc/config/.phpstan-dba-mysqli.cache b/tests/defaultFetchAssoc/config/.phpstan-dba-mysqli.cache index 2e85e5d4a..bd3337730 100644 --- a/tests/defaultFetchAssoc/config/.phpstan-dba-mysqli.cache +++ b/tests/defaultFetchAssoc/config/.phpstan-dba-mysqli.cache @@ -1,5 +1,6 @@ 'v5-runtime-config-bugfix', + 'schemaVersion' => 'v6-schema-hash', + 'schemaHash' => NULL, 'records' => array ( 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada' => diff --git a/tests/defaultFetchAssoc/config/.phpunit-phpstan-dba-mysqli.cache b/tests/defaultFetchAssoc/config/.phpunit-phpstan-dba-mysqli.cache index 2e85e5d4a..bd3337730 100644 --- a/tests/defaultFetchAssoc/config/.phpunit-phpstan-dba-mysqli.cache +++ b/tests/defaultFetchAssoc/config/.phpunit-phpstan-dba-mysqli.cache @@ -1,5 +1,6 @@ 'v5-runtime-config-bugfix', + 'schemaVersion' => 'v6-schema-hash', + 'schemaHash' => NULL, 'records' => array ( 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada' => diff --git a/tests/defaultFetchNumeric/config/.phpstan-dba-mysqli.cache b/tests/defaultFetchNumeric/config/.phpstan-dba-mysqli.cache index 2e85e5d4a..bd3337730 100644 --- a/tests/defaultFetchNumeric/config/.phpstan-dba-mysqli.cache +++ b/tests/defaultFetchNumeric/config/.phpstan-dba-mysqli.cache @@ -1,5 +1,6 @@ 'v5-runtime-config-bugfix', + 'schemaVersion' => 'v6-schema-hash', + 'schemaHash' => NULL, 'records' => array ( 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada' => diff --git a/tests/defaultFetchNumeric/config/.phpunit-phpstan-dba-mysqli.cache b/tests/defaultFetchNumeric/config/.phpunit-phpstan-dba-mysqli.cache index 2e85e5d4a..bd3337730 100644 --- a/tests/defaultFetchNumeric/config/.phpunit-phpstan-dba-mysqli.cache +++ b/tests/defaultFetchNumeric/config/.phpunit-phpstan-dba-mysqli.cache @@ -1,5 +1,6 @@ 'v5-runtime-config-bugfix', + 'schemaVersion' => 'v6-schema-hash', + 'schemaHash' => NULL, 'records' => array ( 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada' => diff --git a/tests/rules/config/.phpstan-dba-mysqli.cache b/tests/rules/config/.phpstan-dba-mysqli.cache index 9c571785b..85b20e992 100644 --- a/tests/rules/config/.phpstan-dba-mysqli.cache +++ b/tests/rules/config/.phpstan-dba-mysqli.cache @@ -1,5 +1,6 @@ 'v5-runtime-config-bugfix', + 'schemaVersion' => 'v6-schema-hash', + 'schemaHash' => NULL, 'records' => array ( 'SELECT * FROM ada GROUP BY doesNotExist' => @@ -23,13 +24,6 @@ 5 => NULL, ), ), - 'SELECT * FROM unknownTable' => - array ( - 'result' => - array ( - 5 => NULL, - ), - ), 'SELECT * FROM unknown_table' => array ( 'result' => @@ -979,6 +973,7 @@ array ( 'result' => array ( + 3 => NULL, 5 => NULL, ), ), diff --git a/tests/rules/config/.phpunit-phpstan-dba-mysqli.cache b/tests/rules/config/.phpunit-phpstan-dba-mysqli.cache index 39b9d5b00..11e5096aa 100644 --- a/tests/rules/config/.phpunit-phpstan-dba-mysqli.cache +++ b/tests/rules/config/.phpunit-phpstan-dba-mysqli.cache @@ -1,5 +1,6 @@ 'v5-runtime-config-bugfix', + 'schemaVersion' => 'v6-schema-hash', + 'schemaHash' => NULL, 'records' => array ( ' @@ -121,125 +122,6 @@ array ( 'error' => NULL, ), - ' - SELECT email adaid - WHERE gesperrt = \'1\' AND email LIKE \'%@example.com\' - FROM ada - LIMIT 1 - ' => - array ( - 'error' => - staabm\PHPStanDba\Error::__set_state(array( - '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 \'FROM ada LIMIT 0\' at line 3', - 'code' => 1064, - )), - ), - ' - SELECT email, adaid - FROM ada - WHERE gesperrt = \'1\' - FOR UPDATE - ' => - array ( - 'error' => NULL, - ), - ' - SELECT email, adaid - FROM ada - WHERE gesperrt = \'1\' - LIMIT \'1\' - ' => - array ( - 'error' => NULL, - ), - ' - SELECT email, adaid - FROM ada - WHERE gesperrt = \'1\' - LIMIT \'1\' - FOR UPDATE - ' => - array ( - 'error' => NULL, - ), - ' - SELECT email, adaid - FROM ada - WHERE gesperrt = \'1\' - LIMIT \'1\' - OFFSET \'1\' - ' => - array ( - 'error' => NULL, - ), - ' - SELECT email, adaid - FROM ada - WHERE gesperrt = \'1\' - LIMIT \'1\' - OFFSET \'1\' - FOR SHARE - ' => - array ( - 'error' => NULL, - ), - ' - SELECT email, adaid - FROM ada - WHERE gesperrt = \'1\' - LIMIT \'1\' - OFFSET \'1\' - FOR UPDATE - ' => - array ( - 'error' => NULL, - ), - ' - SELECT email, adaid - FROM ada - WHERE gesperrt = \'1\' - LIMIT \'1\' - OFFSET 1 - ' => - array ( - 'error' => NULL, - ), - ' - SELECT email, adaid - FROM ada - WHERE gesperrt = \'1\' - LIMIT \'1\', \'1\' - ' => - array ( - 'error' => NULL, - ), - ' - SELECT email, adaid - FROM ada - WHERE gesperrt = \'1\' - LIMIT \'1\', \'1\' - ' => - array ( - 'error' => NULL, - ), - ' - SELECT email, adaid - FROM ada - WHERE gesperrt = \'1\' AND email LIKE \'%@example%\' - LIMIT 1 - ' => - array ( - 'error' => NULL, - ), - ' - SELECT email, adaid - FROM ada - WHERE gesperrt = \'1\' AND email LIKE NULL - LIMIT 1 - ' => - array ( - 'error' => NULL, - ), 'SELECT * FROM ada GROUP BY doesNotExist' => array ( 'error' => @@ -264,14 +146,6 @@ 'code' => 1054, )), ), - 'SELECT * FROM unknownTable' => - array ( - 'error' => - staabm\PHPStanDba\Error::__set_state(array( - 'message' => 'Table \'phpstan_dba.unknownTable\' doesn\'t exist', - 'code' => 1146, - )), - ), 'SELECT * FROM unknown_table' => array ( 'error' => @@ -365,11 +239,6 @@ array ( 'error' => NULL, ), - 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada - WHERE (gesperrt=\'1\' AND freigabe1u1=1) OR (gesperrt=\'1\' AND freigabe1u1=0)' => - array ( - 'error' => NULL, - ), 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada LIMIT 1' => array ( 'error' => NULL, diff --git a/tests/stringify/config/.phpstan-dba-mysqli.cache b/tests/stringify/config/.phpstan-dba-mysqli.cache index e642704e4..5f95a8356 100644 --- a/tests/stringify/config/.phpstan-dba-mysqli.cache +++ b/tests/stringify/config/.phpstan-dba-mysqli.cache @@ -1,5 +1,6 @@ 'v5-runtime-config-bugfix', + 'schemaVersion' => 'v6-schema-hash', + 'schemaHash' => NULL, 'records' => array ( 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada' => diff --git a/tests/stringify/config/.phpunit-phpstan-dba-mysqli.cache b/tests/stringify/config/.phpunit-phpstan-dba-mysqli.cache index e642704e4..5f95a8356 100644 --- a/tests/stringify/config/.phpunit-phpstan-dba-mysqli.cache +++ b/tests/stringify/config/.phpunit-phpstan-dba-mysqli.cache @@ -1,5 +1,6 @@ 'v5-runtime-config-bugfix', + 'schemaVersion' => 'v6-schema-hash', + 'schemaHash' => NULL, 'records' => array ( 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada' => From d3f889be884782deccc5d3dab11568ec65408f5c Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sun, 20 Feb 2022 15:47:41 +0100 Subject: [PATCH 09/31] fix --- src/DbSchema/SchemaHasherMysql.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/DbSchema/SchemaHasherMysql.php b/src/DbSchema/SchemaHasherMysql.php index 5bc83d74d..c6d6a87c0 100644 --- a/src/DbSchema/SchemaHasherMysql.php +++ b/src/DbSchema/SchemaHasherMysql.php @@ -59,9 +59,10 @@ public function hashDb(): string } } else { $result = $this->connection->query($query); - $row = $result->fetch_assoc(); - - $hash = $row['dbsignature'] ?? ''; + if ($result instanceof \mysqli_result) { + $row = $result->fetch_assoc(); + $hash = $row['dbsignature'] ?? ''; + } } if ('' === $hash) { From ef754444f8b9897966a61182ff534266b96b3f9c Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sun, 20 Feb 2022 15:52:44 +0100 Subject: [PATCH 10/31] fix --- .github/workflows/phpstan.yml | 10 +++++++--- .github/workflows/tests.yml | 10 +++++++--- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/.github/workflows/phpstan.yml b/.github/workflows/phpstan.yml index f8f3f3b44..2e4785230 100644 --- a/.github/workflows/phpstan.yml +++ b/.github/workflows/phpstan.yml @@ -22,6 +22,7 @@ jobs: - php-version: "8.0" db-image: 'mysql:8.0' reflector: "mysqli" + - php-version: "8.1" db-image: 'mysql:8.0' reflector: "mysqli" @@ -29,8 +30,14 @@ jobs: db-image: 'mariadb:latest' reflector: "mysqli" + - php-version: "8.1" + db-image: 'mysql:8.0' + reflector: "mysqli" + mode: "replay-and-recording" + env: DBA_REFLECTOR: ${{ matrix.reflector }} + DBA_MODE: ${{ matrix.mode }} # https://docs.github.com/en/free-pro-team@latest/actions/guides/about-service-containers services: @@ -73,9 +80,6 @@ jobs: fail-fast: false matrix: include: - - php-version: "8.1" - reflector: "mysqli" - mode: "replay-and-recording" - php-version: "8.1" reflector: "mysqli" mode: "replay" diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a48bbad6e..a8dd90354 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -22,6 +22,7 @@ jobs: - php-version: "8.0" db-image: 'mysql:8.0' reflector: "mysqli" + - php-version: "8.1" db-image: 'mysql:8.0' reflector: "mysqli" @@ -29,8 +30,14 @@ jobs: db-image: 'mariadb:latest' reflector: "mysqli" + - php-version: "8.1" + db-image: 'mysql:8.0' + reflector: "pdo" + mode: "replay-and-recording" + env: DBA_REFLECTOR: ${{ matrix.reflector }} + DBA_MODE: ${{ matrix.mode }} # https://docs.github.com/en/free-pro-team@latest/actions/guides/about-service-containers services: @@ -75,9 +82,6 @@ jobs: fail-fast: false matrix: include: - - php-version: "8.1" - reflector: "pdo" - mode: "replay-and-recording" - php-version: "8.1" reflector: "pdo" mode: "replay" From 3755a075ad8608efee93e2a5fcb27a33a74789cd Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sun, 20 Feb 2022 15:59:39 +0100 Subject: [PATCH 11/31] regenerate --- .phpstan-dba-pdo.cache | 176 +++++++++++++----- bootstrap.php | 2 +- composer.json | 4 + tests/default/config/.phpstan-dba-pdo.cache | 162 +--------------- .../config/.phpunit-phpstan-dba-pdo.cache | 162 +--------------- .../config/.phpstan-dba-pdo.cache | 3 +- .../config/.phpunit-phpstan-dba-pdo.cache | 3 +- .../config/.phpstan-dba-pdo.cache | 3 +- .../config/.phpunit-phpstan-dba-pdo.cache | 3 +- tests/rules/config/.phpstan-dba-pdo.cache | 11 +- .../config/.phpunit-phpstan-dba-pdo.cache | 135 +------------- tests/stringify/config/.phpstan-dba-pdo.cache | 3 +- .../config/.phpunit-phpstan-dba-pdo.cache | 3 +- 13 files changed, 153 insertions(+), 517 deletions(-) diff --git a/.phpstan-dba-pdo.cache b/.phpstan-dba-pdo.cache index da3c67239..aa4bde890 100644 --- a/.phpstan-dba-pdo.cache +++ b/.phpstan-dba-pdo.cache @@ -1,13 +1,27 @@ 'v5-runtime-config-bugfix', + 'schemaVersion' => 'v6-schema-hash', + 'schemaHash' => NULL, 'records' => array ( - 'SELECT - coalesce(COLUMN_NAME, "") as COLUMN_NAME, - coalesce(EXTRA, "") as EXTRA, - COLUMN_TYPE - FROM information_schema.columns - WHERE table_name = \'1970-01-01\' AND table_schema = DATABASE()' => + ' + SELECT + MD5( + GROUP_CONCAT( + CONCAT( + COALESCE(COLUMN_NAME, ""), + COALESCE(EXTRA, ""), + COLUMN_TYPE, + IS_NULLABLE + ) + ) + ) AS dbsignature, + 1 AS grouper + FROM + information_schema.columns + WHERE + table_schema = DATABASE() + GROUP BY + grouper' => array ( 'result' => array ( @@ -18,7 +32,7 @@ array ( 0 => PHPStan\Type\Constant\ConstantStringType::__set_state(array( - 'value' => 'COLUMN_NAME', + 'value' => 'dbsignature', 'isClassString' => false, )), 1 => @@ -27,45 +41,48 @@ )), 2 => PHPStan\Type\Constant\ConstantStringType::__set_state(array( - 'value' => 'EXTRA', + 'value' => 'grouper', 'isClassString' => false, )), 3 => PHPStan\Type\Constant\ConstantIntegerType::__set_state(array( 'value' => 1, )), - 4 => - PHPStan\Type\Constant\ConstantStringType::__set_state(array( - 'value' => 'COLUMN_TYPE', - 'isClassString' => false, - )), - 5 => - PHPStan\Type\Constant\ConstantIntegerType::__set_state(array( - 'value' => 2, - )), ), 'valueTypes' => array ( 0 => - PHPStan\Type\StringType::__set_state(array( + PHPStan\Type\UnionType::__set_state(array( + 'types' => + array ( + 0 => + PHPStan\Type\StringType::__set_state(array( + )), + 1 => + PHPStan\Type\NullType::__set_state(array( + )), + ), )), 1 => - PHPStan\Type\StringType::__set_state(array( + PHPStan\Type\UnionType::__set_state(array( + 'types' => + array ( + 0 => + PHPStan\Type\StringType::__set_state(array( + )), + 1 => + PHPStan\Type\NullType::__set_state(array( + )), + ), )), 2 => - PHPStan\Type\StringType::__set_state(array( + PHPStan\Type\IntegerType::__set_state(array( )), 3 => - PHPStan\Type\StringType::__set_state(array( - )), - 4 => - PHPStan\Type\StringType::__set_state(array( - )), - 5 => - PHPStan\Type\StringType::__set_state(array( + PHPStan\Type\IntegerType::__set_state(array( )), ), - 'nextAutoIndex' => 3, + 'nextAutoIndex' => 2, 'optionalKeys' => array ( ), @@ -82,44 +99,105 @@ 'value' => 1, )), 2 => - PHPStan\Type\Constant\ConstantIntegerType::__set_state(array( - 'value' => 2, + PHPStan\Type\Constant\ConstantStringType::__set_state(array( + 'value' => 'dbsignature', + 'isClassString' => false, )), 3 => PHPStan\Type\Constant\ConstantStringType::__set_state(array( - 'value' => 'COLUMN_NAME', + 'value' => 'grouper', 'isClassString' => false, )), - 4 => + ), + )), + 'itemType' => + PHPStan\Type\UnionType::__set_state(array( + 'types' => + array ( + 0 => + PHPStan\Type\IntegerType::__set_state(array( + )), + 1 => + PHPStan\Type\StringType::__set_state(array( + )), + 2 => + PHPStan\Type\NullType::__set_state(array( + )), + ), + )), + )), + 3 => + PHPStan\Type\Constant\ConstantArrayType::__set_state(array( + 'allArrays' => NULL, + 'keyTypes' => + array ( + 0 => + PHPStan\Type\Constant\ConstantStringType::__set_state(array( + 'value' => 'dbsignature', + 'isClassString' => false, + )), + 1 => + PHPStan\Type\Constant\ConstantStringType::__set_state(array( + 'value' => 'grouper', + 'isClassString' => false, + )), + ), + 'valueTypes' => + array ( + 0 => + PHPStan\Type\UnionType::__set_state(array( + 'types' => + array ( + 0 => + PHPStan\Type\StringType::__set_state(array( + )), + 1 => + PHPStan\Type\NullType::__set_state(array( + )), + ), + )), + 1 => + PHPStan\Type\IntegerType::__set_state(array( + )), + ), + 'nextAutoIndex' => 0, + 'optionalKeys' => + array ( + ), + 'keyType' => + PHPStan\Type\UnionType::__set_state(array( + 'types' => + array ( + 0 => PHPStan\Type\Constant\ConstantStringType::__set_state(array( - 'value' => 'COLUMN_TYPE', + 'value' => 'dbsignature', 'isClassString' => false, )), - 5 => + 1 => PHPStan\Type\Constant\ConstantStringType::__set_state(array( - 'value' => 'EXTRA', + 'value' => 'grouper', 'isClassString' => false, )), ), )), 'itemType' => - PHPStan\Type\StringType::__set_state(array( + PHPStan\Type\UnionType::__set_state(array( + 'types' => + array ( + 0 => + PHPStan\Type\IntegerType::__set_state(array( + )), + 1 => + PHPStan\Type\StringType::__set_state(array( + )), + 2 => + PHPStan\Type\NullType::__set_state(array( + )), + ), )), )), ), ), - 'SELECT - coalesce(COLUMN_NAME, "") as COLUMN_NAME, - coalesce(EXTRA, "") as EXTRA, - COLUMN_TYPE - FROM information_schema.columns - WHERE table_name = ? AND table_schema = DATABASE()' => - array ( - 'result' => - array ( - 5 => NULL, - ), - ), 'SELECT coalesce(COLUMN_NAME, "") as COLUMN_NAME, coalesce(EXTRA, "") as EXTRA, diff --git a/bootstrap.php b/bootstrap.php index f462a79f6..4989f0c14 100644 --- a/bootstrap.php +++ b/bootstrap.php @@ -10,7 +10,7 @@ $config->errorMode(RuntimeConfiguration::ERROR_MODE_EXCEPTION); // $config->debugMode(true); -if (false === getenv('GITHUB_ACTION')) { +if (false === getenv('GITHUB_ACTION') && false === getenv('DBA_MODE')) { putenv('DBA_MODE=replay-and-recording'); } diff --git a/composer.json b/composer.json index 53c566d50..962c106bc 100644 --- a/composer.json +++ b/composer.json @@ -46,12 +46,16 @@ "@csfix" ], "record": [ + "@putenv DBA_MODE=recording", + "@putenv DBA_REFLECTOR=mysqli", "@phpunit", "@phpstan", + "@putenv DBA_REFLECTOR=pdo", "@phpunit", "@phpstan", + "@csfix" ], "phpstan": [ diff --git a/tests/default/config/.phpstan-dba-pdo.cache b/tests/default/config/.phpstan-dba-pdo.cache index 3b507a686..ffc4f73d5 100644 --- a/tests/default/config/.phpstan-dba-pdo.cache +++ b/tests/default/config/.phpstan-dba-pdo.cache @@ -1,5 +1,6 @@ 'v5-runtime-config-bugfix', + 'schemaVersion' => 'v6-schema-hash', + 'schemaHash' => NULL, 'records' => array ( 'SELECT * FROM typemix' => @@ -1349,165 +1350,6 @@ )), ), ), - 'SELECT a.email, b.adaid, b.gesperrt FROM ada a LEFT JOIN ada b ON a.adaid=b.adaid' => - array ( - '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, - )), - ), - 'valueTypes' => - array ( - 0 => - PHPStan\Type\StringType::__set_state(array( - )), - 1 => - PHPStan\Type\StringType::__set_state(array( - )), - 2 => - PHPStan\Type\UnionType::__set_state(array( - 'types' => - array ( - 0 => - PHPStan\Type\IntegerRangeType::__set_state(array( - 'min' => -2147483648, - 'max' => 2147483647, - )), - 1 => - PHPStan\Type\NullType::__set_state(array( - )), - ), - )), - 3 => - PHPStan\Type\UnionType::__set_state(array( - 'types' => - array ( - 0 => - PHPStan\Type\IntegerRangeType::__set_state(array( - 'min' => -2147483648, - 'max' => 2147483647, - )), - 1 => - PHPStan\Type\NullType::__set_state(array( - )), - ), - )), - 4 => - PHPStan\Type\UnionType::__set_state(array( - 'types' => - array ( - 0 => - PHPStan\Type\IntegerRangeType::__set_state(array( - 'min' => -128, - 'max' => 127, - )), - 1 => - PHPStan\Type\NullType::__set_state(array( - )), - ), - )), - 5 => - PHPStan\Type\UnionType::__set_state(array( - 'types' => - array ( - 0 => - PHPStan\Type\IntegerRangeType::__set_state(array( - 'min' => -128, - 'max' => 127, - )), - 1 => - PHPStan\Type\NullType::__set_state(array( - )), - ), - )), - ), - 'nextAutoIndex' => 3, - '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\ConstantStringType::__set_state(array( - 'value' => 'adaid', - 'isClassString' => false, - )), - 4 => - PHPStan\Type\Constant\ConstantStringType::__set_state(array( - 'value' => 'email', - 'isClassString' => false, - )), - 5 => - 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' => -2147483648, - 'max' => 2147483647, - )), - 1 => - PHPStan\Type\StringType::__set_state(array( - )), - 2 => - PHPStan\Type\NullType::__set_state(array( - )), - ), - )), - )), - ), - ), 'SELECT adaid FROM ada' => array ( 'result' => diff --git a/tests/default/config/.phpunit-phpstan-dba-pdo.cache b/tests/default/config/.phpunit-phpstan-dba-pdo.cache index 06e5b7f97..684117962 100644 --- a/tests/default/config/.phpunit-phpstan-dba-pdo.cache +++ b/tests/default/config/.phpunit-phpstan-dba-pdo.cache @@ -1,5 +1,6 @@ 'v5-runtime-config-bugfix', + 'schemaVersion' => 'v6-schema-hash', + 'schemaHash' => NULL, 'records' => array ( 'SELECT * FROM typemix' => @@ -1349,165 +1350,6 @@ )), ), ), - 'SELECT a.email, b.adaid, b.gesperrt FROM ada a LEFT JOIN ada b ON a.adaid=b.adaid' => - array ( - '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, - )), - ), - 'valueTypes' => - array ( - 0 => - PHPStan\Type\StringType::__set_state(array( - )), - 1 => - PHPStan\Type\StringType::__set_state(array( - )), - 2 => - PHPStan\Type\UnionType::__set_state(array( - 'types' => - array ( - 0 => - PHPStan\Type\IntegerRangeType::__set_state(array( - 'min' => -2147483648, - 'max' => 2147483647, - )), - 1 => - PHPStan\Type\NullType::__set_state(array( - )), - ), - )), - 3 => - PHPStan\Type\UnionType::__set_state(array( - 'types' => - array ( - 0 => - PHPStan\Type\IntegerRangeType::__set_state(array( - 'min' => -2147483648, - 'max' => 2147483647, - )), - 1 => - PHPStan\Type\NullType::__set_state(array( - )), - ), - )), - 4 => - PHPStan\Type\UnionType::__set_state(array( - 'types' => - array ( - 0 => - PHPStan\Type\IntegerRangeType::__set_state(array( - 'min' => -128, - 'max' => 127, - )), - 1 => - PHPStan\Type\NullType::__set_state(array( - )), - ), - )), - 5 => - PHPStan\Type\UnionType::__set_state(array( - 'types' => - array ( - 0 => - PHPStan\Type\IntegerRangeType::__set_state(array( - 'min' => -128, - 'max' => 127, - )), - 1 => - PHPStan\Type\NullType::__set_state(array( - )), - ), - )), - ), - 'nextAutoIndex' => 3, - '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\ConstantStringType::__set_state(array( - 'value' => 'adaid', - 'isClassString' => false, - )), - 4 => - PHPStan\Type\Constant\ConstantStringType::__set_state(array( - 'value' => 'email', - 'isClassString' => false, - )), - 5 => - 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' => -2147483648, - 'max' => 2147483647, - )), - 1 => - PHPStan\Type\StringType::__set_state(array( - )), - 2 => - PHPStan\Type\NullType::__set_state(array( - )), - ), - )), - )), - ), - ), 'SELECT adaid FROM ada' => array ( 'result' => diff --git a/tests/defaultFetchAssoc/config/.phpstan-dba-pdo.cache b/tests/defaultFetchAssoc/config/.phpstan-dba-pdo.cache index 2e85e5d4a..bd3337730 100644 --- a/tests/defaultFetchAssoc/config/.phpstan-dba-pdo.cache +++ b/tests/defaultFetchAssoc/config/.phpstan-dba-pdo.cache @@ -1,5 +1,6 @@ 'v5-runtime-config-bugfix', + 'schemaVersion' => 'v6-schema-hash', + 'schemaHash' => NULL, 'records' => array ( 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada' => diff --git a/tests/defaultFetchAssoc/config/.phpunit-phpstan-dba-pdo.cache b/tests/defaultFetchAssoc/config/.phpunit-phpstan-dba-pdo.cache index 2e85e5d4a..bd3337730 100644 --- a/tests/defaultFetchAssoc/config/.phpunit-phpstan-dba-pdo.cache +++ b/tests/defaultFetchAssoc/config/.phpunit-phpstan-dba-pdo.cache @@ -1,5 +1,6 @@ 'v5-runtime-config-bugfix', + 'schemaVersion' => 'v6-schema-hash', + 'schemaHash' => NULL, 'records' => array ( 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada' => diff --git a/tests/defaultFetchNumeric/config/.phpstan-dba-pdo.cache b/tests/defaultFetchNumeric/config/.phpstan-dba-pdo.cache index 2e85e5d4a..bd3337730 100644 --- a/tests/defaultFetchNumeric/config/.phpstan-dba-pdo.cache +++ b/tests/defaultFetchNumeric/config/.phpstan-dba-pdo.cache @@ -1,5 +1,6 @@ 'v5-runtime-config-bugfix', + 'schemaVersion' => 'v6-schema-hash', + 'schemaHash' => NULL, 'records' => array ( 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada' => diff --git a/tests/defaultFetchNumeric/config/.phpunit-phpstan-dba-pdo.cache b/tests/defaultFetchNumeric/config/.phpunit-phpstan-dba-pdo.cache index 2e85e5d4a..bd3337730 100644 --- a/tests/defaultFetchNumeric/config/.phpunit-phpstan-dba-pdo.cache +++ b/tests/defaultFetchNumeric/config/.phpunit-phpstan-dba-pdo.cache @@ -1,5 +1,6 @@ 'v5-runtime-config-bugfix', + 'schemaVersion' => 'v6-schema-hash', + 'schemaHash' => NULL, 'records' => array ( 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada' => diff --git a/tests/rules/config/.phpstan-dba-pdo.cache b/tests/rules/config/.phpstan-dba-pdo.cache index 072b54040..85b20e992 100644 --- a/tests/rules/config/.phpstan-dba-pdo.cache +++ b/tests/rules/config/.phpstan-dba-pdo.cache @@ -1,5 +1,6 @@ 'v5-runtime-config-bugfix', + 'schemaVersion' => 'v6-schema-hash', + 'schemaHash' => NULL, 'records' => array ( 'SELECT * FROM ada GROUP BY doesNotExist' => @@ -23,13 +24,6 @@ 5 => NULL, ), ), - 'SELECT * FROM unknownTable' => - array ( - 'result' => - array ( - 5 => NULL, - ), - ), 'SELECT * FROM unknown_table' => array ( 'result' => @@ -980,6 +974,7 @@ 'result' => array ( 3 => NULL, + 5 => NULL, ), ), 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada WHERE gesperrt=1' => diff --git a/tests/rules/config/.phpunit-phpstan-dba-pdo.cache b/tests/rules/config/.phpunit-phpstan-dba-pdo.cache index b2cd9735a..d62c55139 100644 --- a/tests/rules/config/.phpunit-phpstan-dba-pdo.cache +++ b/tests/rules/config/.phpunit-phpstan-dba-pdo.cache @@ -1,5 +1,6 @@ 'v5-runtime-config-bugfix', + 'schemaVersion' => 'v6-schema-hash', + 'schemaHash' => NULL, 'records' => array ( ' @@ -121,125 +122,6 @@ array ( 'error' => NULL, ), - ' - SELECT email adaid - WHERE gesperrt = \'1\' AND email LIKE \'%@example.com\' - FROM ada - LIMIT 1 - ' => - array ( - 'error' => - staabm\PHPStanDba\Error::__set_state(array( - 'message' => 'SQLSTATE[42000]: Syntax error or access violation: 1064 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 \'FROM ada LIMIT 0\' at line 3', - 'code' => '42000', - )), - ), - ' - SELECT email, adaid - FROM ada - WHERE gesperrt = \'1\' - FOR UPDATE - ' => - array ( - 'error' => NULL, - ), - ' - SELECT email, adaid - FROM ada - WHERE gesperrt = \'1\' - LIMIT \'1\' - ' => - array ( - 'error' => NULL, - ), - ' - SELECT email, adaid - FROM ada - WHERE gesperrt = \'1\' - LIMIT \'1\' - FOR UPDATE - ' => - array ( - 'error' => NULL, - ), - ' - SELECT email, adaid - FROM ada - WHERE gesperrt = \'1\' - LIMIT \'1\' - OFFSET \'1\' - ' => - array ( - 'error' => NULL, - ), - ' - SELECT email, adaid - FROM ada - WHERE gesperrt = \'1\' - LIMIT \'1\' - OFFSET \'1\' - FOR SHARE - ' => - array ( - 'error' => NULL, - ), - ' - SELECT email, adaid - FROM ada - WHERE gesperrt = \'1\' - LIMIT \'1\' - OFFSET \'1\' - FOR UPDATE - ' => - array ( - 'error' => NULL, - ), - ' - SELECT email, adaid - FROM ada - WHERE gesperrt = \'1\' - LIMIT \'1\' - OFFSET 1 - ' => - array ( - 'error' => NULL, - ), - ' - SELECT email, adaid - FROM ada - WHERE gesperrt = \'1\' - LIMIT \'1\', \'1\' - ' => - array ( - 'error' => NULL, - ), - ' - SELECT email, adaid - FROM ada - WHERE gesperrt = \'1\' - LIMIT \'1\', \'1\' - ' => - array ( - 'error' => NULL, - ), - ' - SELECT email, adaid - FROM ada - WHERE gesperrt = \'1\' AND email LIKE \'%@example%\' - LIMIT 1 - ' => - array ( - 'error' => NULL, - ), - ' - SELECT email, adaid - FROM ada - WHERE gesperrt = \'1\' AND email LIKE NULL - LIMIT 1 - ' => - array ( - 'error' => NULL, - ), 'SELECT * FROM ada GROUP BY doesNotExist' => array ( 'error' => @@ -264,14 +146,6 @@ 'code' => '42S22', )), ), - 'SELECT * FROM unknownTable' => - array ( - 'error' => - staabm\PHPStanDba\Error::__set_state(array( - 'message' => 'SQLSTATE[42S02]: Base table or view not found: 1146 Table \'phpstan_dba.unknownTable\' doesn\'t exist', - 'code' => '42S02', - )), - ), 'SELECT * FROM unknown_table' => array ( 'error' => @@ -365,11 +239,6 @@ array ( 'error' => NULL, ), - 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada - WHERE (gesperrt=\'1\' AND freigabe1u1=1) OR (gesperrt=\'1\' AND freigabe1u1=0)' => - array ( - 'error' => NULL, - ), 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada LIMIT 1' => array ( 'error' => NULL, diff --git a/tests/stringify/config/.phpstan-dba-pdo.cache b/tests/stringify/config/.phpstan-dba-pdo.cache index e642704e4..5f95a8356 100644 --- a/tests/stringify/config/.phpstan-dba-pdo.cache +++ b/tests/stringify/config/.phpstan-dba-pdo.cache @@ -1,5 +1,6 @@ 'v5-runtime-config-bugfix', + 'schemaVersion' => 'v6-schema-hash', + 'schemaHash' => NULL, 'records' => array ( 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada' => diff --git a/tests/stringify/config/.phpunit-phpstan-dba-pdo.cache b/tests/stringify/config/.phpunit-phpstan-dba-pdo.cache index e642704e4..5f95a8356 100644 --- a/tests/stringify/config/.phpunit-phpstan-dba-pdo.cache +++ b/tests/stringify/config/.phpunit-phpstan-dba-pdo.cache @@ -1,5 +1,6 @@ 'v5-runtime-config-bugfix', + 'schemaVersion' => 'v6-schema-hash', + 'schemaHash' => NULL, 'records' => array ( 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada' => From 913efe300a8beb7d8d2c34280948bdf9ef894d60 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sun, 20 Feb 2022 17:28:53 +0100 Subject: [PATCH 12/31] Update README.md --- README.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 279a4fa40..5eae085f8 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ composer require --dev staabm/phpstan-dba use staabm\PHPStanDba\QueryReflection\RuntimeConfiguration; use staabm\PHPStanDba\QueryReflection\MysqliQueryReflector; use staabm\PHPStanDba\QueryReflection\QueryReflection; -use staabm\PHPStanDba\QueryReflection\RecordingQueryReflector; +use staabm\PHPStanDba\QueryReflection\ReplayAndRecordingQueryReflector; use staabm\PHPStanDba\QueryReflection\ReplayQueryReflector; use staabm\PHPStanDba\QueryReflection\ReflectionCache; @@ -51,13 +51,17 @@ $config = new RuntimeConfiguration(); // $config->debugMode(true); // $config->stringifyTypes(true); +// TODO: Put your database credentials here +$mysqli = new mysqli('hostname', 'username', 'password', 'database'); + QueryReflection::setupReflector( - new RecordingQueryReflector( + new ReplayAndRecordingQueryReflector( ReflectionCache::create( $cacheFile ), - // TODO: Put your database credentials here - new MysqliQueryReflector(new mysqli('hostname', 'username', 'password', 'database')) + new MysqliQueryReflector($mysqli), + new SchemaHasherMysql($mysqli) + ), $config ); From f5352ccfa84399aec245164732298f3b7b8d6b13 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sun, 20 Feb 2022 17:33:13 +0100 Subject: [PATCH 13/31] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 5eae085f8..b5965e4ee 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,7 @@ composer require --dev staabm/phpstan-dba ```php Date: Sun, 20 Feb 2022 18:04:02 +0100 Subject: [PATCH 14/31] Update phpstan.yml --- .github/workflows/phpstan.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/phpstan.yml b/.github/workflows/phpstan.yml index 2e4785230..e3fee5e55 100644 --- a/.github/workflows/phpstan.yml +++ b/.github/workflows/phpstan.yml @@ -19,16 +19,20 @@ jobs: - php-version: "8.0" db-image: 'mysql:8.0' reflector: "pdo" + mode: "recording" - php-version: "8.0" db-image: 'mysql:8.0' reflector: "mysqli" + mode: "recording" - php-version: "8.1" db-image: 'mysql:8.0' reflector: "mysqli" + mode: "recording" - php-version: '8.1' db-image: 'mariadb:latest' reflector: "mysqli" + mode: "recording" - php-version: "8.1" db-image: 'mysql:8.0' From f1126f2c1e69e89615fdab0600d4da161999026c Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sun, 20 Feb 2022 18:04:31 +0100 Subject: [PATCH 15/31] Update tests.yml --- .github/workflows/tests.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a8dd90354..6158ab941 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -19,16 +19,20 @@ jobs: - php-version: "8.0" db-image: 'mysql:8.0' reflector: "pdo" + mode: "recording" - php-version: "8.0" db-image: 'mysql:8.0' reflector: "mysqli" + mode: "recording" - php-version: "8.1" db-image: 'mysql:8.0' reflector: "mysqli" + mode: "recording" - php-version: '8.1' db-image: 'mariadb:latest' reflector: "mysqli" + mode: "recording" - php-version: "8.1" db-image: 'mysql:8.0' From 2bba8246fc434faaa9c4ada71a80cb3ea4648f02 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sun, 20 Feb 2022 18:07:05 +0100 Subject: [PATCH 16/31] Update README.md --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index b5965e4ee..4bf658b4f 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ QueryReflection::setupReflector( ReflectionCache::create( $cacheFile ), - // XXX alternatively you might use PdoQueryReflector instead + // XXX alternatively you can use PdoQueryReflector instead new MysqliQueryReflector($mysqli), new SchemaHasherMysql($mysqli) @@ -77,9 +77,9 @@ Your `phpstan.neon` might look something like: ```neon parameters: - level: 9 + level: 8 paths: - - public + - src/ bootstrapFiles: - phpstan-dba-bootstrap.php @@ -106,7 +106,8 @@ If not configured otherwise, the following defaults are used: ### Record and Replay -In case you don't want to depend on a database at PHPStan analysis time, you can use one of the `*RecordingQueryReflector` classes to record the reflection information. +In case you don't want to depend on a database at PHPStan analysis time, you can use one of the `*RecordingQueryReflector`-classes to record the reflection information. + With this cache file you can utilize [`ReplayQueryReflector`](https://github.com/staabm/phpstan-dba/blob/main/src/QueryReflection/ReplayQueryReflector.php) to replay the reflection information, without the need for a active database connection. ```php From 4e372742cfd81a1df3039dd2007719ba9f648e82 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sun, 20 Feb 2022 21:16:51 +0100 Subject: [PATCH 17/31] Update README.md --- README.md | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4bf658b4f..b40537568 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ QueryReflection::setupReflector( // XXX alternatively you can use PdoQueryReflector instead new MysqliQueryReflector($mysqli), new SchemaHasherMysql($mysqli) - + ), $config ); @@ -142,6 +142,42 @@ This might be usefull if your CI pipeline can't/shouldn't connect to your develo **Note**: In case you commit the generated cache files into your repository, consider [marking them as generated files](https://docs.github.com/en/repositories/working-with-files/managing-files/customizing-how-changed-files-appear-on-github), so they don't show up in Pull Requests. +## Reflector Overview + +### Backend Connecting Reflector + +These reflectors connect to a real database, infers types based on result-set metadata and are able to detect errors within a given sql query. + +| Reflector | Key Features | +|----------------------|--------------------------------------------------------| +| MysqliQueryReflector | - limited to mysql/mariadb databases | +| | - requires a active database connection | +| | - most feature complete reflector | +|----------------------| ---------------------------------------- | +| PdoQueryReflector | - connects to a mysql/mariadb database | +| | - requires a active database connection | +| | - can be used as a foundation for other database types in the future | + +### Utility Reflectors + +Utility reflectors will be used in combination with backend connecting reflectors to provide additional features. + +| Reflector | Key Features | +|----------------------------------|-------------------------------------------------------------------------------------------------------------------------| +| ChainedReflector | - chain several backend connecting reflectors, so applications which use multiple database connections can be analyzed | +| ------------------ | ------------------------------------------------------------------------------------------------------------------------ | +| RecordingQueryReflector | - wraps a backend connecting reflector and caches the reflected information into a local file | +| ------------------ | ------------------------------------------------------------------------------------------------------------------------ | +| ReplayQueryReflector | - utilizes the cached information of a *RecordingQueryReflector | +| | - will **not** validate the cached information, therefore might return stale results | +| | - does **not** require a active database connection | +| ------------------ | ------------------------------------------------------------------------------------------------------------------------ | +| ReplayAndRecordingQueryReflector | - utilizes the cached information of a *RecordingQueryReflector | +| | - will validate the cached information | +| | - will update local cache file information, even on external changes | +| | - will reduce database interactions to a minimum, but still requires a active database connection | +| ------------------ | ------------------------------------------------------------------------------------------------------------------------ | + ## Advanced Usage ### use `SyntaxErrorInPreparedStatementMethodRule` for your custom classes From 2660cc1a34630dcbd2e5af95d7417daaabcbc2f6 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sun, 20 Feb 2022 21:24:20 +0100 Subject: [PATCH 18/31] Update README.md --- README.md | 34 ++++++++++------------------------ 1 file changed, 10 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index b40537568..6e2ea0cd2 100644 --- a/README.md +++ b/README.md @@ -148,35 +148,21 @@ This might be usefull if your CI pipeline can't/shouldn't connect to your develo These reflectors connect to a real database, infers types based on result-set metadata and are able to detect errors within a given sql query. -| Reflector | Key Features | -|----------------------|--------------------------------------------------------| -| MysqliQueryReflector | - limited to mysql/mariadb databases | -| | - requires a active database connection | -| | - most feature complete reflector | -|----------------------| ---------------------------------------- | -| PdoQueryReflector | - connects to a mysql/mariadb database | -| | - requires a active database connection | -| | - can be used as a foundation for other database types in the future | +| Reflector | Key Features | +|----------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------| +| MysqliQueryReflector | - limited to mysql/mariadb databases
- requires a active database connection
- most feature complete reflector | +| PdoQueryReflector | - connects to a mysql/mariadb database
- requires a active database connection
- can be used as a foundation for other database types in the future | ### Utility Reflectors Utility reflectors will be used in combination with backend connecting reflectors to provide additional features. -| Reflector | Key Features | -|----------------------------------|-------------------------------------------------------------------------------------------------------------------------| -| ChainedReflector | - chain several backend connecting reflectors, so applications which use multiple database connections can be analyzed | -| ------------------ | ------------------------------------------------------------------------------------------------------------------------ | -| RecordingQueryReflector | - wraps a backend connecting reflector and caches the reflected information into a local file | -| ------------------ | ------------------------------------------------------------------------------------------------------------------------ | -| ReplayQueryReflector | - utilizes the cached information of a *RecordingQueryReflector | -| | - will **not** validate the cached information, therefore might return stale results | -| | - does **not** require a active database connection | -| ------------------ | ------------------------------------------------------------------------------------------------------------------------ | -| ReplayAndRecordingQueryReflector | - utilizes the cached information of a *RecordingQueryReflector | -| | - will validate the cached information | -| | - will update local cache file information, even on external changes | -| | - will reduce database interactions to a minimum, but still requires a active database connection | -| ------------------ | ------------------------------------------------------------------------------------------------------------------------ | +| Reflector | Key Features | +|----------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| ReplayAndRecordingQueryReflector | - utilizes the cached information of a *RecordingQueryReflector
- will validate the cached information
- will update local cache file information, even on external changes
- will reduce database interactions to a minimum, but still requires a active database connection | +| RecordingQueryReflector | - wraps a backend connecting reflector and caches the reflected information into a local file | +| ReplayQueryReflector | - utilizes the cached information of a *RecordingQueryReflector
- will **not** validate the cached information, therefore might return stale results
- does **not** require a active database connection | +| ChainedReflector | - chain several backend connecting reflectors, so applications which use multiple database connections can be analyzed | ## Advanced Usage From e6945d266481d85c1d3ffef30ac1c702c0743afe Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sun, 20 Feb 2022 21:29:55 +0100 Subject: [PATCH 19/31] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6e2ea0cd2..496db05b7 100644 --- a/README.md +++ b/README.md @@ -146,7 +146,8 @@ This might be usefull if your CI pipeline can't/shouldn't connect to your develo ### Backend Connecting Reflector -These reflectors connect to a real database, infers types based on result-set metadata and are able to detect errors within a given sql query. +These reflectors connect to a real database, infer types based on result-set/schema metadata and are able to detect errors within a given sql query. +It is **not** mandatory to use the same database driver for phpstan-dba, as you use within your application code. | Reflector | Key Features | |----------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------| From caecc98db4fc06c897affbe7c69576bf9cf44e47 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sun, 20 Feb 2022 21:34:43 +0100 Subject: [PATCH 20/31] Update README.md --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 496db05b7..a19562232 100644 --- a/README.md +++ b/README.md @@ -158,12 +158,12 @@ It is **not** mandatory to use the same database driver for phpstan-dba, as you Utility reflectors will be used in combination with backend connecting reflectors to provide additional features. -| Reflector | Key Features | -|----------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| ReplayAndRecordingQueryReflector | - utilizes the cached information of a *RecordingQueryReflector
- will validate the cached information
- will update local cache file information, even on external changes
- will reduce database interactions to a minimum, but still requires a active database connection | -| RecordingQueryReflector | - wraps a backend connecting reflector and caches the reflected information into a local file | -| ReplayQueryReflector | - utilizes the cached information of a *RecordingQueryReflector
- will **not** validate the cached information, therefore might return stale results
- does **not** require a active database connection | -| ChainedReflector | - chain several backend connecting reflectors, so applications which use multiple database connections can be analyzed | +| Reflector | Key Features | +|----------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| ReplayAndRecordingQueryReflector | - wraps a backend connecting reflector, caches the reflected information into a local file and utilizes the cached information
- will validate the cached information
- will update local cache file information, even on external changes
- will reduce database interactions to a minimum, but still requires a active database connection | +| RecordingQueryReflector | - wraps a backend connecting reflector and caches the reflected information into a local file | +| ReplayQueryReflector | - utilizes the cached information of a *RecordingQueryReflector
- will **not** validate the cached information, therefore might return stale results
- does **not** require a active database connection | +| ChainedReflector | - chain several backend connecting reflectors, so applications which use multiple database connections can be analyzed | ## Advanced Usage From a6ac7458fb75065e237a1f1f997ac8b5d68d567b Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sun, 20 Feb 2022 21:37:46 +0100 Subject: [PATCH 21/31] Update README.md --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a19562232..61301bb93 100644 --- a/README.md +++ b/README.md @@ -162,9 +162,15 @@ Utility reflectors will be used in combination with backend connecting reflector |----------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | ReplayAndRecordingQueryReflector | - wraps a backend connecting reflector, caches the reflected information into a local file and utilizes the cached information
- will validate the cached information
- will update local cache file information, even on external changes
- will reduce database interactions to a minimum, but still requires a active database connection | | RecordingQueryReflector | - wraps a backend connecting reflector and caches the reflected information into a local file | -| ReplayQueryReflector | - utilizes the cached information of a *RecordingQueryReflector
- will **not** validate the cached information, therefore might return stale results
- does **not** require a active database connection | +| ReplayQueryReflector | - utilizes the cached information of a `*RecordingQueryReflector`
- will **not** validate the cached information, therefore might return stale results
- does **not** require a active database connection | | ChainedReflector | - chain several backend connecting reflectors, so applications which use multiple database connections can be analyzed | +Legacy utility reflectors + +| Reflector | Key Features | +|----------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| RecordingQueryReflector | - wraps a backend connecting reflector and caches the reflected information into a local file | + ## Advanced Usage ### use `SyntaxErrorInPreparedStatementMethodRule` for your custom classes From dbf349ee818a38fc2b4360248269e7c458c1a097 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sun, 20 Feb 2022 21:40:56 +0100 Subject: [PATCH 22/31] Update README.md --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 61301bb93..62de3a068 100644 --- a/README.md +++ b/README.md @@ -161,7 +161,6 @@ Utility reflectors will be used in combination with backend connecting reflector | Reflector | Key Features | |----------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | ReplayAndRecordingQueryReflector | - wraps a backend connecting reflector, caches the reflected information into a local file and utilizes the cached information
- will validate the cached information
- will update local cache file information, even on external changes
- will reduce database interactions to a minimum, but still requires a active database connection | -| RecordingQueryReflector | - wraps a backend connecting reflector and caches the reflected information into a local file | | ReplayQueryReflector | - utilizes the cached information of a `*RecordingQueryReflector`
- will **not** validate the cached information, therefore might return stale results
- does **not** require a active database connection | | ChainedReflector | - chain several backend connecting reflectors, so applications which use multiple database connections can be analyzed | From 9457d59ec70b43d5f5b395d0c7816999371f436a Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Mon, 21 Feb 2022 12:23:01 +0100 Subject: [PATCH 23/31] introduce cacheIsDirty flag --- src/QueryReflection/ReflectionCache.php | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/QueryReflection/ReflectionCache.php b/src/QueryReflection/ReflectionCache.php index d1ad0d5ab..b34771454 100644 --- a/src/QueryReflection/ReflectionCache.php +++ b/src/QueryReflection/ReflectionCache.php @@ -34,6 +34,11 @@ final class ReflectionCache */ private $schemaHash; + /** + * @var bool + */ + private $cacheIsDirty = false; + /** * @var bool */ @@ -91,6 +96,7 @@ public function getSchemaHash() */ public function setSchemaHash(string $hash) { + $this->cacheIsDirty = true; $this->schemaHash = $hash; } @@ -160,7 +166,7 @@ private function readCachedRecords(bool $useReadLock): ?array public function persist(): void { - if (0 === \count($this->changes)) { + if ($this->cacheIsDirty === false) { return; } @@ -241,10 +247,12 @@ public function putValidationError(string $queryString, ?Error $error): void if (!\array_key_exists($queryString, $records)) { $this->changes[$queryString] = $this->records[$queryString] = []; + $this->cacheIsDirty = true; } if (!\array_key_exists('error', $this->records[$queryString]) || $this->records[$queryString]['error'] !== $error) { $this->changes[$queryString]['error'] = $this->records[$queryString]['error'] = $error; + $this->cacheIsDirty = true; } } @@ -299,15 +307,18 @@ public function putResultType(string $queryString, int $fetchType, ?Type $result if (!\array_key_exists($queryString, $records)) { $this->changes[$queryString] = $this->records[$queryString] = []; + $this->cacheIsDirty = true; } if (!\array_key_exists('result', $this->records[$queryString])) { $this->changes[$queryString]['result'] = $this->records[$queryString]['result'] = []; + $this->cacheIsDirty = true; } // @phpstan-ignore-next-line if (!\array_key_exists($fetchType, $this->records[$queryString]['result']) || $this->records[$queryString]['result'][$fetchType] !== $resultType) { $this->changes[$queryString]['result'][$fetchType] = $this->records[$queryString]['result'][$fetchType] = $resultType; + $this->cacheIsDirty = true; } } } From d4400f8083b802a265f247d4df3b0f7ccc4f108d Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Mon, 21 Feb 2022 12:24:24 +0100 Subject: [PATCH 24/31] SchemaHasherMysql: increase group_concat_max_len to make it work with big databases --- src/DbSchema/SchemaHasherMysql.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/DbSchema/SchemaHasherMysql.php b/src/DbSchema/SchemaHasherMysql.php index c6d6a87c0..7e098c97f 100644 --- a/src/DbSchema/SchemaHasherMysql.php +++ b/src/DbSchema/SchemaHasherMysql.php @@ -31,6 +31,11 @@ public function hashDb(): string return $this->hash; } + // for a schema with 3.000 columns we need roughly + // 70.000 group concat max length + $maxConcatQuery = 'SET SESSION group_concat_max_len = 1000000'; + $this->connection->query($maxConcatQuery); + $query = ' SELECT MD5( From 56d6af6fd6aed49fbd8e435c7808bdf6b1034a0c Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Mon, 21 Feb 2022 12:45:13 +0100 Subject: [PATCH 25/31] Update ReflectionCache.php --- src/QueryReflection/ReflectionCache.php | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/QueryReflection/ReflectionCache.php b/src/QueryReflection/ReflectionCache.php index b34771454..66d11fd2d 100644 --- a/src/QueryReflection/ReflectionCache.php +++ b/src/QueryReflection/ReflectionCache.php @@ -4,6 +4,7 @@ namespace staabm\PHPStanDba\QueryReflection; +use staabm\PHPStanDba\CacheNotPopulatedException; use const LOCK_EX; use PHPStan\ShouldNotHappenException; use PHPStan\Type\Type; @@ -155,6 +156,8 @@ private function readCachedRecords(bool $useReadLock): ?array if (null === $this->schemaHash) { $this->schemaHash = $cache['schemaHash']; + } elseif ($this->schemaHash !== $cache['schemaHash']) { + return null; } if (!\is_array($cache['records'])) { @@ -230,12 +233,12 @@ public function getValidationError(string $queryString): ?Error $records = $this->lazyReadRecords(); if (!\array_key_exists($queryString, $records)) { - throw new DbaException(sprintf('Cache not populated for query "%s"', $queryString)); + throw new CacheNotPopulatedException(sprintf('Cache not populated for query "%s"', $queryString)); } $cacheEntry = $this->records[$queryString]; if (!\array_key_exists('error', $cacheEntry)) { - throw new DbaException(sprintf('Cache not populated for query "%s"', $queryString)); + throw new CacheNotPopulatedException(sprintf('Cache not populated for query "%s"', $queryString)); } return $cacheEntry['error']; @@ -283,16 +286,16 @@ public function getResultType(string $queryString, int $fetchType): ?Type $records = $this->lazyReadRecords(); if (!\array_key_exists($queryString, $records)) { - throw new DbaException(sprintf('Cache not populated for query "%s"', $queryString)); + throw new CacheNotPopulatedException(sprintf('Cache not populated for query "%s"', $queryString)); } $cacheEntry = $this->records[$queryString]; if (!\array_key_exists('result', $cacheEntry)) { - throw new DbaException(sprintf('Cache not populated for query "%s"', $queryString)); + throw new CacheNotPopulatedException(sprintf('Cache not populated for query "%s"', $queryString)); } if (!\array_key_exists($fetchType, $cacheEntry['result'])) { - throw new DbaException(sprintf('Cache not populated for query "%s"', $queryString)); + throw new CacheNotPopulatedException(sprintf('Cache not populated for query "%s"', $queryString)); } return $cacheEntry['result'][$fetchType]; From b9525f10fb281969f6d0e2193541275cd40e1531 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Mon, 21 Feb 2022 12:45:29 +0100 Subject: [PATCH 26/31] Update ReplayAndRecordingQueryReflector.php --- .../ReplayAndRecordingQueryReflector.php | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/QueryReflection/ReplayAndRecordingQueryReflector.php b/src/QueryReflection/ReplayAndRecordingQueryReflector.php index 20efceedb..e82bc96bd 100644 --- a/src/QueryReflection/ReplayAndRecordingQueryReflector.php +++ b/src/QueryReflection/ReplayAndRecordingQueryReflector.php @@ -5,6 +5,7 @@ namespace staabm\PHPStanDba\QueryReflection; use PHPStan\Type\Type; +use staabm\PHPStanDba\CacheNotPopulatedException; use staabm\PHPStanDba\DbSchema\SchemaHasherMysql; use staabm\PHPStanDba\Error; @@ -26,9 +27,9 @@ public function __construct(ReflectionCache $reflectionCache, QueryReflector $qu $this->reflectionCache = $reflectionCache; } - private function getRecordingReflector(): ?RecordingQueryReflector + private function getRecordingReflector(bool $forceCreate = false): ?RecordingQueryReflector { - if (null === $this->recordingReflector && $this->reflectionCache->getSchemaHash() !== $this->schemaHasher->hashDb()) { + if ($forceCreate === true || null === $this->recordingReflector && $this->reflectionCache->getSchemaHash() !== $this->schemaHasher->hashDb()) { $this->reflectionCache->setSchemaHash($this->schemaHasher->hashDb()); $this->recordingReflector = new RecordingQueryReflector($this->reflectionCache, $this->queryReflector); @@ -44,7 +45,11 @@ public function validateQueryString(string $queryString): ?Error return $recordingReflector->validateQueryString($queryString); } - return $this->replayReflector->validateQueryString($queryString); + try { + return $this->replayReflector->validateQueryString($queryString); + } catch(CacheNotPopulatedException $e) { + return $this->getRecordingReflector(true)->validateQueryString($queryString); + } } public function getResultType(string $queryString, int $fetchType): ?Type @@ -54,6 +59,10 @@ public function getResultType(string $queryString, int $fetchType): ?Type return $recordingReflector->getResultType($queryString, $fetchType); } - return $this->replayReflector->getResultType($queryString, $fetchType); + try { + return $this->replayReflector->getResultType($queryString, $fetchType); + } catch (CacheNotPopulatedException $e) { + return $this->getRecordingReflector(true)->getResultType($queryString, $fetchType); + } } } From fb9d83db57ca8d2b50e321db9b6d282e01d9953d Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Mon, 21 Feb 2022 12:46:15 +0100 Subject: [PATCH 27/31] Create CacheNotPopulatedException.php --- src/CacheNotPopulatedException.php | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 src/CacheNotPopulatedException.php diff --git a/src/CacheNotPopulatedException.php b/src/CacheNotPopulatedException.php new file mode 100644 index 000000000..65456f7ac --- /dev/null +++ b/src/CacheNotPopulatedException.php @@ -0,0 +1,7 @@ + Date: Mon, 21 Feb 2022 12:48:51 +0100 Subject: [PATCH 28/31] Update ReplayAndRecordingQueryReflector.php --- .../ReplayAndRecordingQueryReflector.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/QueryReflection/ReplayAndRecordingQueryReflector.php b/src/QueryReflection/ReplayAndRecordingQueryReflector.php index e82bc96bd..8f0e24250 100644 --- a/src/QueryReflection/ReplayAndRecordingQueryReflector.php +++ b/src/QueryReflection/ReplayAndRecordingQueryReflector.php @@ -29,10 +29,14 @@ public function __construct(ReflectionCache $reflectionCache, QueryReflector $qu private function getRecordingReflector(bool $forceCreate = false): ?RecordingQueryReflector { - if ($forceCreate === true || null === $this->recordingReflector && $this->reflectionCache->getSchemaHash() !== $this->schemaHasher->hashDb()) { - $this->reflectionCache->setSchemaHash($this->schemaHasher->hashDb()); - - $this->recordingReflector = new RecordingQueryReflector($this->reflectionCache, $this->queryReflector); + if (null === $this->recordingReflector) + { + if ($forceCreate === true || + $this->reflectionCache->getSchemaHash() !== $this->schemaHasher->hashDb()) + { + $this->reflectionCache->setSchemaHash($this->schemaHasher->hashDb()); + $this->recordingReflector = new RecordingQueryReflector($this->reflectionCache, $this->queryReflector); + } } return $this->recordingReflector; From a5ec1e2d850aefabe9086851c4de82683208e0ca Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Mon, 21 Feb 2022 12:57:10 +0100 Subject: [PATCH 29/31] fix --- .../ReplayAndRecordingQueryReflector.php | 34 ++--- .../config/.phpunit-phpstan-dba-mysqli.cache | 124 ++++++++++++++++++ 2 files changed, 141 insertions(+), 17 deletions(-) diff --git a/src/QueryReflection/ReplayAndRecordingQueryReflector.php b/src/QueryReflection/ReplayAndRecordingQueryReflector.php index 8f0e24250..4ffdc2144 100644 --- a/src/QueryReflection/ReplayAndRecordingQueryReflector.php +++ b/src/QueryReflection/ReplayAndRecordingQueryReflector.php @@ -27,16 +27,18 @@ public function __construct(ReflectionCache $reflectionCache, QueryReflector $qu $this->reflectionCache = $reflectionCache; } - private function getRecordingReflector(bool $forceCreate = false): ?RecordingQueryReflector + private function dbSchemaChanged(): bool { - if (null === $this->recordingReflector) - { - if ($forceCreate === true || - $this->reflectionCache->getSchemaHash() !== $this->schemaHasher->hashDb()) - { - $this->reflectionCache->setSchemaHash($this->schemaHasher->hashDb()); - $this->recordingReflector = new RecordingQueryReflector($this->reflectionCache, $this->queryReflector); - } + $schemaHash = $this->schemaHasher->hashDb(); + $cachedSchemaHash = $this->reflectionCache->getSchemaHash(); + + return $schemaHash !== $cachedSchemaHash; + } + + private function createRecordingReflector(): RecordingQueryReflector { + if (null === $this->recordingReflector) { + $this->reflectionCache->setSchemaHash($this->schemaHasher->hashDb()); + $this->recordingReflector = new RecordingQueryReflector($this->reflectionCache, $this->queryReflector); } return $this->recordingReflector; @@ -44,29 +46,27 @@ private function getRecordingReflector(bool $forceCreate = false): ?RecordingQue public function validateQueryString(string $queryString): ?Error { - $recordingReflector = $this->getRecordingReflector(); - if (null !== $recordingReflector) { - return $recordingReflector->validateQueryString($queryString); + if ($this->dbSchemaChanged()) { + return $this->createRecordingReflector()->validateQueryString($queryString); } try { return $this->replayReflector->validateQueryString($queryString); } catch(CacheNotPopulatedException $e) { - return $this->getRecordingReflector(true)->validateQueryString($queryString); + return $this->createRecordingReflector()->validateQueryString($queryString); } } public function getResultType(string $queryString, int $fetchType): ?Type { - $recordingReflector = $this->getRecordingReflector(); - if (null !== $recordingReflector) { - return $recordingReflector->getResultType($queryString, $fetchType); + if ($this->dbSchemaChanged()) { + return $this->createRecordingReflector()->getResultType($queryString, $fetchType); } try { return $this->replayReflector->getResultType($queryString, $fetchType); } catch (CacheNotPopulatedException $e) { - return $this->getRecordingReflector(true)->getResultType($queryString, $fetchType); + return $this->createRecordingReflector()->getResultType($queryString, $fetchType); } } } diff --git a/tests/rules/config/.phpunit-phpstan-dba-mysqli.cache b/tests/rules/config/.phpunit-phpstan-dba-mysqli.cache index 11e5096aa..587c1c994 100644 --- a/tests/rules/config/.phpunit-phpstan-dba-mysqli.cache +++ b/tests/rules/config/.phpunit-phpstan-dba-mysqli.cache @@ -122,6 +122,125 @@ array ( 'error' => NULL, ), + ' + SELECT email adaid + WHERE gesperrt = \'1\' AND email LIKE \'%@example.com\' + FROM ada + LIMIT 1 + ' => + array ( + 'error' => + staabm\PHPStanDba\Error::__set_state(array( + '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 \'FROM ada LIMIT 0\' at line 3', + 'code' => 1064, + )), + ), + ' + SELECT email, adaid + FROM ada + WHERE gesperrt = \'1\' + FOR UPDATE + ' => + array ( + 'error' => NULL, + ), + ' + SELECT email, adaid + FROM ada + WHERE gesperrt = \'1\' + LIMIT \'1\' + ' => + array ( + 'error' => NULL, + ), + ' + SELECT email, adaid + FROM ada + WHERE gesperrt = \'1\' + LIMIT \'1\' + FOR UPDATE + ' => + array ( + 'error' => NULL, + ), + ' + SELECT email, adaid + FROM ada + WHERE gesperrt = \'1\' + LIMIT \'1\' + OFFSET \'1\' + ' => + array ( + 'error' => NULL, + ), + ' + SELECT email, adaid + FROM ada + WHERE gesperrt = \'1\' + LIMIT \'1\' + OFFSET \'1\' + FOR SHARE + ' => + array ( + 'error' => NULL, + ), + ' + SELECT email, adaid + FROM ada + WHERE gesperrt = \'1\' + LIMIT \'1\' + OFFSET \'1\' + FOR UPDATE + ' => + array ( + 'error' => NULL, + ), + ' + SELECT email, adaid + FROM ada + WHERE gesperrt = \'1\' + LIMIT \'1\' + OFFSET 1 + ' => + array ( + 'error' => NULL, + ), + ' + SELECT email, adaid + FROM ada + WHERE gesperrt = \'1\' + LIMIT \'1\', \'1\' + ' => + array ( + 'error' => NULL, + ), + ' + SELECT email, adaid + FROM ada + WHERE gesperrt = \'1\' + LIMIT \'1\', \'1\' + ' => + array ( + 'error' => NULL, + ), + ' + SELECT email, adaid + FROM ada + WHERE gesperrt = \'1\' AND email LIKE \'%@example%\' + LIMIT 1 + ' => + array ( + 'error' => NULL, + ), + ' + SELECT email, adaid + FROM ada + WHERE gesperrt = \'1\' AND email LIKE NULL + LIMIT 1 + ' => + array ( + 'error' => NULL, + ), 'SELECT * FROM ada GROUP BY doesNotExist' => array ( 'error' => @@ -239,6 +358,11 @@ array ( 'error' => NULL, ), + 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada + WHERE (gesperrt=\'1\' AND freigabe1u1=1) OR (gesperrt=\'1\' AND freigabe1u1=0)' => + array ( + 'error' => NULL, + ), 'SELECT email, adaid, gesperrt, freigabe1u1 FROM ada LIMIT 1' => array ( 'error' => NULL, From f0f30016b0d3a164f0e03da4841440b43412b5f9 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Mon, 21 Feb 2022 13:45:27 +0100 Subject: [PATCH 30/31] cs --- src/QueryReflection/ReflectionCache.php | 4 ++-- src/QueryReflection/ReplayAndRecordingQueryReflector.php | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/QueryReflection/ReflectionCache.php b/src/QueryReflection/ReflectionCache.php index 66d11fd2d..6a277464d 100644 --- a/src/QueryReflection/ReflectionCache.php +++ b/src/QueryReflection/ReflectionCache.php @@ -4,10 +4,10 @@ namespace staabm\PHPStanDba\QueryReflection; -use staabm\PHPStanDba\CacheNotPopulatedException; use const LOCK_EX; use PHPStan\ShouldNotHappenException; use PHPStan\Type\Type; +use staabm\PHPStanDba\CacheNotPopulatedException; use staabm\PHPStanDba\DbaException; use staabm\PHPStanDba\Error; @@ -169,7 +169,7 @@ private function readCachedRecords(bool $useReadLock): ?array public function persist(): void { - if ($this->cacheIsDirty === false) { + if (false === $this->cacheIsDirty) { return; } diff --git a/src/QueryReflection/ReplayAndRecordingQueryReflector.php b/src/QueryReflection/ReplayAndRecordingQueryReflector.php index 4ffdc2144..adcd2b598 100644 --- a/src/QueryReflection/ReplayAndRecordingQueryReflector.php +++ b/src/QueryReflection/ReplayAndRecordingQueryReflector.php @@ -35,7 +35,8 @@ private function dbSchemaChanged(): bool return $schemaHash !== $cachedSchemaHash; } - private function createRecordingReflector(): RecordingQueryReflector { + private function createRecordingReflector(): RecordingQueryReflector + { if (null === $this->recordingReflector) { $this->reflectionCache->setSchemaHash($this->schemaHasher->hashDb()); $this->recordingReflector = new RecordingQueryReflector($this->reflectionCache, $this->queryReflector); @@ -52,7 +53,7 @@ public function validateQueryString(string $queryString): ?Error try { return $this->replayReflector->validateQueryString($queryString); - } catch(CacheNotPopulatedException $e) { + } catch (CacheNotPopulatedException $e) { return $this->createRecordingReflector()->validateQueryString($queryString); } } From 298b27a14e9abe58feddca6ea861337c5513cfa1 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Mon, 21 Feb 2022 13:53:57 +0100 Subject: [PATCH 31/31] fix --- README.md | 16 ++++++++-------- src/QueryReflection/ReflectionCache.php | 5 +++++ 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 62de3a068..59c5669ca 100644 --- a/README.md +++ b/README.md @@ -158,17 +158,17 @@ It is **not** mandatory to use the same database driver for phpstan-dba, as you Utility reflectors will be used in combination with backend connecting reflectors to provide additional features. -| Reflector | Key Features | -|----------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| ReplayAndRecordingQueryReflector | - wraps a backend connecting reflector, caches the reflected information into a local file and utilizes the cached information
- will validate the cached information
- will update local cache file information, even on external changes
- will reduce database interactions to a minimum, but still requires a active database connection | -| ReplayQueryReflector | - utilizes the cached information of a `*RecordingQueryReflector`
- will **not** validate the cached information, therefore might return stale results
- does **not** require a active database connection | -| ChainedReflector | - chain several backend connecting reflectors, so applications which use multiple database connections can be analyzed | +| Reflector | Key Features | +|----------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| ReplayAndRecordingQueryReflector | - wraps a backend connecting reflector, caches the reflected information into a local file and utilizes the cached information
- will re-validate the cached information
- will update local cache file information, even on external changes
- will reduce database interactions to a minimum, but still requires a active database connection | +| ReplayQueryReflector | - utilizes the cached information of a `*RecordingQueryReflector`
- will **not** validate the cached information, therefore might return stale results
- does **not** require a active database connection | +| ChainedReflector | - chain several backend connecting reflectors, so applications which use multiple database connections can be analyzed | Legacy utility reflectors -| Reflector | Key Features | -|----------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| RecordingQueryReflector | - wraps a backend connecting reflector and caches the reflected information into a local file | +| Reflector | Key Features | +|----------------------------------|------------------------------------------------------------------------------------------------------| +| RecordingQueryReflector | - wraps a backend connecting reflector and caches the reflected information into a local file
-requires a active database connection | ## Advanced Usage diff --git a/src/QueryReflection/ReflectionCache.php b/src/QueryReflection/ReflectionCache.php index 6a277464d..0c71b22ae 100644 --- a/src/QueryReflection/ReflectionCache.php +++ b/src/QueryReflection/ReflectionCache.php @@ -228,6 +228,9 @@ public function hasValidationError(string $queryString): bool return \array_key_exists('error', $cacheEntry); } + /** + * @throws CacheNotPopulatedException + */ public function getValidationError(string $queryString): ?Error { $records = $this->lazyReadRecords(); @@ -280,6 +283,8 @@ public function hasResultType(string $queryString, int $fetchType): bool /** * @param QueryReflector::FETCH_TYPE* $fetchType + * + * @throws CacheNotPopulatedException */ public function getResultType(string $queryString, int $fetchType): ?Type {