Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit d2929e7

Browse files
committed
Resolve DateTime value using the clock
1 parent c79d4ab commit d2929e7

File tree

5 files changed

+83
-23
lines changed

5 files changed

+83
-23
lines changed

src/Symfony/Bundle/FrameworkBundle/Resources/config/web.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@
5555
->tag('controller.argument_value_resolver', ['priority' => 100])
5656

5757
->set('argument_resolver.datetime', DateTimeValueResolver::class)
58+
->args([
59+
service('clock')->nullOnInvalid(),
60+
])
5861
->tag('controller.argument_value_resolver', ['priority' => 100])
5962

6063
->set('argument_resolver.request_attribute', RequestAttributeValueResolver::class)

src/Symfony/Component/HttpKernel/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
CHANGELOG
22
=========
33

4+
6.3
5+
---
6+
7+
* Update `DateTimeValueResolver` to accept an instance of `ClockInterface` for the current date
8+
49
6.2
510
---
611

src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/DateTimeValueResolver.php

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Component\HttpKernel\Controller\ArgumentResolver;
1313

14+
use Symfony\Component\Clock\ClockInterface;
1415
use Symfony\Component\HttpFoundation\Request;
1516
use Symfony\Component\HttpKernel\Attribute\MapDateTime;
1617
use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface;
@@ -26,6 +27,11 @@
2627
*/
2728
final class DateTimeValueResolver implements ArgumentValueResolverInterface, ValueResolverInterface
2829
{
30+
public function __construct(
31+
private readonly ?ClockInterface $clock = null,
32+
) {
33+
}
34+
2935
/**
3036
* @deprecated since Symfony 6.2, use resolve() instead
3137
*/
@@ -43,16 +49,22 @@ public function resolve(Request $request, ArgumentMetadata $argument): array
4349
}
4450

4551
$value = $request->attributes->get($argument->getName());
52+
$class = \DateTimeInterface::class === $argument->getType() ? \DateTimeImmutable::class : $argument->getType();
4653

47-
if ($value instanceof \DateTimeInterface) {
48-
return [$value];
54+
if (!$value) {
55+
if ($argument->isNullable()) {
56+
return [null];
57+
}
58+
if (!$this->clock) {
59+
return [new $class];
60+
}
61+
$value = $this->clock->withTimeZone(date_default_timezone_get())->now();
4962
}
5063

51-
if ($argument->isNullable() && !$value) {
52-
return [null];
64+
if ($value instanceof \DateTimeInterface) {
65+
return $value instanceof $class ? [$value] : [$class::createFromInterface($value)];
5366
}
5467

55-
$class = \DateTimeInterface::class === $argument->getType() ? \DateTimeImmutable::class : $argument->getType();
5668
$format = null;
5769

5870
if ($attributes = $argument->getAttributes(MapDateTime::class, ArgumentMetadata::IS_INSTANCEOF)) {

src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/DateTimeValueResolverTest.php

Lines changed: 57 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\HttpKernel\Tests\Controller\ArgumentResolver;
1313

1414
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\Clock\MockClock;
1516
use Symfony\Component\HttpFoundation\Request;
1617
use Symfony\Component\HttpKernel\Attribute\MapDateTime;
1718
use Symfony\Component\HttpKernel\Controller\ArgumentResolver\DateTimeValueResolver;
@@ -34,9 +35,12 @@ protected function tearDown(): void
3435

3536
public function getTimeZones()
3637
{
37-
yield ['UTC'];
38-
yield ['Etc/GMT+9'];
39-
yield ['Etc/GMT-14'];
38+
yield ['UTC', false];
39+
yield ['Etc/GMT+9', false];
40+
yield ['Etc/GMT-14', false];
41+
yield ['UTC', true];
42+
yield ['Etc/GMT+9', true];
43+
yield ['Etc/GMT-14', true];
4044
}
4145

4246
/**
@@ -71,10 +75,10 @@ public function testUnsupportedArgument()
7175
/**
7276
* @dataProvider getTimeZones
7377
*/
74-
public function testFullDate(string $timezone)
78+
public function testFullDate(string $timezone, bool $withClock)
7579
{
7680
date_default_timezone_set($timezone);
77-
$resolver = new DateTimeValueResolver();
81+
$resolver = new DateTimeValueResolver($withClock ? new MockClock() : null);
7882

7983
$argument = new ArgumentMetadata('dummy', \DateTimeImmutable::class, false, false, null);
8084
$request = self::requestWithAttributes(['dummy' => '2012-07-21 00:00:00']);
@@ -90,10 +94,10 @@ public function testFullDate(string $timezone)
9094
/**
9195
* @dataProvider getTimeZones
9296
*/
93-
public function testUnixTimestamp(string $timezone)
97+
public function testUnixTimestamp(string $timezone, bool $withClock)
9498
{
9599
date_default_timezone_set($timezone);
96-
$resolver = new DateTimeValueResolver();
100+
$resolver = new DateTimeValueResolver($withClock ? new MockClock('now', $timezone) : null);
97101

98102
$argument = new ArgumentMetadata('dummy', \DateTimeImmutable::class, false, false, null);
99103
$request = self::requestWithAttributes(['dummy' => '989541720']);
@@ -120,34 +124,69 @@ public function testNullableWithEmptyAttribute()
120124
}
121125

122126
/**
123-
* @dataProvider getTimeZones
127+
* @param class-string<\DateTimeInterface> $class
128+
* @dataProvider getClasses
124129
*/
125-
public function testNow(string $timezone)
130+
public function testNow(string $class)
126131
{
127-
date_default_timezone_set($timezone);
132+
date_default_timezone_set($timezone = 'Etc/GMT+9');
128133
$resolver = new DateTimeValueResolver();
129134

130-
$argument = new ArgumentMetadata('dummy', \DateTime::class, false, false, null, false);
135+
$argument = new ArgumentMetadata('dummy', $class, false, false, null, false);
131136
$request = self::requestWithAttributes(['dummy' => null]);
132137

133138
$results = $resolver->resolve($request, $argument);
134139

135140
$this->assertCount(1, $results);
141+
$this->assertInstanceOf($class, $results[0]);
142+
$this->assertSame($timezone, $results[0]->getTimezone()->getName(), 'Default timezone');
136143
$this->assertEquals('0', $results[0]->diff(new \DateTimeImmutable())->format('%s'));
144+
}
145+
146+
/**
147+
* @param class-string<\DateTimeInterface> $class
148+
* @dataProvider getClasses
149+
*/
150+
public function testNowWithClock(string $class)
151+
{
152+
date_default_timezone_set($timezone = 'Etc/GMT+9');
153+
$clock = new MockClock('2022-02-20 22:20:02');
154+
$resolver = new DateTimeValueResolver($clock);
155+
156+
$argument = new ArgumentMetadata('dummy', $class, false, false, null, false);
157+
$request = self::requestWithAttributes(['dummy' => null]);
158+
159+
$results = $resolver->resolve($request, $argument);
160+
161+
$this->assertCount(1, $results);
162+
$this->assertInstanceOf($class, $results[0]);
137163
$this->assertSame($timezone, $results[0]->getTimezone()->getName(), 'Default timezone');
164+
$this->assertEquals($clock->now(), $results[0]);
165+
}
166+
167+
public static function getClasses()
168+
{
169+
yield [\DateTimeInterface::class];
170+
yield [\DateTime::class];
171+
yield [FooDateTime::class];
138172
}
139173

140-
public function testPreviouslyConvertedAttribute()
174+
/**
175+
* @param class-string<\DateTimeInterface> $class
176+
* @dataProvider getClasses
177+
*/
178+
public function testPreviouslyConvertedAttribute(string $class)
141179
{
142180
$resolver = new DateTimeValueResolver();
143181

144-
$argument = new ArgumentMetadata('dummy', \DateTimeImmutable::class, false, false, null, true);
182+
$argument = new ArgumentMetadata('dummy', $class, false, false, null, true);
145183
$request = self::requestWithAttributes(['dummy' => $datetime = new \DateTimeImmutable()]);
146184

147185
$results = $resolver->resolve($request, $argument);
148186

149187
$this->assertCount(1, $results);
150-
$this->assertSame($datetime, $results[0]);
188+
$this->assertEquals($datetime, $results[0], 'The value is the same, but the class can be modified.');
189+
$this->assertInstanceOf($class, $results[0]);
151190
}
152191

153192
public function testCustomClass()
@@ -168,10 +207,10 @@ public function testCustomClass()
168207
/**
169208
* @dataProvider getTimeZones
170209
*/
171-
public function testDateTimeImmutable(string $timezone)
210+
public function testDateTimeImmutable(string $timezone, bool $withClock)
172211
{
173212
date_default_timezone_set($timezone);
174-
$resolver = new DateTimeValueResolver();
213+
$resolver = new DateTimeValueResolver($withClock ? new MockClock('now', $timezone) : null);
175214

176215
$argument = new ArgumentMetadata('dummy', \DateTimeImmutable::class, false, false, null);
177216
$request = self::requestWithAttributes(['dummy' => '2016-09-08 00:00:00 +05:00']);
@@ -187,10 +226,10 @@ public function testDateTimeImmutable(string $timezone)
187226
/**
188227
* @dataProvider getTimeZones
189228
*/
190-
public function testWithFormat(string $timezone)
229+
public function testWithFormat(string $timezone, bool $withClock)
191230
{
192231
date_default_timezone_set($timezone);
193-
$resolver = new DateTimeValueResolver();
232+
$resolver = new DateTimeValueResolver($withClock ? new MockClock('now', $timezone) : null);
194233

195234
$argument = new ArgumentMetadata('dummy', \DateTimeInterface::class, false, false, null, false, [
196235
MapDateTime::class => new MapDateTime('m-d-y H:i:s'),

src/Symfony/Component/HttpKernel/composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
},
2727
"require-dev": {
2828
"symfony/browser-kit": "^5.4|^6.0",
29+
"symfony/clock": "^6.2",
2930
"symfony/config": "^6.1",
3031
"symfony/console": "^5.4|^6.0",
3132
"symfony/css-selector": "^5.4|^6.0",

0 commit comments

Comments
 (0)