-
Notifications
You must be signed in to change notification settings - Fork 99
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Introduce DoctrineTypeDriverAwareDescriptor & DriverDetector
- Loading branch information
Showing
9 changed files
with
346 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
<?php declare(strict_types = 1); | ||
|
||
namespace PHPStan\Doctrine\Driver; | ||
|
||
use Doctrine\DBAL\Connection; | ||
use Doctrine\DBAL\Driver\IBMDB2\Driver as IbmDb2Driver; | ||
use Doctrine\DBAL\Driver\Mysqli\Driver as MysqliDriver; | ||
use Doctrine\DBAL\Driver\OCI8\Driver as Oci8Driver; | ||
use Doctrine\DBAL\Driver\PDO\MySQL\Driver as PdoMysqlDriver; | ||
use Doctrine\DBAL\Driver\PDO\OCI\Driver as PdoOciDriver; | ||
use Doctrine\DBAL\Driver\PDO\PgSQL\Driver as PdoPgSQLDriver; | ||
use Doctrine\DBAL\Driver\PDO\SQLite\Driver as PdoSQLiteDriver; | ||
use Doctrine\DBAL\Driver\PDO\SQLSrv\Driver as PdoSqlSrvDriver; | ||
use Doctrine\DBAL\Driver\PgSQL\Driver as PgSQLDriver; | ||
use Doctrine\DBAL\Driver\SQLite3\Driver as SQLite3Driver; | ||
use Doctrine\DBAL\Driver\SQLSrv\Driver as SqlSrvDriver; | ||
use mysqli; | ||
use PDO; | ||
use SQLite3; | ||
use Throwable; | ||
use function get_resource_type; | ||
use function is_resource; | ||
use function method_exists; | ||
use function strpos; | ||
|
||
class DriverDetector | ||
{ | ||
|
||
public const IBM_DB2 = 'ibm_db2'; | ||
public const MYSQLI = 'mysqli'; | ||
public const OCI8 = 'oci8'; | ||
public const PDO_MYSQL = 'pdo_mysql'; | ||
public const PDO_OCI = 'pdo_oci'; | ||
public const PDO_PGSQL = 'pdo_pgsql'; | ||
public const PDO_SQLITE = 'pdo_sqlite'; | ||
public const PDO_SQLSRV = 'pdo_sqlsrv'; | ||
public const PGSQL = 'pgsql'; | ||
public const SQLITE3 = 'sqlite3'; | ||
public const SQLSRV = 'sqlsrv'; | ||
|
||
/** @var bool */ | ||
private $failOnInvalidConnection; | ||
|
||
public function __construct(bool $failOnInvalidConnection) | ||
{ | ||
$this->failOnInvalidConnection = $failOnInvalidConnection; | ||
} | ||
|
||
/** | ||
* @return self::*|null | ||
*/ | ||
public function detect(Connection $connection): ?string | ||
{ | ||
$driver = $connection->getDriver(); | ||
|
||
if ($driver instanceof MysqliDriver) { | ||
return self::MYSQLI; | ||
} | ||
|
||
if ($driver instanceof PdoMysqlDriver) { | ||
return self::PDO_MYSQL; | ||
} | ||
|
||
if ($driver instanceof PdoSQLiteDriver) { | ||
return self::PDO_SQLITE; | ||
} | ||
|
||
if ($driver instanceof PdoSqlSrvDriver) { | ||
return self::PDO_SQLSRV; | ||
} | ||
|
||
if ($driver instanceof PdoOciDriver) { | ||
return self::PDO_OCI; | ||
} | ||
|
||
if ($driver instanceof PdoPgSQLDriver) { | ||
return self::PDO_PGSQL; | ||
} | ||
|
||
if ($driver instanceof SQLite3Driver) { | ||
return self::SQLITE3; | ||
} | ||
|
||
if ($driver instanceof PgSQLDriver) { | ||
return self::PGSQL; | ||
} | ||
|
||
if ($driver instanceof SqlSrvDriver) { | ||
return self::SQLSRV; | ||
} | ||
|
||
if ($driver instanceof Oci8Driver) { | ||
return self::OCI8; | ||
} | ||
|
||
if ($driver instanceof IbmDb2Driver) { | ||
return self::IBM_DB2; | ||
} | ||
|
||
// fallback to connection-based detection when driver is wrapped by middleware | ||
|
||
if (!method_exists($connection, 'getNativeConnection')) { | ||
return null; // dbal < 3.3 (released in 2022-01) | ||
} | ||
|
||
try { | ||
$nativeConnection = $connection->getNativeConnection(); | ||
} catch (Throwable $e) { | ||
if ($this->failOnInvalidConnection) { | ||
throw $e; | ||
} | ||
return null; // connection cannot be established | ||
} | ||
|
||
if ($nativeConnection instanceof mysqli) { | ||
return self::MYSQLI; | ||
} | ||
|
||
if ($nativeConnection instanceof SQLite3) { | ||
return self::SQLITE3; | ||
} | ||
|
||
if ($nativeConnection instanceof \PgSql\Connection) { | ||
return self::PGSQL; | ||
} | ||
|
||
if ($nativeConnection instanceof PDO) { | ||
$driverName = $nativeConnection->getAttribute(PDO::ATTR_DRIVER_NAME); | ||
|
||
if ($driverName === 'mysql') { | ||
return self::PDO_MYSQL; | ||
} | ||
|
||
if ($driverName === 'sqlite') { | ||
return self::PDO_SQLITE; | ||
} | ||
|
||
if ($driverName === 'pgsql') { | ||
return self::PDO_PGSQL; | ||
} | ||
|
||
if ($driverName === 'oci') { // semi-verified (https://stackoverflow.com/questions/10090709/get-current-pdo-driver-from-existing-connection/10090754#comment12923198_10090754) | ||
return self::PDO_OCI; | ||
} | ||
|
||
if ($driverName === 'sqlsrv') { | ||
return self::PDO_SQLSRV; | ||
} | ||
} | ||
|
||
if (is_resource($nativeConnection)) { | ||
$resourceType = get_resource_type($nativeConnection); | ||
|
||
if (strpos($resourceType, 'oci') !== false) { // not verified | ||
return self::OCI8; | ||
} | ||
|
||
if (strpos($resourceType, 'db2') !== false) { // not verified | ||
return self::IBM_DB2; | ||
} | ||
|
||
if (strpos($resourceType, 'SQL Server Connection') !== false) { | ||
return self::SQLSRV; | ||
} | ||
|
||
if (strpos($resourceType, 'pgsql link') !== false) { | ||
return self::PGSQL; | ||
} | ||
} | ||
|
||
return null; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
29 changes: 29 additions & 0 deletions
29
src/Type/Doctrine/Descriptors/DoctrineTypeDriverAwareDescriptor.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
<?php declare(strict_types = 1); | ||
|
||
namespace PHPStan\Type\Doctrine\Descriptors; | ||
|
||
use Doctrine\DBAL\Connection; | ||
use PHPStan\Type\Type; | ||
|
||
/** @api */ | ||
interface DoctrineTypeDriverAwareDescriptor | ||
{ | ||
|
||
/** | ||
* This is used for inferring how database fetches column of such type | ||
* It should return the native type without stringification that may occur on certain PHP versions or driver configuration | ||
* | ||
* This is not used for direct column type inferring, | ||
* but when such column appears in expression like SELECT MAX(e.field) | ||
* | ||
* See: https://github.com/janedbal/php-database-drivers-fetch-test | ||
* | ||
* mysql sqlite pdo_pgsql pgsql | ||
* - decimal: string int|float string string | ||
* - float: float float string float | ||
* - bigint: int int int int | ||
* - bool: int int bool bool | ||
*/ | ||
public function getDatabaseInternalTypeForDriver(Connection $connection): Type; | ||
|
||
} |
Oops, something went wrong.