diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 6620380..c32092f 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -83,6 +83,10 @@ jobs: - name: "Composer" uses: "orisai/github-workflows/.github/actions/setup-composer@v1" + - name: "Extra dependencies" + if: "!startsWith(${{ matrix.php-version }}, '7') && !startsWith(${{ matrix.php-version }}, '8.0')" + run: "composer require symfony/clock" + - name: "PHPStan" uses: "orisai/github-workflows/.github/actions/phpstan@v1" with: @@ -130,6 +134,10 @@ jobs: with: command: "composer update --no-interaction --no-progress --prefer-dist ${{ matrix.composer-flags }}" + - name: "Extra dependencies" + if: "!startsWith(${{ matrix.php-version }}, '7') && !startsWith(${{ matrix.php-version }}, '8.0')" + run: "composer require symfony/clock" + - name: "PHPUnit" uses: "orisai/github-workflows/.github/actions/phpunit@v1" with: @@ -198,6 +206,10 @@ jobs: - name: "Composer" uses: "orisai/github-workflows/.github/actions/setup-composer@v1" + - name: "Extra dependencies" + if: "!startsWith(${{ matrix.php-version }}, '7') && !startsWith(${{ matrix.php-version }}, '8.0')" + run: "composer require symfony/clock" + - name: "PHPUnit" uses: "orisai/github-workflows/.github/actions/phpunit@v1" with: diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f5b733..6c3c2e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - `Clock` interface has a `sleep()` method - `PsrToOrisaiClockAdapter` - makes any PSR clock compatible with `Orisai\Clock\Clock` +- `SymfonyToOrisaiClockAdapter` - makes any symfony/clock compatible with `Orisai\Clock\Clock` ### Changed diff --git a/docs/README.md b/docs/README.md index 0b8659b..e82d52e 100644 --- a/docs/README.md +++ b/docs/README.md @@ -15,6 +15,7 @@ Provides current time for runtime and controllable time for testing - [Frozen clock](#frozen-clock) - [Measurement clock](#measurement-clock) - [PSR to Orisai clock adapter](#psr-to-orisai-clock-adapter) + - [Symfony to Orisai clock adapter](#symfony-to-orisai-clock-adapter) - [Integrations and extensions](#integrations-and-extensions) ## Setup @@ -159,7 +160,16 @@ use Orisai\Clock\Adapter\PsrToOrisaiClockAdapter; $clock = new PsrToOrisaiClockAdapter(new ExamplePsrClock()); ``` -[Sleep](#sleep) uses standard `sleep()` function for non-Orisai clock implementation. +### Symfony to Orisai clock adapter + +Decorate any `Symfony\Component\Clock\ClockInterface` implementation to conform interface `Orisai\Clock\Clock`. + +```php +use Orisai\Clock\Adapter\SymfonyToOrisaiClockAdapter; +use Symfony\Component\Clock\NativeClock; + +$clock = new SymfonyToOrisaiClockAdapter(new NativeClock()); +``` ## Integrations and extensions diff --git a/src/Adapter/SymfonyToOrisaiClockAdapter.php b/src/Adapter/SymfonyToOrisaiClockAdapter.php new file mode 100644 index 0000000..24d492f --- /dev/null +++ b/src/Adapter/SymfonyToOrisaiClockAdapter.php @@ -0,0 +1,46 @@ +clock = $clock; + } + + public function now(): DateTimeImmutable + { + return $this->clock->now(); + } + + /** + * @infection-ignore-all + */ + public function sleep(int $seconds = 0, int $milliseconds = 0, int $microseconds = 0): void + { + if (func_get_args() === []) { + trigger_error( + 'Arguments must be passed to method sleep(), otherwise it does not do anything.', + E_USER_WARNING, + ); + } + + $this->clock->sleep( + $seconds + + ($milliseconds / 1_000) + + ($microseconds / 1_000_000), + ); + } + +} diff --git a/tests/Unit/SymfonyToOrisaiClockAdapterTest.php b/tests/Unit/SymfonyToOrisaiClockAdapterTest.php new file mode 100644 index 0000000..dfdac15 --- /dev/null +++ b/tests/Unit/SymfonyToOrisaiClockAdapterTest.php @@ -0,0 +1,79 @@ +now(); + + usleep(1); + $stop = microtime(true); + + self::assertGreaterThan($start, (float) $now->format('U.u')); + self::assertLessThan($stop, (float) $now->format('U.u')); + } + + public function testSleep(): void + { + $clock = new SymfonyToOrisaiClockAdapter( + new MockClock(DateTimeImmutable::createFromFormat('U', '10')), + ); + + self::assertSame( + '10.000000', + $clock->now()->format('U.u'), + ); + + $clock->sleep(1, 2, 3); + self::assertSame( + '11.002003', + $clock->now()->format('U.u'), + ); + } + + public function testSleepNoArgs(): void + { + $clock = new SymfonyToOrisaiClockAdapter( + new MockClock(DateTimeImmutable::createFromFormat('U', '0')), + ); + + @$clock->sleep(); + + self::assertSame( + 'Arguments must be passed to method sleep(), otherwise it does not do anything.', + error_get_last()['message'] ?? null, + ); + self::assertSame( + '0.000000', + $clock->now()->format('U.u'), + ); + } + +} diff --git a/tools/phpstan.baseline.neon b/tools/phpstan.baseline.neon index 1d47c3b..3621384 100644 --- a/tools/phpstan.baseline.neon +++ b/tools/phpstan.baseline.neon @@ -27,3 +27,8 @@ parameters: """ count: 9 path: ../tests/Unit/FrozenClockTest.php + + - + message: "#^Parameter \\#1 \\$now of class Symfony\\\\Component\\\\Clock\\\\MockClock constructor expects DateTimeImmutable\\|string, DateTimeImmutable\\|false given\\.$#" + count: 2 + path: ../tests/Unit/SymfonyToOrisaiClockAdapterTest.php