From: =?utf-8?q?David_Pr=C3=A9vot?= <taffit@debian.org>
Date: Sun, 26 Jan 2025 13:30:31 +0100
Subject: Ship php-carbon-doctrine-types in NEW processing

---
 src/Carbon/Doctrine/CarbonDoctrineType.php       |  16 +++
 src/Carbon/Doctrine/CarbonImmutableType.php      |   9 ++
 src/Carbon/Doctrine/CarbonType.php               |   9 ++
 src/Carbon/Doctrine/CarbonTypeConverter.php      | 131 +++++++++++++++++++++++
 src/Carbon/Doctrine/DateTimeDefaultPrecision.php |  30 ++++++
 src/Carbon/Doctrine/DateTimeImmutableType.php    |  32 ++++++
 src/Carbon/Doctrine/DateTimeType.php             |  24 +++++
 7 files changed, 251 insertions(+)
 create mode 100644 src/Carbon/Doctrine/CarbonDoctrineType.php
 create mode 100644 src/Carbon/Doctrine/CarbonImmutableType.php
 create mode 100644 src/Carbon/Doctrine/CarbonType.php
 create mode 100644 src/Carbon/Doctrine/CarbonTypeConverter.php
 create mode 100644 src/Carbon/Doctrine/DateTimeDefaultPrecision.php
 create mode 100644 src/Carbon/Doctrine/DateTimeImmutableType.php
 create mode 100644 src/Carbon/Doctrine/DateTimeType.php

diff --git a/src/Carbon/Doctrine/CarbonDoctrineType.php b/src/Carbon/Doctrine/CarbonDoctrineType.php
new file mode 100644
index 0000000..a63a9b8
--- /dev/null
+++ b/src/Carbon/Doctrine/CarbonDoctrineType.php
@@ -0,0 +1,16 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Carbon\Doctrine;
+
+use Doctrine\DBAL\Platforms\AbstractPlatform;
+
+interface CarbonDoctrineType
+{
+    public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform);
+
+    public function convertToPHPValue(mixed $value, AbstractPlatform $platform);
+
+    public function convertToDatabaseValue($value, AbstractPlatform $platform);
+}
diff --git a/src/Carbon/Doctrine/CarbonImmutableType.php b/src/Carbon/Doctrine/CarbonImmutableType.php
new file mode 100644
index 0000000..8fc6bef
--- /dev/null
+++ b/src/Carbon/Doctrine/CarbonImmutableType.php
@@ -0,0 +1,9 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Carbon\Doctrine;
+
+class CarbonImmutableType extends DateTimeImmutableType implements CarbonDoctrineType
+{
+}
diff --git a/src/Carbon/Doctrine/CarbonType.php b/src/Carbon/Doctrine/CarbonType.php
new file mode 100644
index 0000000..206b0e3
--- /dev/null
+++ b/src/Carbon/Doctrine/CarbonType.php
@@ -0,0 +1,9 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Carbon\Doctrine;
+
+class CarbonType extends DateTimeType implements CarbonDoctrineType
+{
+}
diff --git a/src/Carbon/Doctrine/CarbonTypeConverter.php b/src/Carbon/Doctrine/CarbonTypeConverter.php
new file mode 100644
index 0000000..a813311
--- /dev/null
+++ b/src/Carbon/Doctrine/CarbonTypeConverter.php
@@ -0,0 +1,131 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Carbon\Doctrine;
+
+use Carbon\Carbon;
+use Carbon\CarbonInterface;
+use DateTimeInterface;
+use Doctrine\DBAL\Platforms\AbstractPlatform;
+use Doctrine\DBAL\Platforms\DB2Platform;
+use Doctrine\DBAL\Platforms\OraclePlatform;
+use Doctrine\DBAL\Platforms\SQLitePlatform;
+use Doctrine\DBAL\Platforms\SQLServerPlatform;
+use Doctrine\DBAL\Types\Exception\InvalidType;
+use Doctrine\DBAL\Types\Exception\ValueNotConvertible;
+use Exception;
+
+/**
+ * @template T of CarbonInterface
+ */
+trait CarbonTypeConverter
+{
+    /**
+     * This property differentiates types installed by carbonphp/carbon-doctrine-types
+     * from the ones embedded previously in nesbot/carbon source directly.
+     *
+     * @readonly
+     */
+    public bool $external = true;
+
+    /**
+     * @return class-string<T>
+     */
+    protected function getCarbonClassName(): string
+    {
+        return Carbon::class;
+    }
+
+    public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform): string
+    {
+        $precision = min(
+            $fieldDeclaration['precision'] ?? DateTimeDefaultPrecision::get(),
+            $this->getMaximumPrecision($platform),
+        );
+
+        $type = parent::getSQLDeclaration($fieldDeclaration, $platform);
+
+        if (!$precision) {
+            return $type;
+        }
+
+        if (str_contains($type, '(')) {
+            return preg_replace('/\(\d+\)/', "($precision)", $type);
+        }
+
+        [$before, $after] = explode(' ', "$type ");
+
+        return trim("$before($precision) $after");
+    }
+
+    /**
+     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+     */
+    public function convertToDatabaseValue($value, AbstractPlatform $platform): ?string
+    {
+        if ($value === null) {
+            return $value;
+        }
+
+        if ($value instanceof DateTimeInterface) {
+            return $value->format('Y-m-d H:i:s.u');
+        }
+
+        throw InvalidType::new(
+            $value,
+            static::class,
+            ['null', 'DateTime', 'Carbon']
+        );
+    }
+
+    private function doConvertToPHPValue(mixed $value)
+    {
+        $class = $this->getCarbonClassName();
+
+        if ($value === null || is_a($value, $class)) {
+            return $value;
+        }
+
+        if ($value instanceof DateTimeInterface) {
+            return $class::instance($value);
+        }
+
+        $date = null;
+        $error = null;
+
+        try {
+            $date = $class::parse($value);
+        } catch (Exception $exception) {
+            $error = $exception;
+        }
+
+        if (!$date) {
+            throw ValueNotConvertible::new(
+                $value,
+                static::class,
+                'Y-m-d H:i:s.u or any format supported by '.$class.'::parse()',
+                $error
+            );
+        }
+
+        return $date;
+    }
+
+    private function getMaximumPrecision(AbstractPlatform $platform): int
+    {
+        if ($platform instanceof DB2Platform) {
+            return 12;
+        }
+
+        if ($platform instanceof OraclePlatform) {
+            return 9;
+        }
+
+        if ($platform instanceof SQLServerPlatform || $platform instanceof SQLitePlatform) {
+            return 3;
+        }
+
+        return 6;
+    }
+}
diff --git a/src/Carbon/Doctrine/DateTimeDefaultPrecision.php b/src/Carbon/Doctrine/DateTimeDefaultPrecision.php
new file mode 100644
index 0000000..cd9896f
--- /dev/null
+++ b/src/Carbon/Doctrine/DateTimeDefaultPrecision.php
@@ -0,0 +1,30 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Carbon\Doctrine;
+
+class DateTimeDefaultPrecision
+{
+    private static $precision = 6;
+
+    /**
+     * Change the default Doctrine datetime and datetime_immutable precision.
+     *
+     * @param int $precision
+     */
+    public static function set(int $precision): void
+    {
+        self::$precision = $precision;
+    }
+
+    /**
+     * Get the default Doctrine datetime and datetime_immutable precision.
+     *
+     * @return int
+     */
+    public static function get(): int
+    {
+        return self::$precision;
+    }
+}
diff --git a/src/Carbon/Doctrine/DateTimeImmutableType.php b/src/Carbon/Doctrine/DateTimeImmutableType.php
new file mode 100644
index 0000000..fd6467e
--- /dev/null
+++ b/src/Carbon/Doctrine/DateTimeImmutableType.php
@@ -0,0 +1,32 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Carbon\Doctrine;
+
+use Carbon\CarbonImmutable;
+use DateTimeImmutable;
+use Doctrine\DBAL\Platforms\AbstractPlatform;
+use Doctrine\DBAL\Types\VarDateTimeImmutableType;
+
+class DateTimeImmutableType extends VarDateTimeImmutableType implements CarbonDoctrineType
+{
+    /** @use CarbonTypeConverter<CarbonImmutable> */
+    use CarbonTypeConverter;
+
+    /**
+     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+     */
+    public function convertToPHPValue(mixed $value, AbstractPlatform $platform): ?CarbonImmutable
+    {
+        return $this->doConvertToPHPValue($value);
+    }
+
+    /**
+     * @return class-string<CarbonImmutable>
+     */
+    protected function getCarbonClassName(): string
+    {
+        return CarbonImmutable::class;
+    }
+}
diff --git a/src/Carbon/Doctrine/DateTimeType.php b/src/Carbon/Doctrine/DateTimeType.php
new file mode 100644
index 0000000..89e4b79
--- /dev/null
+++ b/src/Carbon/Doctrine/DateTimeType.php
@@ -0,0 +1,24 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Carbon\Doctrine;
+
+use Carbon\Carbon;
+use DateTime;
+use Doctrine\DBAL\Platforms\AbstractPlatform;
+use Doctrine\DBAL\Types\VarDateTimeType;
+
+class DateTimeType extends VarDateTimeType implements CarbonDoctrineType
+{
+    /** @use CarbonTypeConverter<Carbon> */
+    use CarbonTypeConverter;
+
+    /**
+     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+     */
+    public function convertToPHPValue(mixed $value, AbstractPlatform $platform): ?Carbon
+    {
+        return $this->doConvertToPHPValue($value);
+    }
+}
