From c031ad039f22a7976be307779d176ed5611e83ea Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Fri, 22 Sep 2023 13:49:01 +0200 Subject: [PATCH] Stricter `DateTime` types (#6161) --- UPGRADE.md | 19 ++++++++++++++ src/Platforms/AbstractPlatform.php | 4 +-- src/Types/DateImmutableType.php | 10 +++++++- src/Types/DateTimeImmutableType.php | 10 +++++++- src/Types/DateTimeType.php | 35 ++++---------------------- src/Types/DateTimeTzImmutableType.php | 10 +++++++- src/Types/DateTimeTzType.php | 33 +++--------------------- src/Types/DateType.php | 35 ++++---------------------- src/Types/PhpDateMappingType.php | 14 +++++++++++ src/Types/PhpTimeMappingType.php | 14 +++++++++++ src/Types/TimeImmutableType.php | 10 +++++++- src/Types/TimeType.php | 35 ++++---------------------- src/Types/VarDateTimeImmutableType.php | 2 +- src/Types/VarDateTimeType.php | 5 ++-- tests/Types/BaseDateTypeTestCase.php | 27 +------------------- 15 files changed, 108 insertions(+), 155 deletions(-) create mode 100644 src/Types/PhpDateMappingType.php create mode 100644 src/Types/PhpTimeMappingType.php diff --git a/UPGRADE.md b/UPGRADE.md index 2ea323e3891..213738383b1 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -8,6 +8,25 @@ awareness about deprecated code. # Upgrade to 4.0 +## BC BREAK: Stricter `DateTime` types + +The following types don't accept or return `DateTimeImmutable` instances anymore: + +* `DateTimeType` +* `DateTimeTzType` +* `DateType` +* `TimeType` +* `VarDateTimeType` + +As a consequence, the following type classes don't extend their mutable +counterparts anymore: + +* `DateTimeImmutableType` +* `DateTimeTzImmutableType` +* `DateImmutableType` +* `TimeImmutableType` +* `VarDateTimeImmutableType` + ## BC BREAK: Remove legacy execute and fetch methods. The following methods have been removed: diff --git a/src/Platforms/AbstractPlatform.php b/src/Platforms/AbstractPlatform.php index 0eb80c83adf..51363f46bd9 100644 --- a/src/Platforms/AbstractPlatform.php +++ b/src/Platforms/AbstractPlatform.php @@ -1463,11 +1463,11 @@ public function getDefaultValueDeclarationSQL(array $column): string return ' DEFAULT ' . $this->getCurrentTimestampSQL(); } - if ($type instanceof Types\TimeType && $default === $this->getCurrentTimeSQL()) { + if ($type instanceof Types\PhpTimeMappingType && $default === $this->getCurrentTimeSQL()) { return ' DEFAULT ' . $this->getCurrentTimeSQL(); } - if ($type instanceof Types\DateType && $default === $this->getCurrentDateSQL()) { + if ($type instanceof Types\PhpDateMappingType && $default === $this->getCurrentDateSQL()) { return ' DEFAULT ' . $this->getCurrentDateSQL(); } diff --git a/src/Types/DateImmutableType.php b/src/Types/DateImmutableType.php index 3d03c2b946b..732efcd1a67 100644 --- a/src/Types/DateImmutableType.php +++ b/src/Types/DateImmutableType.php @@ -12,8 +12,16 @@ /** * Immutable type of {@see DateType}. */ -class DateImmutableType extends DateType +class DateImmutableType extends Type implements PhpDateMappingType { + /** + * {@inheritDoc} + */ + public function getSQLDeclaration(array $column, AbstractPlatform $platform): string + { + return $platform->getDateTypeDeclarationSQL($column); + } + /** * @param T $value * diff --git a/src/Types/DateTimeImmutableType.php b/src/Types/DateTimeImmutableType.php index df9c20fa49f..2d49d1dd763 100644 --- a/src/Types/DateTimeImmutableType.php +++ b/src/Types/DateTimeImmutableType.php @@ -13,8 +13,16 @@ /** * Immutable type of {@see DateTimeType}. */ -class DateTimeImmutableType extends DateTimeType +class DateTimeImmutableType extends Type implements PhpDateTimeMappingType { + /** + * {@inheritDoc} + */ + public function getSQLDeclaration(array $column, AbstractPlatform $platform): string + { + return $platform->getDateTimeTypeDeclarationSQL($column); + } + /** * @param T $value * diff --git a/src/Types/DateTimeType.php b/src/Types/DateTimeType.php index ea0ed6b133b..9fd0ba028fc 100644 --- a/src/Types/DateTimeType.php +++ b/src/Types/DateTimeType.php @@ -5,12 +5,9 @@ namespace Doctrine\DBAL\Types; use DateTime; -use DateTimeImmutable; -use DateTimeInterface; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Types\Exception\InvalidFormat; use Doctrine\DBAL\Types\Exception\InvalidType; -use Doctrine\Deprecations\Deprecation; use Exception; /** @@ -39,49 +36,27 @@ public function convertToDatabaseValue(mixed $value, AbstractPlatform $platform) return $value; } - if ($value instanceof DateTimeImmutable) { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/6017', - 'Passing an instance of %s is deprecated, use %s::%s() instead.', - $value::class, - DateTimeImmutableType::class, - __FUNCTION__, - ); - } - - if ($value instanceof DateTimeInterface) { + if ($value instanceof DateTime) { return $value->format($platform->getDateTimeFormatString()); } throw InvalidType::new( $value, static::class, - ['null', DateTime::class, DateTimeImmutable::class], + ['null', DateTime::class], ); } /** * @param T $value * - * @return (T is null ? null : DateTimeInterface) + * @return (T is null ? null : DateTime) * * @template T */ - public function convertToPHPValue(mixed $value, AbstractPlatform $platform): ?DateTimeInterface + public function convertToPHPValue(mixed $value, AbstractPlatform $platform): ?DateTime { - if ($value instanceof DateTimeImmutable) { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/6017', - 'Passing an instance of %s is deprecated, use %s::%s() instead.', - $value::class, - DateTimeImmutableType::class, - __FUNCTION__, - ); - } - - if ($value === null || $value instanceof DateTimeInterface) { + if ($value === null || $value instanceof DateTime) { return $value; } diff --git a/src/Types/DateTimeTzImmutableType.php b/src/Types/DateTimeTzImmutableType.php index db664616c19..a7d662dca0e 100644 --- a/src/Types/DateTimeTzImmutableType.php +++ b/src/Types/DateTimeTzImmutableType.php @@ -12,8 +12,16 @@ /** * Immutable type of {@see DateTimeTzType}. */ -class DateTimeTzImmutableType extends DateTimeTzType +class DateTimeTzImmutableType extends Type implements PhpDateTimeMappingType { + /** + * {@inheritDoc} + */ + public function getSQLDeclaration(array $column, AbstractPlatform $platform): string + { + return $platform->getDateTimeTzTypeDeclarationSQL($column); + } + /** * @psalm-param T $value * diff --git a/src/Types/DateTimeTzType.php b/src/Types/DateTimeTzType.php index 4f077e3ca31..b4cb7a13fe0 100644 --- a/src/Types/DateTimeTzType.php +++ b/src/Types/DateTimeTzType.php @@ -5,12 +5,9 @@ namespace Doctrine\DBAL\Types; use DateTime; -use DateTimeImmutable; -use DateTimeInterface; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Types\Exception\InvalidFormat; use Doctrine\DBAL\Types\Exception\InvalidType; -use Doctrine\Deprecations\Deprecation; /** * DateTime type saving additional timezone information. @@ -51,18 +48,7 @@ public function convertToDatabaseValue(mixed $value, AbstractPlatform $platform) return $value; } - if ($value instanceof DateTimeImmutable) { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/6017', - 'Passing an instance of %s is deprecated, use %s::%s() instead.', - $value::class, - DateTimeTzImmutableType::class, - __FUNCTION__, - ); - } - - if ($value instanceof DateTimeInterface) { + if ($value instanceof DateTime) { return $value->format($platform->getDateTimeTzFormatString()); } @@ -76,24 +62,13 @@ public function convertToDatabaseValue(mixed $value, AbstractPlatform $platform) /** * @param T $value * - * @return (T is null ? null : DateTimeInterface) + * @return (T is null ? null : DateTime) * * @template T */ - public function convertToPHPValue(mixed $value, AbstractPlatform $platform): ?DateTimeInterface + public function convertToPHPValue(mixed $value, AbstractPlatform $platform): ?DateTime { - if ($value instanceof DateTimeImmutable) { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/6017', - 'Passing an instance of %s is deprecated, use %s::%s() instead.', - $value::class, - DateTimeTzImmutableType::class, - __FUNCTION__, - ); - } - - if ($value === null || $value instanceof DateTimeInterface) { + if ($value === null || $value instanceof DateTime) { return $value; } diff --git a/src/Types/DateType.php b/src/Types/DateType.php index 7c0aaf60e8a..6548c53bae3 100644 --- a/src/Types/DateType.php +++ b/src/Types/DateType.php @@ -5,17 +5,14 @@ namespace Doctrine\DBAL\Types; use DateTime; -use DateTimeImmutable; -use DateTimeInterface; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Types\Exception\InvalidFormat; use Doctrine\DBAL\Types\Exception\InvalidType; -use Doctrine\Deprecations\Deprecation; /** * Type that maps an SQL DATE to a PHP Date object. */ -class DateType extends Type +class DateType extends Type implements PhpDateMappingType { /** * {@inheritDoc} @@ -38,18 +35,7 @@ public function convertToDatabaseValue(mixed $value, AbstractPlatform $platform) return $value; } - if ($value instanceof DateTimeInterface) { - if ($value instanceof DateTimeImmutable) { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/6017', - 'Passing an instance of %s is deprecated, use %s::%s() instead.', - $value::class, - DateImmutableType::class, - __FUNCTION__, - ); - } - + if ($value instanceof DateTime) { return $value->format($platform->getDateFormatString()); } @@ -59,24 +45,13 @@ public function convertToDatabaseValue(mixed $value, AbstractPlatform $platform) /** * @param T $value * - * @return (T is null ? null : DateTimeInterface) + * @return (T is null ? null : DateTime) * * @template T */ - public function convertToPHPValue(mixed $value, AbstractPlatform $platform): ?DateTimeInterface + public function convertToPHPValue(mixed $value, AbstractPlatform $platform): ?DateTime { - if ($value instanceof DateTimeImmutable) { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/6017', - 'Passing an instance of %s is deprecated, use %s::%s() instead.', - $value::class, - DateImmutableType::class, - __FUNCTION__, - ); - } - - if ($value === null || $value instanceof DateTimeInterface) { + if ($value === null || $value instanceof DateTime) { return $value; } diff --git a/src/Types/PhpDateMappingType.php b/src/Types/PhpDateMappingType.php new file mode 100644 index 00000000000..200f2772c21 --- /dev/null +++ b/src/Types/PhpDateMappingType.php @@ -0,0 +1,14 @@ +getTimeTypeDeclarationSQL($column); + } + /** * @param T $value * diff --git a/src/Types/TimeType.php b/src/Types/TimeType.php index 7e8bbef4101..0f96fd56101 100644 --- a/src/Types/TimeType.php +++ b/src/Types/TimeType.php @@ -5,17 +5,14 @@ namespace Doctrine\DBAL\Types; use DateTime; -use DateTimeImmutable; -use DateTimeInterface; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Types\Exception\InvalidFormat; use Doctrine\DBAL\Types\Exception\InvalidType; -use Doctrine\Deprecations\Deprecation; /** * Type that maps an SQL TIME to a PHP DateTime object. */ -class TimeType extends Type +class TimeType extends Type implements PhpTimeMappingType { /** * {@inheritDoc} @@ -38,18 +35,7 @@ public function convertToDatabaseValue(mixed $value, AbstractPlatform $platform) return $value; } - if ($value instanceof DateTimeImmutable) { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/6017', - 'Passing an instance of %s is deprecated, use %s::%s() instead.', - $value::class, - TimeImmutableType::class, - __FUNCTION__, - ); - } - - if ($value instanceof DateTimeInterface) { + if ($value instanceof DateTime) { return $value->format($platform->getTimeFormatString()); } @@ -59,24 +45,13 @@ public function convertToDatabaseValue(mixed $value, AbstractPlatform $platform) /** * @param T $value * - * @return (T is null ? null : DateTimeInterface) + * @return (T is null ? null : DateTime) * * @template T */ - public function convertToPHPValue(mixed $value, AbstractPlatform $platform): ?DateTimeInterface + public function convertToPHPValue(mixed $value, AbstractPlatform $platform): ?DateTime { - if ($value instanceof DateTimeImmutable) { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/6017', - 'Passing an instance of %s is deprecated, use %s::%s() instead.', - $value::class, - TimeImmutableType::class, - __FUNCTION__, - ); - } - - if ($value === null || $value instanceof DateTimeInterface) { + if ($value === null || $value instanceof DateTime) { return $value; } diff --git a/src/Types/VarDateTimeImmutableType.php b/src/Types/VarDateTimeImmutableType.php index a6b09ca50f7..67c2cd8b8c6 100644 --- a/src/Types/VarDateTimeImmutableType.php +++ b/src/Types/VarDateTimeImmutableType.php @@ -13,7 +13,7 @@ /** * Immutable type of {@see VarDateTimeType}. */ -class VarDateTimeImmutableType extends VarDateTimeType +class VarDateTimeImmutableType extends DateTimeImmutableType { /** * @param T $value diff --git a/src/Types/VarDateTimeType.php b/src/Types/VarDateTimeType.php index b791040daa4..55dec49309b 100644 --- a/src/Types/VarDateTimeType.php +++ b/src/Types/VarDateTimeType.php @@ -5,7 +5,6 @@ namespace Doctrine\DBAL\Types; use DateTime; -use DateTimeInterface; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Types\Exception\ValueNotConvertible; use Exception; @@ -22,11 +21,11 @@ class VarDateTimeType extends DateTimeType /** * @param T $value * - * @return (T is null ? null : DateTimeInterface) + * @return (T is null ? null : DateTime) * * @template T */ - public function convertToPHPValue(mixed $value, AbstractPlatform $platform): ?DateTimeInterface + public function convertToPHPValue(mixed $value, AbstractPlatform $platform): ?DateTime { if ($value === null || $value instanceof DateTime) { return $value; diff --git a/tests/Types/BaseDateTypeTestCase.php b/tests/Types/BaseDateTypeTestCase.php index 1fa9fe7a4ac..d4a6a1e2c32 100644 --- a/tests/Types/BaseDateTypeTestCase.php +++ b/tests/Types/BaseDateTypeTestCase.php @@ -5,7 +5,6 @@ namespace Doctrine\DBAL\Tests\Types; use DateTime; -use DateTimeImmutable; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Types\ConversionException; use Doctrine\DBAL\Types\Type; @@ -18,9 +17,7 @@ abstract class BaseDateTypeTestCase extends TestCase { - /** @var AbstractPlatform&MockObject */ - protected AbstractPlatform $platform; - + protected AbstractPlatform&MockObject $platform; protected Type $type; private string $currentTimezone; @@ -60,28 +57,6 @@ public function testConvertDateTimeToPHPValue(): void self::assertSame($date, $this->type->convertToPHPValue($date, $this->platform)); } - /** - * Note that while \@see \DateTimeImmutable is supposed to be handled - * by @see \Doctrine\DBAL\Types\DateTimeImmutableType, previous DBAL versions handled it just fine. - * This test is just in place to prevent further regressions, even if the type is being misused - */ - public function testConvertDateTimeImmutableToPHPValue(): void - { - $date = new DateTimeImmutable('now'); - - self::assertSame($date, $this->type->convertToPHPValue($date, $this->platform)); - } - - /** - * Note that while \@see \DateTimeImmutable is supposed to be handled - * by @see \Doctrine\DBAL\Types\DateTimeImmutableType, previous DBAL versions handled it just fine. - * This test is just in place to prevent further regressions, even if the type is being misused - */ - public function testDateTimeImmutableConvertsToDatabaseValue(): void - { - self::assertIsString($this->type->convertToDatabaseValue(new DateTimeImmutable(), $this->platform)); - } - /** @return mixed[][] */ public static function invalidPHPValuesProvider(): iterable {