Skip to content

Commit e9705e9

Browse files
committed
Adjust driver detection to support middlewares
1 parent a733539 commit e9705e9

File tree

8 files changed

+244
-97
lines changed

8 files changed

+244
-97
lines changed

phpstan.neon

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,9 @@ parameters:
4545

4646
-
4747
message: '#^Call to function method_exists\(\) with Doctrine\\DBAL\\Connection and ''getNativeConnection'' will always evaluate to true\.$#' # needed for older DBAL versions
48-
path: src/Type/Doctrine/Query/QueryResultTypeWalker.php
48+
paths:
49+
- src/Type/Doctrine/Query/QueryResultTypeWalker.php
50+
- src/Doctrine/Driver/DriverType.php
4951

5052
-
5153
messages: # needed for older DBAL versions (fails only on PHP 7.3)

src/Doctrine/Driver/DriverType.php

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Doctrine\Driver;
4+
5+
use Doctrine\DBAL\Connection;
6+
use Doctrine\DBAL\Driver\IBMDB2\Driver as IbmDb2Driver;
7+
use Doctrine\DBAL\Driver\Mysqli\Driver as MysqliDriver;
8+
use Doctrine\DBAL\Driver\OCI8\Driver as Oci8Driver;
9+
use Doctrine\DBAL\Driver\PDO\MySQL\Driver as PdoMysqlDriver;
10+
use Doctrine\DBAL\Driver\PDO\OCI\Driver as PdoOciDriver;
11+
use Doctrine\DBAL\Driver\PDO\PgSQL\Driver as PdoPgSQLDriver;
12+
use Doctrine\DBAL\Driver\PDO\SQLite\Driver as PdoSQLiteDriver;
13+
use Doctrine\DBAL\Driver\PDO\SQLSrv\Driver as PdoSqlSrvDriver;
14+
use Doctrine\DBAL\Driver\PgSQL\Driver as PgSQLDriver;
15+
use Doctrine\DBAL\Driver\SQLite3\Driver as SQLite3Driver;
16+
use Doctrine\DBAL\Driver\SQLSrv\Driver as SqlSrvDriver;
17+
use mysqli;
18+
use PDO;
19+
use SQLite3;
20+
use function get_resource_type;
21+
use function is_resource;
22+
use function method_exists;
23+
use function strpos;
24+
25+
class DriverType
26+
{
27+
28+
public const IBM_DB2 = 'ibm_db2';
29+
public const MYSQLI = 'mysqli';
30+
public const OCI8 = 'oci8';
31+
public const PDO_MYSQL = 'pdo_mysql';
32+
public const PDO_OCI = 'pdo_oci';
33+
public const PDO_PGSQL = 'pdo_pgsql';
34+
public const PDO_SQLITE = 'pdo_sqlite';
35+
public const PDO_SQLSRV = 'pdo_sqlsrv';
36+
public const PGSQL = 'pgsql';
37+
public const SQLITE3 = 'sqlite3';
38+
public const SQLSRV = 'sqlsrv';
39+
40+
41+
/**
42+
* @return self::*
43+
*/
44+
public static function detect(Connection $connection): ?string
45+
{
46+
$driver = $connection->getDriver();
47+
48+
if ($driver instanceof MysqliDriver) {
49+
return self::MYSQLI;
50+
}
51+
52+
if ($driver instanceof PdoMysqlDriver) {
53+
return self::PDO_MYSQL;
54+
}
55+
56+
if ($driver instanceof PdoSQLiteDriver) {
57+
return self::PDO_SQLITE;
58+
}
59+
60+
if ($driver instanceof PdoSqlSrvDriver) {
61+
return self::PDO_SQLSRV;
62+
}
63+
64+
if ($driver instanceof PdoOciDriver) {
65+
return self::PDO_OCI;
66+
}
67+
68+
if ($driver instanceof PdoPgSQLDriver) {
69+
return self::PDO_PGSQL;
70+
}
71+
72+
if ($driver instanceof SQLite3Driver) {
73+
return self::SQLITE3;
74+
}
75+
76+
if ($driver instanceof PgSQLDriver) {
77+
return self::PGSQL;
78+
}
79+
80+
if ($driver instanceof SqlSrvDriver) {
81+
return self::SQLSRV;
82+
}
83+
84+
if ($driver instanceof Oci8Driver) {
85+
return self::OCI8;
86+
}
87+
88+
if ($driver instanceof IbmDb2Driver) {
89+
return self::IBM_DB2;
90+
}
91+
92+
// fallback to connection-based detection when driver is wrapped by middleware
93+
94+
if (!method_exists($connection, 'getNativeConnection')) {
95+
return null; // dbal < 3.3 (released in 2022-01)
96+
}
97+
98+
$nativeConnection = $connection->getNativeConnection();
99+
100+
if ($nativeConnection instanceof mysqli) {
101+
return self::MYSQLI;
102+
}
103+
104+
if ($nativeConnection instanceof SQLite3) {
105+
return self::SQLITE3;
106+
}
107+
108+
if ($nativeConnection instanceof \PgSql\Connection) {
109+
return self::PGSQL;
110+
}
111+
112+
if ($nativeConnection instanceof PDO) {
113+
$driverName = $nativeConnection->getAttribute(PDO::ATTR_DRIVER_NAME);
114+
115+
if ($driverName === 'mysql') {
116+
return self::PDO_MYSQL;
117+
}
118+
119+
if ($driverName === 'sqlite') {
120+
return self::PDO_SQLITE;
121+
}
122+
123+
if ($driverName === 'pgsql') {
124+
return self::PDO_PGSQL;
125+
}
126+
127+
if ($driverName === 'oci') { // semi-verified (https://stackoverflow.com/questions/10090709/get-current-pdo-driver-from-existing-connection/10090754#comment12923198_10090754)
128+
return self::PDO_OCI;
129+
}
130+
131+
if ($driverName === 'sqlsrv') {
132+
return self::PDO_SQLSRV;
133+
}
134+
}
135+
136+
if (is_resource($nativeConnection)) {
137+
$resourceType = get_resource_type($nativeConnection);
138+
139+
if (strpos($resourceType, 'oci') !== false) { // not verified
140+
return self::OCI8;
141+
}
142+
143+
if (strpos($resourceType, 'db2') !== false) { // not verified
144+
return self::IBM_DB2;
145+
}
146+
147+
if (strpos($resourceType, 'SQL Server Connection') !== false) {
148+
return self::SQLSRV;
149+
}
150+
151+
if (strpos($resourceType, 'pgsql link') !== false) {
152+
return self::PGSQL;
153+
}
154+
}
155+
156+
return null;
157+
}
158+
159+
}

src/Type/Doctrine/Descriptors/BooleanType.php

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,8 @@
22

33
namespace PHPStan\Type\Doctrine\Descriptors;
44

5-
use Doctrine\DBAL\Driver;
6-
use Doctrine\DBAL\Driver\Mysqli\Driver as MysqliDriver;
7-
use Doctrine\DBAL\Driver\PDO\MySQL\Driver as PdoMysqlDriver;
8-
use Doctrine\DBAL\Driver\PDO\PgSQL\Driver as PdoPgSQLDriver;
9-
use Doctrine\DBAL\Driver\PDO\SQLite\Driver as PdoSQLiteDriver;
10-
use Doctrine\DBAL\Driver\PgSQL\Driver as PgSQLDriver;
11-
use Doctrine\DBAL\Driver\SQLite3\Driver as SQLite3Driver;
5+
use Doctrine\DBAL\Connection;
6+
use PHPStan\Doctrine\Driver\DriverType;
127
use PHPStan\Type\Constant\ConstantIntegerType;
138
use PHPStan\Type\Type;
149
use PHPStan\Type\TypeCombinator;
@@ -40,17 +35,19 @@ public function getDatabaseInternalType(): Type
4035
);
4136
}
4237

43-
public function getDatabaseInternalTypeForDriver(Driver $driver): Type
38+
public function getDatabaseInternalTypeForDriver(Connection $connection): Type
4439
{
45-
if ($driver instanceof PgSQLDriver || $driver instanceof PdoPgSQLDriver) {
40+
$driverType = DriverType::detect($connection);
41+
42+
if ($driverType === DriverType::PGSQL || $driverType === DriverType::PDO_PGSQL) {
4643
return new \PHPStan\Type\BooleanType();
4744
}
4845

4946
if (
50-
$driver instanceof SQLite3Driver
51-
|| $driver instanceof PdoSQLiteDriver
52-
|| $driver instanceof MysqliDriver
53-
|| $driver instanceof PdoMysqlDriver
47+
$driverType === DriverType::SQLITE3
48+
|| $driverType === DriverType::PDO_SQLITE
49+
|| $driverType === DriverType::MYSQLI
50+
|| $driverType === DriverType::PDO_MYSQL
5451
) {
5552
return TypeCombinator::union(
5653
new ConstantIntegerType(0),

src/Type/Doctrine/Descriptors/DecimalType.php

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,8 @@
22

33
namespace PHPStan\Type\Doctrine\Descriptors;
44

5-
use Doctrine\DBAL\Driver;
6-
use Doctrine\DBAL\Driver\Mysqli\Driver as MysqliDriver;
7-
use Doctrine\DBAL\Driver\PDO\MySQL\Driver as PdoMysqlDriver;
8-
use Doctrine\DBAL\Driver\PDO\PgSQL\Driver as PdoPgSQLDriver;
9-
use Doctrine\DBAL\Driver\PDO\SQLite\Driver as PdoSQLiteDriver;
10-
use Doctrine\DBAL\Driver\PgSQL\Driver as PgSQLDriver;
11-
use Doctrine\DBAL\Driver\SQLite3\Driver as SQLite3Driver;
5+
use Doctrine\DBAL\Connection;
6+
use PHPStan\Doctrine\Driver\DriverType;
127
use PHPStan\Type\Accessory\AccessoryNumericStringType;
138
use PHPStan\Type\FloatType;
149
use PHPStan\Type\IntegerType;
@@ -46,17 +41,19 @@ public function getDatabaseInternalType(): Type
4641
);
4742
}
4843

49-
public function getDatabaseInternalTypeForDriver(Driver $driver): Type
44+
public function getDatabaseInternalTypeForDriver(Connection $connection): Type
5045
{
51-
if ($driver instanceof Sqlite3Driver || $driver instanceof PdoSqliteDriver) {
46+
$driverType = DriverType::detect($connection);
47+
48+
if ($driverType === DriverType::SQLITE3 || $driverType === DriverType::PDO_SQLITE) {
5249
return new FloatType();
5350
}
5451

5552
if (
56-
$driver instanceof MysqliDriver
57-
|| $driver instanceof PdoMysqlDriver
58-
|| $driver instanceof PgSQLDriver
59-
|| $driver instanceof PdoPgSQLDriver
53+
$driverType === DriverType::MYSQLI
54+
|| $driverType === DriverType::PDO_MYSQL
55+
|| $driverType === DriverType::PGSQL
56+
|| $driverType === DriverType::PDO_PGSQL
6057
) {
6158
return new IntersectionType([
6259
new StringType(),

src/Type/Doctrine/Descriptors/DoctrineTypeDriverAwareDescriptor.php

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

33
namespace PHPStan\Type\Doctrine\Descriptors;
44

5-
use Doctrine\DBAL\Driver;
5+
use Doctrine\DBAL\Connection;
66
use PHPStan\Type\Type;
77

88
/** @api */
@@ -24,6 +24,6 @@ interface DoctrineTypeDriverAwareDescriptor
2424
* - bigint: int int int int
2525
* - bool: int int bool bool
2626
*/
27-
public function getDatabaseInternalTypeForDriver(Driver $driver): Type;
27+
public function getDatabaseInternalTypeForDriver(Connection $connection): Type;
2828

2929
}

src/Type/Doctrine/Descriptors/FloatType.php

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,8 @@
22

33
namespace PHPStan\Type\Doctrine\Descriptors;
44

5-
use Doctrine\DBAL\Driver;
6-
use Doctrine\DBAL\Driver\Mysqli\Driver as MysqliDriver;
7-
use Doctrine\DBAL\Driver\PDO\MySQL\Driver as PdoMysqlDriver;
8-
use Doctrine\DBAL\Driver\PDO\PgSQL\Driver as PdoPgSQLDriver;
9-
use Doctrine\DBAL\Driver\PDO\SQLite\Driver as PdoSQLiteDriver;
10-
use Doctrine\DBAL\Driver\PgSQL\Driver as PgSQLDriver;
11-
use Doctrine\DBAL\Driver\SQLite3\Driver as SQLite3Driver;
5+
use Doctrine\DBAL\Connection;
6+
use PHPStan\Doctrine\Driver\DriverType;
127
use PHPStan\Type\Accessory\AccessoryNumericStringType;
138
use PHPStan\Type\IntegerType;
149
use PHPStan\Type\IntersectionType;
@@ -45,21 +40,23 @@ public function getDatabaseInternalType(): Type
4540
);
4641
}
4742

48-
public function getDatabaseInternalTypeForDriver(Driver $driver): Type
43+
public function getDatabaseInternalTypeForDriver(Connection $connection): Type
4944
{
50-
if ($driver instanceof PdoPgSQLDriver) {
45+
$driverType = DriverType::detect($connection);
46+
47+
if ($driverType === DriverType::PDO_PGSQL) {
5148
return new IntersectionType([
5249
new StringType(),
5350
new AccessoryNumericStringType(),
5451
]);
5552
}
5653

5754
if (
58-
$driver instanceof SQLite3Driver
59-
|| $driver instanceof PdoSQLiteDriver
60-
|| $driver instanceof MysqliDriver
61-
|| $driver instanceof PdoMysqlDriver
62-
|| $driver instanceof PgSQLDriver
55+
$driverType === DriverType::SQLITE3
56+
|| $driverType === DriverType::PDO_SQLITE
57+
|| $driverType === DriverType::MYSQLI
58+
|| $driverType === DriverType::PDO_MYSQL
59+
|| $driverType === DriverType::PGSQL
6360
) {
6461
return new \PHPStan\Type\FloatType();
6562
}

src/Type/Doctrine/Descriptors/ReflectionDescriptor.php

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

33
namespace PHPStan\Type\Doctrine\Descriptors;
44

5-
use Doctrine\DBAL\Driver;
5+
use Doctrine\DBAL\Connection;
66
use Doctrine\DBAL\Platforms\AbstractPlatform;
77
use Doctrine\DBAL\Types\Type as DbalType;
88
use PHPStan\DependencyInjection\Container;
@@ -73,7 +73,7 @@ public function getDatabaseInternalType(): Type
7373
return new MixedType();
7474
}
7575

76-
public function getDatabaseInternalTypeForDriver(Driver $driver): Type
76+
public function getDatabaseInternalTypeForDriver(Connection $connection): Type
7777
{
7878
$registry = $this->container->getByType(DefaultDescriptorRegistry::class);
7979
$parents = $this->reflectionProvider->getClass($this->type)->getParentClassesNames();
@@ -85,7 +85,7 @@ public function getDatabaseInternalTypeForDriver(Driver $driver): Type
8585
$descriptor = $registry->getByClassName($dbalTypeParentClass);
8686

8787
return $descriptor instanceof DoctrineTypeDriverAwareDescriptor
88-
? $descriptor->getDatabaseInternalTypeForDriver($driver)
88+
? $descriptor->getDatabaseInternalTypeForDriver($connection)
8989
: $descriptor->getDatabaseInternalType();
9090

9191
} catch (DescriptorNotRegisteredException $e) {

0 commit comments

Comments
 (0)