8000 [Form] Add support for \DateTimeImmutable to date types · symfony/symfony@8d60d72 · GitHub
[go: up one dir, main page]

Skip to content

Commit 8d60d72

Browse files
James Halsalljameshalsall
James Halsall
authored andcommitted
[Form] Add support for \DateTimeImmutable to date types
1 parent 93c0e8a commit 8d60d72

12 files changed

+213
-80
lines changed

src/Symfony/Component/Form/Extension/Core/DataTransformer/BaseDateTimeTransformer.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,19 @@ abstract class BaseDateTimeTransformer implements DataTransformerInterface
2929

3030
protected $outputTimezone;
3131

32+
protected $immutable;
33+
3234
/**
3335
* Constructor.
3436
*
3537
* @param string $inputTimezone The name of the input timezone
3638
* @param string $outputTimezone The name of the output timezone
39+
* @param bool $immutable Whether to use \DateTimeImmutable instead of \DateTime
3740
*
3841
* @throws UnexpectedTypeException if a timezone is not a string
3942
* @throws InvalidArgumentException if a timezone is not valid
4043
*/
41-
public function __construct($inputTimezone = null, $outputTimezone = null)
44+
public function __construct($inputTimezone = null, $outputTimezone = null, $immutable = false)
4245
{
4346
if (null !== $inputTimezone && !is_string($inputTimezone)) {
4447
throw new UnexpectedTypeException($inputTimezone, 'string');
@@ -63,5 +66,12 @@ public function __construct($inputTimezone = null, $outputTimezone = null)
6366
} catch (\Exception $e) {
6467
throw new InvalidArgumentException(sprintf('Output timezone is invalid: %s.', $this->outputTimezone), $e->getCode(), $e);
6568
}
69+
70+
$this->immutable = $immutable;
71+
}
72+
73+
protected function getDateTimeClass()
74+
{
75+
return true === $this->immutable ? \DateTimeImmutable::class : \DateTime::class;
6676
}
6777
}

src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToArrayTransformer.php

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,13 @@ class DateTimeToArrayTransformer extends BaseDateTimeTransformer
3333
* @param string $outputTimezone The output timezone
3434
* @param array $fields The date fields
3535
* @param bool $pad Whether to use padding
36+
* @param bool $immutable Whether to use \DateTimeImmutable instead of \DateTime
3637
*
3738
* @throws UnexpectedTypeException if a timezone is not a string
3839
*/
39-
public function __construct($inputTimezone = null, $outputTimezone = null, array $fields = null, $pad = false)
40+
public function __construct($inputTimezone = null, $outputTimezone = null, array $fields = null, $pad = false, $immutable = false)
4041
{
41-
parent::__construct($inputTimezone, $outputTimezone);
42+
parent::__construct($inputTimezone, $outputTimezone, $immutable);
4243

4344
if (null === $fields) {
4445
$fields = array('year', 'month', 'day', 'hour', 'minute', 'second');
@@ -108,7 +109,7 @@ public function transform($dateTime)
108109
*
109110
* @param array $value Localized date
110111
*
111-
* @return \DateTime Normalized date
112+
* @return \DateTimeInterface Normalized date
112113
*
113114
* @throws TransformationFailedException If the given value is not an array,
114115
* if the value could not be transformed
@@ -170,7 +171,8 @@ public function reverseTransform($value)
170171
}
171172

172173
try {
173-
$dateTime = new \DateTime(sprintf(
174+
$dateTimeClass = $this->getDateTimeClass();
175+
$dateTime = new $dateTimeClass(sprintf(
174176
'%s-%s-%s %s:%s:%s',
175177
empty($value['year']) ? '1970' : $value['year'],
176178
empty($value['month']) ? '1' : $value['month'],

src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,13 @@ class DateTimeToLocalizedStringTransformer extends BaseDateTimeTransformer
3838
* @param int $timeFormat The time format
3939
* @param int $calendar One of the \IntlDateFormatter calendar constants
4040
* @param string $pattern A pattern to pass to \IntlDateFormatter
41+
* @param bool $immutable Whether to use \DateTimeImmutable instead of \DateTime
4142
*
4243
* @throws UnexpectedTypeException If a format is not supported or if a timezone is not a string
4344
*/
44-
public function __construct($inputTimezone = null, $outputTimezone = null, $dateFormat = null, $timeFormat = null, $calendar = \IntlDateFormatter::GREGORIAN, $pattern = null)
45+
public function __construct($inputTimezone = null, $outputTimezone = null, $dateFormat = null, $timeFormat = null, $calendar = \IntlDateFormatter::GREGORIAN, $pattern = null, $immutable = false)
4546
{
46-
parent::__construct($inputTimezone, $outputTimezone);
47+
parent::__construct($inputTimezone, $outputTimezone, $immutable);
4748

4849
if (null === $dateFormat) {
4950
$dateFormat = \IntlDateFormatter::MEDIUM;
@@ -101,7 +102,7 @@ public function transform($dateTime)
101102
*
102103
* @param string|array $value Localized date string/array
103104
*
104-
* @return \DateTime Normalized date
105+
* @return \DateTimeInterface Normalized date
105106
*
106107
* @throws TransformationFailedException if the given value is not a string,
107108
* if the date could not be parsed
@@ -126,14 +127,16 @@ public function reverseTransform($value)
126127
throw new TransformationFailedException(intl_get_error_message());
127128
}
128129

130+
$dateTimeClass = $this->getDateTimeClass();
131+
129132
try {
130133
if ($dateOnly) {
131134
// we only care about year-month-date, which has been delivered as a timestamp pointing to UTC midnight
132-
return new \DateTime(gmdate('Y-m-d', $timestamp), new \DateTimeZone($this->inputTimezone));
135+
return new $dateTimeClass(gmdate('Y-m-d', $timestamp), new \DateTimeZone($this->inputTimezone));
133136
}
134137

135138
// read timestamp into DateTime object - the formatter delivers a timestamp
136-
$dateTime = new \DateTime(sprintf('@%s', $timestamp));
139+
$dateTime = new $dateTimeClass(sprintf('@%s', $timestamp));
137140
// set timezone separately, as it would be ignored if set via the constructor,
138141
// see http://php.net/manual/en/datetime.construct.php
139142
$dateTime->setTimezone(new \DateTimeZone($this->outputTimezone));

src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToRfc3339Transformer.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ public function transform($dateTime)
5353
*
5454
* @param string $rfc3339 Formatted string
5555
*
56-
* @return \DateTime Normalized date
56+
* @return \DateTimeInterface Normalized date
5757
*
5858
* @throws TransformationFailedException If the given value is not a string,
5959
* if the value could not be transformed
@@ -68,8 +68,10 @@ public function reverseTransform($rfc3339)
6868
return;
6969
}
7070

71+
$dateTimeClass = $this->getDateTimeClass();
72+
7173
try {
72-
$dateTime = new \DateTime($rfc3339);
74+
$dateTime = new $dateTimeClass($rfc3339);
7375
} catch (\Exception $e) {
7476
throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e);
7577
}

src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToStringTransformer.php

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,13 @@ class DateTimeToStringTransformer extends BaseDateTimeTransformer
5858
* @param string $outputTimezone The name of the output timezone
5959
* @param string $format The date format
6060
* @param bool $parseUsingPipe Whether to parse by appending a pipe "|" to the parse format
61+
* @param bool $immutable Whether to use \DateTimeImmutable instead of \DateTime
6162
*
6263
* @throws UnexpectedTypeException if a timezone is not a string
6364
*/
64-
public function __construct($inputTimezone = null, $outputTimezone = null, $format = 'Y-m-d H:i:s', $parseUsingPipe = true)
65+
public function __construct($inputTimezone = null, $outputTimezone = null, $format = 'Y-m-d H:i:s', $parseUsingPipe = true, $immutable = false)
6566
{
66-
parent::__construct($inputTimezone, $outputTimezone);
67+
parent::__construct($inputTimezone, $outputTimezone, $immutable);
6768

6869
$this->generateFormat = $this->parseFormat = $format;
6970
$this->parseUsingPipe = $parseUsingPipe || null === $parseUsingPipe;
@@ -115,7 +116,7 @@ public function transform($dateTime)
115116
*
116117
* @param string $value A value as produced by PHP's date() function
117118
*
118-
* @return \DateTime An instance of \DateTime
119+
* @return \DateTimeInterface An instance of \DateTime
119120
*
120121
* @throws TransformationFailedException If the given value is not a string,
121122
* or could not be transformed
@@ -130,10 +131,12 @@ public function reverseTransform($value)
130131
throw new TransformationFailedException('Expected a string.');
131132
}
132133

134+
$dateTimeClass = $this->getDateTimeClass();
135+
133136
$outputTz = new \DateTimeZone($this->outputTimezone);
134-
$dateTime = \DateTime::createFromFormat($this->parseFormat, $value, $outputTz);
137+
$dateTime = $dateTimeClass::createFromFormat($this->parseFormat, $value, $outputTz);
135138

136-
$lastErrors = \DateTime::getLastErrors();
139+
$lastErrors = $dateTimeClass::getLastErrors();
137140

138141
if (0 < $lastErrors['warning_count'] || 0 < $lastErrors['error_count']) {
139142
throw new TransformationFailedException(

src/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ public function buildForm(FormBuilderInterface $builder, array $options)
8787
$timeFormat = self::DEFAULT_TIME_FORMAT;
8888
$calendar = \IntlDateFormatter::GREGORIAN;
8989
$pattern = is_string($options['format']) ? $options['format'] : null;
90+
$immutable = 'datetimeimmutable' === $options['input'];
9091

9192
if (!in_array($dateFormat, self::$acceptedFormats, true)) {
9293
throw new InvalidOptionsException('The "date_format" option must be one of the IntlDateFormatter constants (FULL, LONG, MEDIUM, SHORT) or a string representing a custom format.');
@@ -96,7 +97,8 @@ public function buildForm(FormBuilderInterface $builder, array $options)
9697
if (self::HTML5_FORMAT === $pattern) {
9798
$builder->addViewTransformer(new DateTimeToRfc3339Transformer(
9899
$options['model_timezone'],
99-
$options['view_timezone']
100+
$options['view_timezone'],
101+
$immutable
100102
));
101103
} else {
102104
$builder->addViewTransformer(new DateTimeToLocalizedStringTransformer(
@@ -105,7 +107,8 @@ public function buildForm(FormBuilderInterface $builder, array $options)
105107
$dateFormat,
106108
$timeFormat,
107109
$calendar,
108-
$pattern
110+
$pattern,
111+
$immutable
109112
));
110113
}
111114
} else {
@@ -155,7 +158,7 @@ public function buildForm(FormBuilderInterface $builder, array $options)
155158

156159
$builder
157160
->addViewTransformer(new DataTransformerChain(array(
158-
new DateTimeToArrayTransformer($options['model_timezone'], $options['view_timezone'], $parts),
161+
new DateTimeToArrayTransformer($options['model_timezone'], $options['view_timezone'], $parts, false, $immutable),
159162
new ArrayToPartsTransformer(array(
160163
'date' => $dateParts,
161164
'time' => $timeParts,
@@ -255,6 +258,7 @@ public function configureOptions(OptionsResolver $resolver)
255258

256259
$resolver->setAllowedValues('input', array(
257260
'datetime',
261+
'datetimeimmutable',
258262
'string',
259263
'timestamp',
260264
'array',

src/Symfony/Component/Form/Extension/Core/Type/DateType.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ public function buildForm(FormBuilderInterface $builder, array $options)
5151
$timeFormat = \IntlDateFormatter::NONE;
5252
$calendar = \IntlDateFormatter::GREGORIAN;
5353
$pattern = is_string($options['format']) ? $options['format'] : null;
54+
$immutable = 'datetimeimmutable' === $options['input'];
5455

5556
if (!in_array($dateFormat, self::$acceptedFormats, true)) {
5657
throw new InvalidOptionsException('The "format" option must be one of the IntlDateFormatter constants (FULL, LONG, MEDIUM, SHORT) or a string representing a custom format.');
@@ -67,7 +68,8 @@ public function buildForm(FormBuilderInterface $builder, array $options)
6768
$dateFormat,
6869
$timeFormat,
6970
$calendar,
70-
$pattern
71+
$pattern,
72+
$immutable
7173
));
7274
} else {
7375
$yearOptions = $monthOptions = $dayOptions = array(
@@ -113,7 +115,7 @@ public function buildForm(FormBuilderInterface $builder, array $options)
113115
->add('month', self::$widgets[$options['widget']], $monthOptions)
114116
->add('day', self::$widgets[$options['widget']], $dayOptions)
115117
->addViewTransformer(new DateTimeToArrayTransformer(
116-
$options['model_timezone'], $options['view_timezone'], array('year', 'month', 'day')
118+
$options['model_timezone'], $options['view_timezone'], array('year', 'month', 'day'), false, $immutable
117119
))
118120
->setAttribute('formatter', $formatter)
119121
;
@@ -254,6 +256,7 @@ public function configureOptions(OptionsResolver $resolver)
254256

255257
$resolver->setAllowedValues('input', array(
256258
'datetime',
259+
'datetimeimmutable',
257260
'string',
258261
'timestamp',
259262
'array',

src/Symfony/Component/Form/Extension/Core/Type/TimeType.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,10 @@ public function buildForm(FormBuilderInterface $builder, array $options)
5252
$parts[] = 'second';
5353
}
5454

55+
$immutable = 'datetimeimmutable' === $options['input'];
56+
5557
if ('single_text' === $options['widget']) {
56-
$builder->addViewTransformer(new DateTimeToStringTransformer($options['model_timezone'], $options['view_timezone'], $format));
58+
$builder->addViewTransformer(new DateTimeToStringTransformer($options['model_timezone'], $options['view_timezone'], $format, true, $immutable));
5759
} else {
5860
$hourOptions = $minuteOptions = $secondOptions = array(
5961
'error_bubbling' => true,
@@ -117,7 +119,7 @@ public function buildForm(FormBuilderInterface $builder, array $options)
117119
$builder->add('second', self::$widgets[$options['widget']], $secondOptions);
118120
}
119121

120-
$builder->addViewTransformer(new DateTimeToArrayTransformer($options['model_timezone'], $options['view_timezone'], $parts, 'text' === $options['widget']));
122+
$builder->addViewTransformer(new DateTimeToArrayTransformer($options['model_timezone'], $options['view_timezone'], $parts, 'text' === $options['widget'], $immutable));
121123
}
122124

123125
if ('string' === $options['input']) {
@@ -239,6 +241,7 @@ public function configureOptions(OptionsResolver $resolver)
239241

240242
$resolver->setAllowedValues('input', array(
241243
'datetime',
244+
'datetimeimmutable',
242245
'string',
243246
'timestamp',
244247
'array',

src/Symfony/Component/Form/Test/TypeTestCase.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ protected function setUp()
3434
$this->builder = new FormBuilder(null, null, $this->dispatcher, $this->factory);
3535
}
3636

37-
public static function assertDateTimeEquals(\DateTime $expected, \DateTime $actual)
37+
public static function assertDateTimeEquals(\DateTimeInterface $expected, \DateTimeInterface $actual)
3838
{
3939
self::assertEquals($expected->format('c'), $actual->format('c'));
4040
}

0 commit comments

Comments
 (0)
0