-
-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
introduce PdoMysqlQueryReflector and deprecate PdoQueryReflector (#370)
Co-authored-by: Markus Staab <[email protected]>
- Loading branch information
Showing
41 changed files
with
183 additions
and
161 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,12 @@ | ||
# mark cache files as generated files, so they don't show up in Pull Requests diffs | ||
.phpstan-dba-mysqli.cache linguist-generated=true | ||
.phpstan-dba-pdo.cache linguist-generated=true | ||
.phpstan-dba-pdo-mysql.cache linguist-generated=true | ||
.phpunit-phpstan-dba-mysqli.cache linguist-generated=true | ||
.phpunit-phpstan-dba-pdo.cache linguist-generated=true | ||
.phpunit-phpstan-dba-pdo-mysql.cache linguist-generated=true | ||
.phpunit-phpstan-dba-pdo-pgsql.cache linguist-generated=true | ||
|
||
# Exclude non-essential files from dist | ||
/tests export-ignore | ||
.phpunit-phpstan-dba-mysqli.cache | ||
.phpunit-phpstan-dba-pdo.cache | ||
.phpunit-phpstan-dba-pdo-mysql.cache | ||
.phpunit-phpstan-dba-pdo-pgsql.cache |
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
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
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
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,125 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace staabm\PHPStanDba\QueryReflection; | ||
|
||
use Iterator; | ||
use PDO; | ||
use PDOException; | ||
use PHPStan\ShouldNotHappenException; | ||
use PHPStan\Type\Type; | ||
use staabm\PHPStanDba\TypeMapping\MysqlTypeMapper; | ||
use staabm\PHPStanDba\TypeMapping\TypeMapper; | ||
|
||
/** | ||
* @phpstan-import-type ColumnMeta from BasePdoQueryReflector | ||
* | ||
* @final | ||
*/ | ||
class PdoMysqlQueryReflector extends BasePdoQueryReflector | ||
{ | ||
public function __construct(PDO $pdo) | ||
{ | ||
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); | ||
|
||
parent::__construct($pdo, new MysqlTypeMapper()); | ||
} | ||
|
||
/** @return PDOException|list<ColumnMeta>|null */ | ||
protected function simulateQuery(string $queryString) | ||
{ | ||
if (\array_key_exists($queryString, $this->cache)) { | ||
return $this->cache[$queryString]; | ||
} | ||
|
||
if (\count($this->cache) > self::MAX_CACHE_SIZE) { | ||
// make room for the next element by randomly removing a existing one | ||
array_shift($this->cache); | ||
} | ||
|
||
$simulatedQuery = QuerySimulation::simulate($queryString); | ||
if (null === $simulatedQuery) { | ||
return $this->cache[$queryString] = null; | ||
} | ||
|
||
try { | ||
$this->pdo->beginTransaction(); | ||
} catch (PDOException $e) { | ||
// not all drivers may support transactions | ||
} | ||
|
||
try { | ||
$stmt = $this->pdo->query($simulatedQuery); | ||
} catch (PDOException $e) { | ||
return $this->cache[$queryString] = $e; | ||
} finally { | ||
try { | ||
$this->pdo->rollBack(); | ||
} catch (PDOException $e) { | ||
// not all drivers may support transactions | ||
} | ||
} | ||
|
||
$this->cache[$queryString] = []; | ||
$columnCount = $stmt->columnCount(); | ||
$columnIndex = 0; | ||
while ($columnIndex < $columnCount) { | ||
$columnMeta = $stmt->getColumnMeta($columnIndex); | ||
|
||
if (false === $columnMeta || !\array_key_exists('table', $columnMeta)) { | ||
throw new ShouldNotHappenException('Failed to get column meta for column index '.$columnIndex); | ||
} | ||
|
||
// Native type may not be set, for example in case of JSON column. | ||
if (!\array_key_exists('native_type', $columnMeta)) { | ||
$columnMeta['native_type'] = \PDO::PARAM_INT === $columnMeta['pdo_type'] ? 'INT' : 'STRING'; | ||
} | ||
|
||
$flags = $this->emulateFlags($columnMeta['native_type'], $columnMeta['table'], $columnMeta['name']); | ||
foreach ($flags as $flag) { | ||
$columnMeta['flags'][] = $flag; | ||
} | ||
|
||
// @phpstan-ignore-next-line | ||
$this->cache[$queryString][$columnIndex] = $columnMeta; | ||
++$columnIndex; | ||
} | ||
|
||
return $this->cache[$queryString]; | ||
} | ||
|
||
/** | ||
* @return Iterator<string, TypeMapper::FLAG_*> | ||
*/ | ||
protected function checkInformationSchema(string $tableName): Iterator | ||
{ | ||
if (null === $this->stmt) { | ||
$this->stmt = $this->pdo->prepare( | ||
// EXTRA, COLUMN_NAME seems to be nullable in mariadb | ||
'SELECT | ||
coalesce(COLUMN_NAME, "") as COLUMN_NAME, | ||
coalesce(EXTRA, "") as EXTRA, | ||
COLUMN_TYPE | ||
FROM information_schema.columns | ||
WHERE table_name = ? AND table_schema = DATABASE()' | ||
); | ||
} | ||
|
||
$this->stmt->execute([$tableName]); | ||
$result = $this->stmt->fetchAll(PDO::FETCH_ASSOC); | ||
|
||
foreach ($result as $row) { | ||
$extra = $row['EXTRA']; | ||
$columnType = $row['COLUMN_TYPE']; | ||
$columnName = $row['COLUMN_NAME']; | ||
|
||
if (str_contains($extra, 'auto_increment')) { | ||
yield $columnName => TypeMapper::FLAG_AUTO_INCREMENT; | ||
} | ||
if (str_contains($columnType, 'unsigned')) { | ||
yield $columnName => TypeMapper::FLAG_UNSIGNED; | ||
} | ||
} | ||
} | ||
} |
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
Oops, something went wrong.