Skip to content

Commit b86c38c

Browse files
committed
Compatibility with Doctrine DBAL 3.x
1 parent 8061e5f commit b86c38c

11 files changed

+180
-6
lines changed

.gitattributes

+1
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@ Makefile export-ignore
99
phpcs.xml export-ignore
1010
phpstan.neon export-ignore
1111
phpstan-baseline.neon export-ignore
12+
phpstan-baseline-dbal-3.neon export-ignore
1213
phpunit.xml export-ignore

composer.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
"doctrine/annotations": "^1.11.0",
2121
"doctrine/collections": "^1.6",
2222
"doctrine/common": "^2.7 || ^3.0",
23-
"doctrine/dbal": "^2.13.7",
23+
"doctrine/dbal": "^2.13.7 || ^3.0",
2424
"doctrine/lexer": "^1.2.1",
2525
"doctrine/mongodb-odm": "^1.3 || ^2.1",
2626
"doctrine/orm": "^2.11.0",

phpstan-baseline-dbal-3.neon

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
parameters:
2+
ignoreErrors:
3+
-
4+
message: "#^Class Doctrine\\\\DBAL\\\\Platforms\\\\MySQLPlatform referenced with incorrect case\\: Doctrine\\\\DBAL\\\\Platforms\\\\MySqlPlatform\\.$#"
5+
count: 2
6+
path: src/Doctrine/Mapping/ClassMetadataFactory.php
7+
8+
-
9+
message: "#^Caught class Doctrine\\\\DBAL\\\\DBALException not found\\.$#"
10+
count: 1
11+
path: src/Type/Doctrine/CreateQueryDynamicReturnTypeExtension.php
12+
13+
-
14+
message: "#^Class Doctrine\\\\DBAL\\\\Driver\\\\ResultStatement not found\\.$#"
15+
count: 2
16+
path: src/Type/Doctrine/DBAL/QueryBuilder/QueryBuilderExecuteMethodExtension.php
17+
18+
-
19+
message: "#^Class Doctrine\\\\DBAL\\\\Types\\\\JsonArrayType not found\\.$#"
20+
count: 1
21+
path: src/Type/Doctrine/Descriptors/JsonArrayType.php
22+
23+
-
24+
message: "#^Method PHPStan\\\\Type\\\\Doctrine\\\\Descriptors\\\\JsonArrayType\\:\\:getType\\(\\) should return class\\-string\\<Doctrine\\\\DBAL\\\\Types\\\\Type\\> but returns string\\.$#"
25+
count: 1
26+
path: src/Type/Doctrine/Descriptors/JsonArrayType.php
27+
28+
-
29+
message: "#^Caught class Doctrine\\\\DBAL\\\\DBALException not found\\.$#"
30+
count: 1
31+
path: src/Type/Doctrine/QueryBuilder/QueryBuilderGetQueryDynamicReturnTypeExtension.php
32+

phpstan-baseline.neon

+5
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ parameters:
55
count: 1
66
path: src/Doctrine/Mapping/ClassMetadataFactory.php
77

8+
-
9+
message: "#^Class Doctrine\\\\DBAL\\\\Platforms\\\\MySqlPlatform referenced with incorrect case\\: Doctrine\\\\DBAL\\\\Platforms\\\\MySQLPlatform\\.$#"
10+
count: 1
11+
path: src/Doctrine/Mapping/ClassMetadataFactory.php
12+
813
-
914
message: "#^Parameter \\#1 \\$entityName of class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata constructor expects class\\-string\\<T of object\\>, string given\\.$#"
1015
count: 1

phpstan.neon

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ includes:
22
- extension.neon
33
- rules.neon
44
- phpstan-baseline.neon
5+
- phpstan-baseline-dbal-3.neon
56
- vendor/phpstan/phpstan-strict-rules/rules.neon
67
- vendor/phpstan/phpstan-phpunit/extension.neon
78
- vendor/phpstan/phpstan-phpunit/rules.neon

src/Doctrine/Mapping/ClassMetadataFactory.php

+8-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
use Doctrine\Common\Annotations\AnnotationReader;
66
use Doctrine\Common\EventManager;
7-
use Doctrine\DBAL\Platforms\MySqlPlatform;
87
use Doctrine\ORM\Mapping\ClassMetadata;
98
use Doctrine\ORM\Mapping\Driver\AnnotationDriver;
109
use Doctrine\ORM\Mapping\Driver\AttributeDriver;
@@ -36,7 +35,14 @@ protected function initialize(): void
3635

3736
$targetPlatformProperty = $parentReflection->getProperty('targetPlatform');
3837
$targetPlatformProperty->setAccessible(true);
39-
$targetPlatformProperty->setValue($this, new MySqlPlatform());
38+
39+
if (class_exists(\Doctrine\DBAL\Platforms\MySqlPlatform::class)) {
40+
$platform = new \Doctrine\DBAL\Platforms\MySqlPlatform();
41+
} else {
42+
$platform = new \Doctrine\DBAL\Platforms\MySQLPlatform();
43+
}
44+
45+
$targetPlatformProperty->setValue($this, $platform);
4046
}
4147

4248
protected function newClassMetadataInstance($className)

src/Type/Doctrine/CreateQueryDynamicReturnTypeExtension.php

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use Doctrine\Common\CommonException;
66
use Doctrine\DBAL\DBALException;
7+
use Doctrine\DBAL\Exception as NewDBALException;
78
use Doctrine\ORM\EntityManagerInterface;
89
use Doctrine\ORM\ORMException;
910
use Doctrine\ORM\Query;
@@ -85,7 +86,7 @@ public function getTypeFromMethodCall(
8586
try {
8687
$query = $em->createQuery($queryString);
8788
QueryResultTypeWalker::walk($query, $typeBuilder, $this->descriptorRegistry);
88-
} catch (ORMException | DBALException | CommonException $e) {
89+
} catch (ORMException | DBALException | NewDBALException | CommonException $e) {
8990
return new QueryType($queryString, null);
9091
}
9192

src/Type/Doctrine/DBAL/QueryBuilder/QueryBuilderExecuteMethodExtension.php

+15-1
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@
22

33
namespace PHPStan\Type\Doctrine\DBAL\QueryBuilder;
44

5+
use Doctrine\DBAL\Driver\Result;
56
use Doctrine\DBAL\Driver\ResultStatement;
67
use Doctrine\DBAL\Query\QueryBuilder;
78
use PhpParser\Node\Expr\MethodCall;
89
use PhpParser\Node\Identifier;
910
use PHPStan\Analyser\Scope;
1011
use PHPStan\Reflection\MethodReflection;
1112
use PHPStan\Reflection\ParametersAcceptorSelector;
13+
use PHPStan\Reflection\ReflectionProvider;
1214
use PHPStan\Type\DynamicMethodReturnTypeExtension;
1315
use PHPStan\Type\ObjectType;
1416
use PHPStan\Type\Type;
@@ -17,6 +19,14 @@
1719
class QueryBuilderExecuteMethodExtension implements DynamicMethodReturnTypeExtension
1820
{
1921

22+
/** @var ReflectionProvider */
23+
private $reflectionProvider;
24+
25+
public function __construct(ReflectionProvider $reflectionProvider)
26+
{
27+
$this->reflectionProvider = $reflectionProvider;
28+
}
29+
2030
public function getClass(): string
2131
{
2232
return QueryBuilder::class;
@@ -46,7 +56,11 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method
4656

4757
$name = $nameObject->toString();
4858
if ($name === 'select' || $name === 'addSelect') {
49-
return TypeCombinator::intersect($defaultReturnType, new ObjectType(ResultStatement::class));
59+
if ($this->reflectionProvider->hasClass(ResultStatement::class)) {
60+
return TypeCombinator::intersect($defaultReturnType, new ObjectType(ResultStatement::class));
61+
}
62+
63+
return TypeCombinator::intersect($defaultReturnType, new ObjectType(Result::class));
5064
}
5165

5266
$var = $var->var;

tests/DoctrineIntegration/ORM/EntityManagerIntegrationTest.php

+4-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace PHPStan\DoctrineIntegration\ORM;
44

5+
use Composer\InstalledVersions;
56
use PHPStan\Testing\LevelsTestCase;
67

78
final class EntityManagerIntegrationTest extends LevelsTestCase
@@ -12,12 +13,14 @@ final class EntityManagerIntegrationTest extends LevelsTestCase
1213
*/
1314
public function dataTopics(): array
1415
{
16+
$version = InstalledVersions::getVersion('doctrine/dbal');
17+
$hasDbal3 = $version !== null && strpos(InstalledVersions::getVersion('doctrine/dbal'), '3.') === 0;
1518
return [
1619
['entityManagerDynamicReturn'],
1720
['entityManagerMergeReturn'],
1821
['customRepositoryUsage'],
1922
['queryBuilder'],
20-
['dbalQueryBuilderExecuteDynamicReturn'],
23+
[$hasDbal3 ? 'dbalQueryBuilderExecuteDynamicReturnDbal3' : 'dbalQueryBuilderExecuteDynamicReturn'],
2124
];
2225
}
2326

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
[
2+
{
3+
"message": "Cannot call method fetchAll() on Doctrine\\DBAL\\Result|int.",
4+
"line": 43,
5+
"ignorable": true
6+
},
7+
{
8+
"message": "Cannot call method fetchAll() on Doctrine\\DBAL\\Result|int.",
9+
"line": 60,
10+
"ignorable": true
11+
},
12+
{
13+
"message": "Cannot call method fetchAll() on Doctrine\\DBAL\\Result|int.",
14+
"line": 76,
15+
"ignorable": true
16+
},
17+
{
18+
"message": "Cannot call method fetchAll() on Doctrine\\DBAL\\Result|int.",
19+
"line": 87,
20+
"ignorable": true
21+
}
22+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace PHPStan\DoctrineIntegration\ORM\DbalQueryBuilderExecuteDynamicReturn;
4+
5+
use Doctrine\DBAL\Connection;
6+
use Doctrine\DBAL\Query\QueryBuilder;
7+
8+
class Example
9+
{
10+
/** @var Connection */
11+
private $connection;
12+
13+
public function __construct(Connection $connection)
14+
{
15+
$this->connection = $connection;
16+
}
17+
18+
/**
19+
* @return mixed[]
20+
*/
21+
public function testCaseOne(int $userId): array
22+
{
23+
return $this->connection->createQueryBuilder()
24+
->select('*')
25+
->from('user')
26+
->where('user.id = :id')
27+
->setParameter('id', $userId)
28+
->execute()
29+
->fetchAll();
30+
}
31+
32+
/**
33+
* @return mixed[]
34+
*/
35+
public function testCaseTwo(int $userId): array
36+
{
37+
$qb = $this->connection->createQueryBuilder();
38+
$qb->select('*');
39+
$qb->from('user');
40+
$qb->where('user.id = :id');
41+
$qb->setParameter('id', $userId);
42+
43+
return $qb->execute()->fetchAll();
44+
}
45+
46+
/**
47+
* @return mixed[]
48+
*/
49+
public function testCaseThree(?int $userId = null): array
50+
{
51+
$qb = $this->connection->createQueryBuilder();
52+
$qb->select('*');
53+
$qb->from('user');
54+
55+
if ($userId !== null) {
56+
$qb->where('user.id = :id');
57+
$qb->setParameter('id', $userId);
58+
}
59+
60+
return $qb->execute()->fetchAll();
61+
}
62+
63+
/**
64+
* @return mixed[]
65+
*/
66+
public function testCaseFourA(?int $userId = null): array
67+
{
68+
$qb = $this->connection->createQueryBuilder();
69+
$qb->select('*');
70+
$qb->from('user');
71+
72+
if ($userId !== null) {
73+
return $this->testCaseFourB($qb, $userId);
74+
}
75+
76+
return $qb->execute()->fetchAll();
77+
}
78+
79+
/**
80+
* @return mixed[]
81+
*/
82+
private function testCaseFourB(QueryBuilder $qb, int $userId): array
83+
{
84+
$qb->where('user.id = :id');
85+
$qb->setParameter('id', $userId);
86+
87+
return $qb->execute()->fetchAll();
88+
}
89+
}

0 commit comments

Comments
 (0)