Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
5e5d2ce
feat: add support for phpstan level 9 in tests files
shakaran Dec 6, 2025
3c90b6b
feat: add support for return.type in MySQLInheritCharsetTest
shakaran Dec 6, 2025
8105752
feat: add support for argument.type at DefaultValueTest
shakaran Dec 6, 2025
00c486f
feat: solve more argument.type tests errors in phpstan level 9
shakaran Dec 6, 2025
c88167c
feat: reduce to 517 errors solving errors in tests
shakaran Dec 6, 2025
25a1b06
feat: convert to php value types detection and reduce to 492 phpstan …
shakaran Dec 6, 2025
ab98023
feat: reduce to 477 erros fixing is_string values
shakaran Dec 6, 2025
48903bf
fix: solve argument.type and display TypeError throws in ForeignKeyCo…
shakaran Dec 6, 2025
94d65be
feat: solve missingType.checkedException at ForeignKeyConstraintEditor
shakaran Dec 6, 2025
cd1a5eb
feat: solve all cases for missingType.checkedException errors
shakaran Dec 6, 2025
f6cdfe8
fix(phpstan): properly type setPlatformOption method in Column class
shakaran Dec 6, 2025
8e8db9a
feat: ensure key type for int or sring instead mixed in Result
shakaran Dec 6, 2025
ae70117
feat: solve return types mixed and argument.type in drivers
shakaran Dec 6, 2025
c55e31f
feat: solve all binaryOp.invalid and mixed cast in PgSQL/Driver
shakaran Dec 6, 2025
e778e78
feat: solve remaining drivers assignOp.invalid in Drivers
shakaran Dec 6, 2025
edb5094
feat: ensure type of value so phpstan infer the type
shakaran Dec 6, 2025
33b6242
feat: ensure scalar type from mixed value for phpstan infer type at I…
shakaran Dec 6, 2025
c8b3d8c
feat: make intermediate variable for help phpstan infer the type at I…
shakaran Dec 6, 2025
52f8447
feat: solve another case of infer type with intermediate variable
shakaran Dec 6, 2025
be5c2ff
feat: infer types for sqlstate in remaining drivers
shakaran Dec 6, 2025
e662386
feat: solve issues with infer types in IBMDB2
shakaran Dec 6, 2025
87a4512
feat: solve infering type in PDO driver with intermediate variables
shakaran Dec 6, 2025
2d39145
feat: create more intermediate variables for detect infer types
shakaran Dec 6, 2025
039f3c5
feat: add infer type list in array for ArrayResult
shakaran Dec 6, 2025
f0ff4b7
feat: ensure collation and charset and infer types with intermediate …
shakaran Dec 7, 2025
811469e
feat: solve infer type for database param as boolean or string
shakaran Dec 7, 2025
913b41b
feat: check scalars with intermediate variables
shakaran Dec 7, 2025
dc4bd71
feat: solve infering types in SQLiteSchemaManager
shakaran Dec 7, 2025
b999bba
feat: solve problems of casting mixed at SQLServerSchemaManager
shakaran Dec 7, 2025
f5d4853
feat: solve casting errors at PostgreSQLSchemaManager
shakaran Dec 7, 2025
9358874
feat: resolve all errors with infering types mixed in schemas
shakaran Dec 7, 2025
516e2d9
feat: improve renaming of rows with names per column in SQLServerMeta…
shakaran Dec 7, 2025
d68664e
feat: resolve errors in schema index phpstan level 9
shakaran Dec 7, 2025
231b1e2
feat: change rows to named parameters from arry in SQLiteMetadataProv…
shakaran Dec 7, 2025
dc64d0d
feat: update rows by columns names in Db2MetadataProvider
shakaran Dec 7, 2025
90fcfdd
fix: remove castings by asserts and update rows by columns names in O…
shakaran Dec 7, 2025
893bc8d
feat: change rows by columns names in MySQLMetadataProvider
shakaran Dec 7, 2025
2dbe337
feat: resolve errors with types and remove cast for PostgreSQLMetadat…
shakaran Dec 7, 2025
5216b6e
feat: resolve errors on types for Db2MetadataProvider
shakaran Dec 7, 2025
967e913
feat: ensure scalar typs in Driver for PgSQL
shakaran Dec 7, 2025
08f334a
feat: add assert for infer types in abstract platform
shakaran Dec 7, 2025
91f7cd3
feat: enable to return Params phpstan types in TestUtil
shakaran Dec 7, 2025
83ca319
feat: enable phpstan level 9 in config with 0 errors
shakaran Dec 7, 2025
d40f6ba
fix: check all phpcbf, phpunit and phpstan errors
shakaran Dec 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion phpstan.neon.dist
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
parameters:
level: 8
level: 9
phpVersion: 80402
paths:
- src
Expand Down
2 changes: 1 addition & 1 deletion src/Cache/ArrayResult.php
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ public function __serialize(): array
return [$this->columnNames, $this->rows];
}

/** @param mixed[] $data */
/** @param array{0: list<string>, 1: list<list<mixed>>} $data */
public function __unserialize(array $data): void
{
// Handle objects serialized with DBAL 4.1 and earlier.
Expand Down
11 changes: 10 additions & 1 deletion src/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@

use function array_key_exists;
use function array_merge;
use function assert;
use function count;
use function implode;
use function is_array;
Expand Down Expand Up @@ -158,7 +159,15 @@ public function getDatabase(): ?string
$platform = $this->getDatabasePlatform();
$query = $platform->getDummySelectSQL($platform->getCurrentDatabaseExpression());

return $this->fetchOne($query);
$database = $this->fetchOne($query);

if ($database === false) {
return null;
}

assert(is_string($database) && $database !== '');

return $database;
}

/**
Expand Down
12 changes: 11 additions & 1 deletion src/Driver/AbstractOracleDriver/EasyConnectString.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@

use Doctrine\Deprecations\Deprecation;

use function assert;
use function implode;
use function is_array;
use function is_scalar;
use function is_string;
use function sprintf;

/**
Expand Down Expand Up @@ -44,11 +47,16 @@ public static function fromArray(array $params): self
public static function fromConnectionParameters(array $params): self
{
if (isset($params['connectstring'])) {
assert(is_string($params['connectstring']));

return new self($params['connectstring']);
}

if (! isset($params['host'])) {
return new self($params['dbname'] ?? '');
$dbname = $params['dbname'] ?? '';
assert(is_string($dbname));

return new self($dbname);
}

$connectData = [];
Expand Down Expand Up @@ -118,6 +126,8 @@ private static function renderValue(mixed $value): string
return self::renderParams($value);
}

assert(is_scalar($value));

return (string) $value;
}
}
13 changes: 10 additions & 3 deletions src/Driver/IBMDB2/DataSourceName.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@

use SensitiveParameter;

use function assert;
use function implode;
use function is_scalar;
use function is_string;
use function sprintf;
use function str_contains;

Expand Down Expand Up @@ -38,7 +41,8 @@ public static function fromArray(
$chunks = [];

foreach ($params as $key => $value) {
$chunks[] = sprintf('%s=%s', $key, $value);
assert(is_scalar($value)); // Since value is mixed, ensure it's scalar for string conversion
$chunks[] = sprintf('%s=%s', $key, (string) $value);
}

return new self(implode(';', $chunks));
Expand All @@ -52,8 +56,11 @@ public static function fromArray(
public static function fromConnectionParameters(#[SensitiveParameter]
array $params,): self
{
if (isset($params['dbname']) && str_contains($params['dbname'], '=')) {
return new self($params['dbname']);
if (isset($params['dbname'])) {
assert(is_string($params['dbname'])); // Check type to help phpstan infer the type before using str_contains
if (str_contains($params['dbname'], '=')) {
return new self($params['dbname']);
}
}

$dsnParams = [];
Expand Down
3 changes: 3 additions & 0 deletions src/Driver/IBMDB2/Statement.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use function fclose;
use function is_int;
use function is_resource;
use function is_string;
use function stream_copy_to_stream;
use function stream_get_meta_data;
use function tmpfile;
Expand Down Expand Up @@ -60,6 +61,8 @@ public function bindValue(int|string $param, mixed $value, ParameterType $type):
break;

case ParameterType::LARGE_OBJECT:
// Ensure that the value is either a string, a resource, or null (the assert is needed to help to phpstan to infer the type at level 9)
assert(is_string($value) || is_resource($value) || $value === null);
$this->lobs[$param] = &$value;
break;

Expand Down
66 changes: 56 additions & 10 deletions src/Driver/Mysqli/Driver.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@
use mysqli_sql_exception;
use SensitiveParameter;

use function assert;
use function is_array;
use function is_int;
use function is_scalar;
use function is_string;

final class Driver extends AbstractMySQLDriver
{
/**
Expand All @@ -41,14 +47,16 @@ public function connect(
}

try {
$flags = $params['driverOptions'][Connection::OPTION_FLAGS] ?? 0; // Intermediate variable to help phpstan level 9 infer the type
assert(is_int($flags));
$success = @$connection->real_connect(
$host,
$params['user'] ?? '',
$params['password'] ?? '',
$params['dbname'] ?? '',
$params['port'] ?? 0,
$params['unix_socket'] ?? '',
$params['driverOptions'][Connection::OPTION_FLAGS] ?? 0,
$flags,
);
} catch (mysqli_sql_exception $e) {
throw ConnectionFailed::upcast($e);
Expand All @@ -74,10 +82,15 @@ private function compilePreInitializers(
#[SensitiveParameter]
array $params,
): Generator {
unset($params['driverOptions'][Connection::OPTION_FLAGS]);

if (isset($params['driverOptions']) && $params['driverOptions'] !== []) {
yield new Options($params['driverOptions']);
if (isset($params['driverOptions'])) {
assert(is_array($params['driverOptions']));
$driverOptions = $params['driverOptions'];
unset($driverOptions[Connection::OPTION_FLAGS]);

/** @var array<int, mixed> $driverOptions */
if ($driverOptions !== []) {
yield new Options($driverOptions);
}
}

if (
Expand All @@ -90,12 +103,43 @@ private function compilePreInitializers(
return;
}

// Create intermediate variables for phpstan level 9 type inference
$ssl_key = '';
if (isset($params['ssl_key'])) {
assert(is_scalar($params['ssl_key']));
$ssl_key = (string) $params['ssl_key'];
}

$ssl_cert = '';
if (isset($params['ssl_cert'])) {
assert(is_scalar($params['ssl_cert']));
$ssl_cert = (string) $params['ssl_cert'];
}

$ssl_ca = '';
if (isset($params['ssl_ca'])) {
assert(is_scalar($params['ssl_ca']));
$ssl_ca = (string) $params['ssl_ca'];
}

$ssl_capath = '';
if (isset($params['ssl_capath'])) {
assert(is_scalar($params['ssl_capath']));
$ssl_capath = (string) $params['ssl_capath'];
}

$ssl_cipher = '';
if (isset($params['ssl_cipher'])) {
assert(is_scalar($params['ssl_cipher']));
$ssl_cipher = (string) $params['ssl_cipher'];
}

yield new Secure(
$params['ssl_key'] ?? '',
$params['ssl_cert'] ?? '',
$params['ssl_ca'] ?? '',
$params['ssl_capath'] ?? '',
$params['ssl_cipher'] ?? '',
$ssl_key,
$ssl_cert,
$ssl_ca,
$ssl_capath,
$ssl_cipher,
);
}

Expand All @@ -112,6 +156,8 @@ private function compilePostInitializers(
return;
}

assert(is_string($params['charset']));

yield new Charset($params['charset']);
}
}
13 changes: 10 additions & 3 deletions src/Driver/Mysqli/Exception/ConnectionError.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,25 @@
use mysqli_sql_exception;
use ReflectionProperty;

use function assert;
use function is_string;

/** @internal */
final class ConnectionError extends AbstractException
{
public static function new(mysqli $connection): self
{
return new self($connection->error, $connection->sqlstate, $connection->errno);
$sqlstate = $connection->sqlstate;

return new self($connection->error, $sqlstate, $connection->errno);
}

public static function upcast(mysqli_sql_exception $exception): self
{
$p = new ReflectionProperty(mysqli_sql_exception::class, 'sqlstate');
$p = new ReflectionProperty(mysqli_sql_exception::class, 'sqlstate');
$sqlstate = $p->getValue($exception);
assert(is_string($sqlstate)); // Intermediate variable for phpstan type inference

return new self($exception->getMessage(), $p->getValue($exception), $exception->getCode(), $exception);
return new self($exception->getMessage(), $sqlstate, $exception->getCode(), $exception);
}
}
7 changes: 5 additions & 2 deletions src/Driver/Mysqli/Exception/ConnectionFailed.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use ReflectionProperty;

use function assert;
use function is_string;

/** @internal */
final class ConnectionFailed extends AbstractException
Expand All @@ -24,8 +25,10 @@ public static function new(mysqli $connection): self

public static function upcast(mysqli_sql_exception $exception): self
{
$p = new ReflectionProperty(mysqli_sql_exception::class, 'sqlstate');
$p = new ReflectionProperty(mysqli_sql_exception::class, 'sqlstate');
$sqlstate = $p->getValue($exception);
assert(is_string($sqlstate));

return new self($exception->getMessage(), $p->getValue($exception), $exception->getCode(), $exception);
return new self($exception->getMessage(), $sqlstate, $exception->getCode(), $exception);
}
}
8 changes: 6 additions & 2 deletions src/Driver/Mysqli/Exception/InvalidCharset.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
use mysqli_sql_exception;
use ReflectionProperty;

use function assert;
use function is_string;
use function sprintf;

/** @internal */
Expand All @@ -25,11 +27,13 @@ public static function fromCharset(mysqli $connection, string $charset): self

public static function upcast(mysqli_sql_exception $exception, string $charset): self
{
$p = new ReflectionProperty(mysqli_sql_exception::class, 'sqlstate');
$p = new ReflectionProperty(mysqli_sql_exception::class, 'sqlstate');
$sqlstate = $p->getValue($exception);
assert(is_string($sqlstate));

return new self(
sprintf('Failed to set charset "%s": %s', $charset, $exception->getMessage()),
$p->getValue($exception),
$sqlstate,
$exception->getCode(),
$exception,
);
Expand Down
7 changes: 6 additions & 1 deletion src/Driver/Mysqli/Exception/InvalidOption.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,20 @@

use Doctrine\DBAL\Driver\AbstractException;

use function gettype;
use function is_scalar;
use function sprintf;
use function strval;

/** @internal */
final class InvalidOption extends AbstractException
{
public static function fromOption(int $option, mixed $value): self
{
$stringValue = is_scalar($value) || $value === null ? strval($value) : gettype($value);

return new self(
sprintf('Failed to set option %d with value "%s"', $option, $value),
sprintf('Failed to set option %d with value "%s"', $option, $stringValue),
);
}
}
14 changes: 11 additions & 3 deletions src/Driver/Mysqli/Exception/StatementError.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,26 @@
use mysqli_stmt;
use ReflectionProperty;

use function assert;
use function is_string;

/** @internal */
final class StatementError extends AbstractException
{
public static function new(mysqli_stmt $statement): self
{
return new self($statement->error, $statement->sqlstate, $statement->errno);
$sqlstate = $statement->sqlstate;
assert($sqlstate !== '');

return new self($statement->error, $sqlstate, $statement->errno);
}

public static function upcast(mysqli_sql_exception $exception): self
{
$p = new ReflectionProperty(mysqli_sql_exception::class, 'sqlstate');
$p = new ReflectionProperty(mysqli_sql_exception::class, 'sqlstate');
$sqlstate = $p->getValue($exception);
assert(is_string($sqlstate) && $sqlstate !== '');

return new self($exception->getMessage(), $p->getValue($exception), $exception->getCode(), $exception);
return new self($exception->getMessage(), $sqlstate, $exception->getCode(), $exception);
}
}
5 changes: 5 additions & 0 deletions src/Driver/Mysqli/Initializer/Options.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
use Doctrine\DBAL\Driver\Mysqli\Initializer;
use mysqli;

use function assert;
use function is_int;
use function is_string;
use function mysqli_options;

final class Options implements Initializer
Expand All @@ -20,6 +23,8 @@ public function __construct(private readonly array $options)
public function initialize(mysqli $connection): void
{
foreach ($this->options as $option => $value) {
assert(is_int($value) || is_string($value));
/** @var int|string $value */
if (! mysqli_options($connection, $option, $value)) {
throw InvalidOption::fromOption($option, $value);
}
Expand Down
Loading