Skip to content

Commit 6a5b8bd

Browse files
committed
QueryResultTypeWalker: precise type inferring
1 parent dd71401 commit 6a5b8bd

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+2403
-428
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,12 @@ $query->getOneOrNullResult(Query::HYDRATE_OBJECT); // User
152152

153153
This is due to the design of the `Query` class preventing from determining the hydration mode used by these functions unless it is specified explicitly during the call.
154154

155+
### Expression types inferring
156+
157+
Whether `MAX(e.id)` is fetched as `string` or `int` highly [depends on drivers, their setup and PHP version](https://github.com/janedbal/php-database-drivers-fetch-test).
158+
This extension copies the logic from linked analysis, autodetects your setup and provides accurate results for `pdo_mysql`, `mysqli`, `pdo_sqlite`, `sqlite3`, `pdo_pgsql` and `pgsql`.
159+
Any other driver will result in union with stringified version, e.g. `numeric-string|int`.
160+
155161
### Problematic approaches
156162

157163
Not every QueryBuilder can be statically analysed, here are few advices to maximize type inferring:

phpstan.neon

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,11 @@ parameters:
4949
- '#^Cannot call method getWrappedResourceHandle\(\) on class\-string\|object\.$#'
5050
path: tests/Platform/QueryResultTypeWalkerFetchTypeMatrixTest.php
5151
reportUnmatched: false
52+
-
53+
message: '#^Call to function method_exists\(\) with Doctrine\\DBAL\\Connection and ''getNativeConnection'' will always evaluate to true\.$#' # needed for older DBAL versions
54+
path: src/Type/Doctrine/Query/QueryResultTypeWalker.php
55+
-
56+
messages: # needed for older DBAL versions (fails only on PHP 7.3)
57+
- '#^Class Doctrine\\DBAL\\Driver\\PgSQL\\Driver not found\.$#'
58+
- '#^Class Doctrine\\DBAL\\Driver\\SQLite3\\Driver not found\.$#'
59+
path: src/Type/Doctrine/Query/QueryResultTypeWalker.php

src/Type/Doctrine/CreateQueryDynamicReturnTypeExtension.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use Doctrine\Persistence\Mapping\MappingException;
1313
use PhpParser\Node\Expr\MethodCall;
1414
use PHPStan\Analyser\Scope;
15+
use PHPStan\Php\PhpVersion;
1516
use PHPStan\Reflection\MethodReflection;
1617
use PHPStan\Type\Constant\ConstantStringType;
1718
use PHPStan\Type\Doctrine\Query\QueryResultTypeBuilder;
@@ -37,10 +38,14 @@ final class CreateQueryDynamicReturnTypeExtension implements DynamicMethodReturn
3738
/** @var DescriptorRegistry */
3839
private $descriptorRegistry;
3940

40-
public function __construct(ObjectMetadataResolver $objectMetadataResolver, DescriptorRegistry $descriptorRegistry)
41+
/** @var PhpVersion */
42+
private $phpVersion;
43+
44+
public function __construct(ObjectMetadataResolver $objectMetadataResolver, DescriptorRegistry $descriptorRegistry, PhpVersion $phpVersion)
4145
{
4246
$this->objectMetadataResolver = $objectMetadataResolver;
4347
$this->descriptorRegistry = $descriptorRegistry;
48+
$this->phpVersion = $phpVersion;
4449
}
4550

4651
public function getClass(): string
@@ -87,7 +92,7 @@ public function getTypeFromMethodCall(
8792

8893
try {
8994
$query = $em->createQuery($queryString);
90-
QueryResultTypeWalker::walk($query, $typeBuilder, $this->descriptorRegistry);
95+
QueryResultTypeWalker::walk($query, $typeBuilder, $this->descriptorRegistry, $this->phpVersion);
9196
} catch (ORMException | DBALException | NewDBALException | CommonException | MappingException | \Doctrine\ORM\Exception\ORMException $e) {
9297
return new QueryType($queryString, null, null);
9398
} catch (AssertionError $e) {

src/Type/Doctrine/DefaultDescriptorRegistry.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,15 @@ public function get(string $type): DoctrineTypeDescriptor
3636
return $this->descriptors[$typeClass];
3737
}
3838

39+
/**
40+
* @throws DescriptorNotRegisteredException
41+
*/
42+
public function getByClassName(string $className): DoctrineTypeDescriptor
43+
{
44+
if (!isset($this->descriptors[$className])) {
45+
throw new DescriptorNotRegisteredException();
46+
}
47+
return $this->descriptors[$className];
48+
}
49+
3950
}

src/Type/Doctrine/Descriptors/ArrayType.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace PHPStan\Type\Doctrine\Descriptors;
44

5+
use Doctrine\DBAL\Driver;
56
use PHPStan\Type\MixedType;
67
use PHPStan\Type\StringType;
78
use PHPStan\Type\Type;
@@ -24,7 +25,7 @@ public function getWritableToDatabaseType(): Type
2425
return new \PHPStan\Type\ArrayType(new MixedType(), new MixedType());
2526
}
2627

27-
public function getDatabaseInternalType(): Type
28+
public function getDatabaseInternalType(Driver $driver): Type
2829
{
2930
return new StringType();
3031
}

src/Type/Doctrine/Descriptors/AsciiStringType.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace PHPStan\Type\Doctrine\Descriptors;
44

5+
use Doctrine\DBAL\Driver;
56
use PHPStan\Type\StringType;
67
use PHPStan\Type\Type;
78

@@ -23,7 +24,7 @@ public function getWritableToDatabaseType(): Type
2324
return new StringType();
2425
}
2526

26-
public function getDatabaseInternalType(): Type
27+
public function getDatabaseInternalType(Driver $driver): Type
2728
{
2829
return new StringType();
2930
}

src/Type/Doctrine/Descriptors/BigIntType.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace PHPStan\Type\Doctrine\Descriptors;
44

55
use Composer\InstalledVersions;
6+
use Doctrine\DBAL\Driver;
67
use PHPStan\Type\Accessory\AccessoryNumericStringType;
78
use PHPStan\Type\IntegerType;
89
use PHPStan\Type\StringType;
@@ -30,10 +31,10 @@ public function getWritableToPropertyType(): Type
3031

3132
public function getWritableToDatabaseType(): Type
3233
{
33-
return TypeCombinator::union(new StringType(), new IntegerType());
34+
return TypeCombinator::union(new StringType());
3435
}
3536

36-
public function getDatabaseInternalType(): Type
37+
public function getDatabaseInternalType(Driver $driver): Type
3738
{
3839
return new IntegerType();
3940
}

src/Type/Doctrine/Descriptors/BinaryType.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace PHPStan\Type\Doctrine\Descriptors;
44

5+
use Doctrine\DBAL\Driver;
56
use PHPStan\Type\MixedType;
67
use PHPStan\Type\ResourceType;
78
use PHPStan\Type\StringType;
@@ -25,7 +26,7 @@ public function getWritableToDatabaseType(): Type
2526
return new MixedType();
2627
}
2728

28-
public function getDatabaseInternalType(): Type
29+
public function getDatabaseInternalType(Driver $driver): Type
2930
{
3031
return new StringType();
3132
}

src/Type/Doctrine/Descriptors/BlobType.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace PHPStan\Type\Doctrine\Descriptors;
44

5+
use Doctrine\DBAL\Driver;
56
use PHPStan\Type\MixedType;
67
use PHPStan\Type\ResourceType;
78
use PHPStan\Type\Type;
@@ -24,7 +25,7 @@ public function getWritableToDatabaseType(): Type
2425
return new MixedType();
2526
}
2627

27-
public function getDatabaseInternalType(): Type
28+
public function getDatabaseInternalType(Driver $driver): Type
2829
{
2930
return new MixedType();
3031
}

src/Type/Doctrine/Descriptors/BooleanType.php

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
namespace PHPStan\Type\Doctrine\Descriptors;
44

5+
use Doctrine\DBAL\Driver;
6+
use Doctrine\DBAL\Driver\PDO\PgSQL\Driver as PdoPgSQLDriver;
7+
use Doctrine\DBAL\Driver\PgSQL\Driver as PgSQLDriver;
58
use PHPStan\Type\Constant\ConstantIntegerType;
69
use PHPStan\Type\Type;
710
use PHPStan\Type\TypeCombinator;
@@ -24,12 +27,15 @@ public function getWritableToDatabaseType(): Type
2427
return new \PHPStan\Type\BooleanType();
2528
}
2629

27-
public function getDatabaseInternalType(): Type
30+
public function getDatabaseInternalType(Driver $driver): Type
2831
{
32+
if ($driver instanceof PgSQLDriver || $driver instanceof PdoPgSQLDriver) {
33+
return new \PHPStan\Type\BooleanType();
34+
}
35+
2936
return TypeCombinator::union(
3037
new ConstantIntegerType(0),
31-
new ConstantIntegerType(1),
32-
new \PHPStan\Type\BooleanType()
38+
new ConstantIntegerType(1)
3339
);
3440
}
3541

0 commit comments

Comments
 (0)