Skip to content

Commit 95dd27e

Browse files
committed
feature #46715 [Clock] A new component to decouple applications from the system clock (nicolas-grekas)
This PR was squashed before being merged into the 6.2 branch. Discussion ---------- [Clock] A new component to decouple applications from the system clock | Q | A | ------------- | --- | Branch? | 6.2 | Bug fix? | no | New feature? | yes | Deprecations? | no | Tickets | - | License | MIT | Doc PR | - After watching @afilina's talk at SymfonyWorld Online last week + listening to her using a "clock" service to improve testability of time-sensitive logics, I decided to propose this new "Clock" component. This also relates to the ongoing efforts to standardize a `ClockInterface` in [PSR-20](https://github.com/php-fig/clock). This PR provides a `ClockInterface`, with 3 methods: - `now(): \DateTimeImmutable` is designed to be compatible with the envisioned PSR-20; - `sleep(float|int $seconds): void` advances the clock by the provided number of seconds; - `withTimeZone(): static` changes the time zone returned by `now()`. The `sleep()` methods takes inspiration from `ClockMock` in the PhpUnitBridge, where this proved useful to improve testability. Ideally, we could use this component everywhere measuring the current time is needed and stop relying on `ClockMock`. This PR provides 3 clock implementations: - `NativeClock` which relies on the system clock; - `MockClock` which allows mocking the time; - `MonotonicClock` which relies on `hrtime()` to provide a monotonic clock. If this gets accepted, I'll follow up with a PR to add clock services to FrameworkBundle and we'll then be able to see where we could use such clock services in other components. I hope PSR-20 will be stabilized by Symfony 6.2, but this is not required for this PR to be useful. Commits ------- 9db08e40d4 [Clock] A new component to decouple applications from the system clock
2 parents 5f84c95 + 4ba7d87 commit 95dd27e

File tree

2 files changed

+11
-3
lines changed

2 files changed

+11
-3
lines changed

DependencyInjection/FrameworkExtension.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
use PHPStan\PhpDocParser\Parser\PhpDocParser;
2121
use Psr\Cache\CacheItemPoolInterface;
2222
use Psr\Container\ContainerInterface as PsrContainerInterface;
23-
use Psr\EventDispatcher\EventDispatcherInterface as PsrEventDispatcherInterface;
2423
use Psr\Http\Client\ClientInterface;
2524
use Psr\Log\LoggerAwareInterface;
2625
use Symfony\Bridge\Monolog\Processor\DebugProcessor;
@@ -40,6 +39,7 @@
4039
use Symfony\Component\Cache\Marshaller\DefaultMarshaller;
4140
use Symfony\Component\Cache\Marshaller\MarshallerInterface;
4241
use Symfony\Component\Cache\ResettableInterface;
42+
use Symfony\Component\Clock\ClockInterface;
4343
use Symfony\Component\Config\Definition\ConfigurationInterface;
4444
use Symfony\Component\Config\FileLocator;
4545
use Symfony\Component\Config\Loader\LoaderInterface;
@@ -277,8 +277,9 @@ public function load(array $configs, ContainerBuilder $container)
277277
$loader->load('fragment_renderer.php');
278278
$loader->load('error_renderer.php');
279279

280-
if (ContainerBuilder::willBeAvailable('psr/event-dispatcher', PsrEventDispatcherInterface::class, ['symfony/framework-bundle'])) {
281-
$container->setAlias(PsrEventDispatcherInterface::class, 'event_dispatcher');
280+
if (!ContainerBuilder::willBeAvailable('symfony/clock', ClockInterface::class, ['symfony/framework-bundle'])) {
281+
$container->removeDefinition('clock');
282+
$container->removeAlias(ClockInterface::class);
282283
}
283284

284285
$container->registerAliasForArgument('parameter_bag', PsrContainerInterface::class);

Resources/config/services.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,11 @@
1111

1212
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
1313

14+
use Psr\EventDispatcher\EventDispatcherInterface as PsrEventDispatcherInterface;
1415
use Symfony\Bundle\FrameworkBundle\CacheWarmer\ConfigBuilderCacheWarmer;
1516
use Symfony\Bundle\FrameworkBundle\HttpCache\HttpCache;
17+
use Symfony\Component\Clock\ClockInterface;
18+
use Symfony\Component\Clock\NativeClock;
1619
use Symfony\Component\Config\Loader\LoaderInterface;
1720
use Symfony\Component\Config\Resource\SelfCheckingResourceChecker;
1821
use Symfony\Component\Config\ResourceCheckerConfigCacheFactory;
@@ -77,6 +80,7 @@ class_exists(WorkflowEvents::class) ? WorkflowEvents::ALIASES : []
7780
->tag('event_dispatcher.dispatcher', ['name' => 'event_dispatcher'])
7881
->alias(EventDispatcherInterfaceComponentAlias::class, 'event_dispatcher')
7982
->alias(EventDispatcherInterface::class, 'event_dispatcher')
83+
->alias(PsrEventDispatcherInterface::class, 'event_dispatcher')
8084

8185
->set('http_kernel', HttpKernel::class)
8286
->public()
@@ -224,6 +228,9 @@ class_exists(WorkflowEvents::class) ? WorkflowEvents::ALIASES : []
224228
->args([service(KernelInterface::class), service('logger')->nullOnInvalid()])
225229
->tag('kernel.cache_warmer')
226230

231+
->set('clock', NativeClock::class)
232+
->alias(ClockInterface::class, 'clock')
233+
227234
// register as abstract and excluded, aka not-autowirable types
228235
->set(LoaderInterface::class)->abstract()->tag('container.excluded')
229236
->set(Request::class)->abstract()->tag('container.excluded')

0 commit comments

Comments
 (0)