Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit dfcb811

Browse filesBrowse files
feature #48098 [HttpKernel]  Resolve DateTime value using the Clock (GromNaN)
This PR was merged into the 6.3 branch. Discussion ---------- [HttpKernel]  Resolve DateTime value using the Clock | Q | A | ------------- | --- | Branch? | 6.3 | Bug fix? | no | New feature? | yes | Deprecations? | no | Tickets | - | License | MIT | Doc PR | TODO In order to mock the current time in functional test cases by injecting a `Symfony\Component\Clock\MockClock` as `clock` service. This is necessary when a `DateTimeInterface` argument is not nullable and we expect the current date time by default. Example when 2 routes are configured on the same controller with an optional parameter. ```php class TvGridController { #[Route('/', name: 'prime_now')] #[Route('/{date}', name: 'prime_date'] public function prime(\DateTimeInterface $date) { return new Response('Grid for date: '.$date->format('Y-m-d')); } } ``` Commits ------- 4917528 Resolve DateTime value using the clock
2 parents 85590ec + 4917528 commit dfcb811
Copy full SHA for dfcb811

File tree

5 files changed

+67
-21
lines changed
Filter options

5 files changed

+67
-21
lines changed

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

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/Resources/config/web.php
+3Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Component/HttpKernel/CHANGELOG.md
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ CHANGELOG
77
* Deprecate parameters `container.dumper.inline_factories` and `container.dumper.inline_class_loader`, use `.container.dumper.inline_factories` and `.container.dumper.inline_class_loader` instead
88
* `FileProfilerStorage` removes profiles automatically after two days
99
* Add `#[HttpStatus]` for defining status codes for exceptions
10+
* Use an instance of `Psr\Clock\ClockInterface` to generate the current date time in `DateTimeValueResolver`
1011

1112
6.2
1213
---

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/DateTimeValueResolver.php
+17-5Lines 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 Psr\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
*/
@@ -45,12 +51,18 @@ public function resolve(Request $request, ArgumentMetadata $argument): array
4551
$value = $request->attributes->get($argument->getName());
4652
$class = \DateTimeInterface::class === $argument->getType() ? \DateTimeImmutable::class : $argument->getType();
4753

48-
if ($value instanceof \DateTimeInterface) {
49-
return [$value instanceof $class ? $value : $class::createFromInterface($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->now();
5062
}
5163

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

5668
$format = null;
@@ -71,7 +83,7 @@ public function resolve(Request $request, ArgumentMetadata $argument): array
7183
$value = '@'.$value;
7284
}
7385
try {
74-
$date = new $class($value ?? 'now');
86+
$date = new $class($value);
7587
} catch (\Exception) {
7688
$date = false;
7789
}

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/DateTimeValueResolverTest.php
+45-16Lines changed: 45 additions & 16 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 static 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
public static function getClasses()
@@ -78,10 +82,10 @@ public function testUnsupportedArgument()
7882
/**
7983
* @dataProvider getTimeZones
8084
*/
81-
public function testFullDate(string $timezone)
85+
public function testFullDate(string $timezone, bool $withClock)
8286
{
8387
date_default_timezone_set($timezone);
84-
$resolver = new DateTimeValueResolver();
88+
$resolver = new DateTimeValueResolver($withClock ? new MockClock() : null);
8589

8690
$argument = new ArgumentMetadata('dummy', \DateTimeImmutable::class, false, false, null);
8791
$request = self::requestWithAttributes(['dummy' => '2012-07-21 00:00:00']);
@@ -97,10 +101,10 @@ public function testFullDate(string $timezone)
97101
/**
98102
* @dataProvider getTimeZones
99103
*/
100-
public function testUnixTimestamp(string $timezone)
104+
public function testUnixTimestamp(string $timezone, bool $withClock)
101105
{
102106
date_default_timezone_set($timezone);
103-
$resolver = new DateTimeValueResolver();
107+
$resolver = new DateTimeValueResolver($withClock ? new MockClock('now', $timezone) : null);
104108

105109
$argument = new ArgumentMetadata('dummy', \DateTimeImmutable::class, false, false, null);
106110
$request = self::requestWithAttributes(['dummy' => '989541720']);
@@ -127,21 +131,46 @@ public function testNullableWithEmptyAttribute()
127131
}
128132

129133
/**
130-
* @dataProvider getTimeZones
134+
* @param class-string<\DateTimeInterface> $class
135+
*
136+
* @dataProvider getClasses
131137
*/
132-
public function testNow(string $timezone)
138+
public function testNow(string $class)
133139
{
134-
date_default_timezone_set($timezone);
140+
date_default_timezone_set($timezone = 'Etc/GMT+9');
135141
$resolver = new DateTimeValueResolver();
136142

137-
$argument = new ArgumentMetadata('dummy', \DateTime::class, false, false, null, false);
143+
$argument = new ArgumentMetadata('dummy', $class, false, false, null, false);
138144
$request = self::requestWithAttributes(['dummy' => null]);
139145

140146
$results = $resolver->resolve($request, $argument);
141147

142148
$this->assertCount(1, $results);
143-
$this->assertEquals('0', $results[0]->diff(new \DateTimeImmutable())->format('%s'));
149+
$this->assertInstanceOf($class, $results[0]);
144150
$this->assertSame($timezone, $results[0]->getTimezone()->getName(), 'Default timezone');
151+
$this->assertEquals('0', $results[0]->diff(new \DateTimeImmutable())->format('%s'));
152+
}
153+
154+
/**
155+
* @param class-string<\DateTimeInterface> $class
156+
*
157+
* @dataProvider getClasses
158+
*/
159+
public function testNowWithClock(string $class)
160+
{
161+
date_default_timezone_set('Etc/GMT+9');
162+
$clock = new MockClock('2022-02-20 22:20:02');
163+
$resolver = new DateTimeValueResolver($clock);
164+
165+
$argument = new ArgumentMetadata('dummy', $class, false, false, null, false);
166+
$request = self::requestWithAttributes(['dummy' => null]);
167+
168+
$results = $resolver->resolve($request, $argument);
169+
170+
$this->assertCount(1, $results);
171+
$this->assertInstanceOf($class, $results[0]);
172+
$this->assertSame('UTC', $results[0]->getTimezone()->getName(), 'Default timezone');
173+
$this->assertEquals($clock->now(), $results[0]);
145174
}
146175

147176
/**
@@ -181,10 +210,10 @@ public function testCustomClass()
181210
/**
182211
* @dataProvider getTimeZones
183212
*/
184-
public function testDateTimeImmutable(string $timezone)
213+
public function testDateTimeImmutable(string $timezone, bool $withClock)
185214
{
186215
date_default_timezone_set($timezone);
187-
$resolver = new DateTimeValueResolver();
216+
$resolver = new DateTimeValueResolver($withClock ? new MockClock('now', $timezone) : null);
188217

189218
$argument = new ArgumentMetadata('dummy', \DateTimeImmutable::class, false, false, null);
190219
$request = self::requestWithAttributes(['dummy' => '2016-09-08 00:00:00 +05:00']);
@@ -200,10 +229,10 @@ public function testDateTimeImmutable(string $timezone)
200229
/**
201230
* @dataProvider getTimeZones
202231
*/
203-
public function testWithFormat(string $timezone)
232+
public function testWithFormat(string $timezone, bool $withClock)
204233
{
205234
date_default_timezone_set($timezone);
206-
$resolver = new DateTimeValueResolver();
235+
$resolver = new DateTimeValueResolver($withClock ? new MockClock('now', $timezone) : null);
207236

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

‎src/Symfony/Component/HttpKernel/composer.json

Copy file name to clipboardExpand all lines: src/Symfony/Component/HttpKernel/composer.json
+1Lines 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 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.