From 7101fa98703e549d9547c77608d5f9733fe78273 Mon Sep 17 00:00:00 2001 From: Paul Jura Date: Mon, 27 Jul 2026 10:01:15 +1000 Subject: [PATCH] chore(deps): allow nesbot/carbon 3.x for Symfony 7 translation support Carbon 2.x caps symfony/translation at ^6.0, which blocks consumers from upgrading to Symfony 7. Carbon 3.x allows symfony/translation ^7.0 || ^8.0, so widening the constraint to ^2.63.0 || ^3.0 unblocks Symfony 7 upgrades without breaking existing Carbon 2.x consumers. Changes: - composer.json: widen nesbot/carbon to ^2.63.0 || ^3.0 - DateField/DatetimeField/TimeField: reject non-string, non-DateTimeInterface inputs. Carbon 3.x is more permissive than 2.x and treats bool/int values as Unix timestamps, which silently accepted invalid input (e.g. true). DurationField already had this guard. - FieldTypesTest: compare CarbonInterval by spec() instead of whole-object assertEquals. Carbon 3.x changed internal CarbonInterval properties (originalInput) breaking whole-object comparison. The interval values are identical; only the internal Carbon state differs. All 148 tests pass with both Carbon 2.73.0 and Carbon 3.13.1. --- composer.json | 2 +- src/Fields/DateField.php | 4 ++++ src/Fields/DatetimeField.php | 4 ++++ src/Fields/TimeField.php | 4 ++++ tests/FieldTypesTest.php | 9 ++++++++- 5 files changed, 21 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index c701831..33c66fe 100644 --- a/composer.json +++ b/composer.json @@ -7,7 +7,7 @@ "ext-mbstring": "*", "ext-json": "*", "justinrainbow/json-schema": "^5.2.10", - "nesbot/carbon": "^2.63.0", + "nesbot/carbon": "^2.63.0 || ^3.0", "jmikola/geojson": "^1.0" }, "require-dev": { diff --git a/src/Fields/DateField.php b/src/Fields/DateField.php index 6e1be82..0284c06 100644 --- a/src/Fields/DateField.php +++ b/src/Fields/DateField.php @@ -11,6 +11,10 @@ class DateField extends BaseField protected function validateCastValue($val) { + if (!is_string($val) && !$val instanceof \DateTimeInterface) { + throw $this->getValidationException('must be string or datetime', $val); + } + if ('any' === $this->format()) { try { $date = new Carbon($val); diff --git a/src/Fields/DatetimeField.php b/src/Fields/DatetimeField.php index 5b10bda..60a0f27 100644 --- a/src/Fields/DatetimeField.php +++ b/src/Fields/DatetimeField.php @@ -9,6 +9,10 @@ class DatetimeField extends BaseField { protected function validateCastValue($val) { + if (!is_string($val) && !$val instanceof \DateTimeInterface) { + throw $this->getValidationException('must be string or datetime', $val); + } + $val = trim($val); switch ($this->format()) { case 'default': diff --git a/src/Fields/TimeField.php b/src/Fields/TimeField.php index 9a50578..6fc4dbd 100644 --- a/src/Fields/TimeField.php +++ b/src/Fields/TimeField.php @@ -13,6 +13,10 @@ class TimeField extends BaseField { protected function validateCastValue($val) { + if (!is_string($val) && !$val instanceof \DateTimeInterface) { + throw $this->getValidationException('must be string or datetime', $val); + } + switch ($this->format()) { case 'default': $time = explode(':', $val); diff --git a/tests/FieldTypesTest.php b/tests/FieldTypesTest.php index 75399bc..ff34317 100644 --- a/tests/FieldTypesTest.php +++ b/tests/FieldTypesTest.php @@ -579,7 +579,14 @@ protected function assertFieldTestData($fieldType, $testData): void if (self::ERROR === $expectedCastValue) { $this->assertNotEmpty($field->validateValue($inputValue), $assertMessage); } elseif (is_object($expectedCastValue)) { - $this->assertEquals($expectedCastValue, $field->castValue($inputValue), $assertMessage); + $castValue = $field->castValue($inputValue); + if ($expectedCastValue instanceof CarbonInterval && $castValue instanceof CarbonInterval) { + // Carbon 3.x changed internal CarbonInterval properties (e.g. originalInput), + // breaking whole-object comparison. Compare the ISO 8601 spec instead. + $this->assertEquals($expectedCastValue->spec(), $castValue->spec(), $assertMessage); + } else { + $this->assertEquals($expectedCastValue, $castValue, $assertMessage); + } } else { $this->assertSame($expectedCastValue, $field->castValue($inputValue), $assertMessage); }