MockClock.php 3.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\Clock;
  11. /**
  12. * A clock that always returns the same date, suitable for testing time-sensitive logic.
  13. *
  14. * Consider using ClockSensitiveTrait in your test cases instead of using this class directly.
  15. *
  16. * @author Nicolas Grekas <p@tchwork.com>
  17. */
  18. final class MockClock implements ClockInterface
  19. {
  20. private DatePoint $now;
  21. /**
  22. * @throws \DateMalformedStringException When $now is invalid
  23. * @throws \DateInvalidTimeZoneException When $timezone is invalid
  24. */
  25. public function __construct(\DateTimeImmutable|string $now = 'now', \DateTimeZone|string|null $timezone = null)
  26. {
  27. if (\PHP_VERSION_ID >= 80300 && \is_string($timezone)) {
  28. $timezone = new \DateTimeZone($timezone);
  29. } elseif (\is_string($timezone)) {
  30. try {
  31. $timezone = new \DateTimeZone($timezone);
  32. } catch (\Exception $e) {
  33. throw new \DateInvalidTimeZoneException($e->getMessage(), $e->getCode(), $e);
  34. }
  35. }
  36. if (\is_string($now)) {
  37. $now = new DatePoint($now, $timezone ?? new \DateTimeZone('UTC'));
  38. } elseif (!$now instanceof DatePoint) {
  39. $now = DatePoint::createFromInterface($now);
  40. }
  41. $this->now = null !== $timezone ? $now->setTimezone($timezone) : $now;
  42. }
  43. public function now(): DatePoint
  44. {
  45. return clone $this->now;
  46. }
  47. public function sleep(float|int $seconds): void
  48. {
  49. $now = (float) $this->now->format('Uu') + $seconds * 1e6;
  50. $now = substr_replace(\sprintf('@%07.0F', $now), '.', -6, 0);
  51. $timezone = $this->now->getTimezone();
  52. $this->now = DatePoint::createFromInterface(new \DateTimeImmutable($now, $timezone))->setTimezone($timezone);
  53. }
  54. /**
  55. * @throws \DateMalformedStringException When $modifier is invalid
  56. */
  57. public function modify(string $modifier): void
  58. {
  59. if (\PHP_VERSION_ID < 80300) {
  60. $this->now = @$this->now->modify($modifier) ?: throw new \DateMalformedStringException(error_get_last()['message'] ?? \sprintf('Invalid modifier: "%s". Could not modify MockClock.', $modifier));
  61. return;
  62. }
  63. $this->now = $this->now->modify($modifier);
  64. }
  65. /**
  66. * @throws \DateInvalidTimeZoneException When the timezone name is invalid
  67. */
  68. public function withTimeZone(\DateTimeZone|string $timezone): static
  69. {
  70. if (\PHP_VERSION_ID >= 80300 && \is_string($timezone)) {
  71. $timezone = new \DateTimeZone($timezone);
  72. } elseif (\is_string($timezone)) {
  73. try {
  74. $timezone = new \DateTimeZone($timezone);
  75. } catch (\Exception $e) {
  76. throw new \DateInvalidTimeZoneException($e->getMessage(), $e->getCode(), $e);
  77. }
  78. }
  79. $clone = clone $this;
  80. $clone->now = $clone->now->setTimezone($timezone);
  81. return $clone;
  82. }
  83. }