mirror of
https://github.com/beste/clock.git
synced 2026-10-17 19:27:35 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0754922206 | ||
|
|
df585ab815 | ||
|
|
c094cf87e3 | ||
|
|
c2243a5934 | ||
|
|
e8f20c112e | ||
|
|
7db7032723 | ||
|
|
8f5274748c | ||
|
|
e73aacc19c | ||
|
|
c062aff727 | ||
|
|
b4408036c1 | ||
|
|
0a55b7a019 | ||
|
|
9818902960 | ||
|
|
622d86328b | ||
|
|
bebff7fd27 | ||
|
|
2c5bf134e4 | ||
|
|
8f0a5a5f6c | ||
|
|
e7b50902b0 | ||
|
|
264e712f65 | ||
|
|
ebe8de302d | ||
|
|
4bd164d047 | ||
|
|
5ae0cc3a8d | ||
|
|
0611b8339d | ||
|
|
729cb30697 | ||
|
|
dc84fbe85a | ||
|
|
93c6f6dca7 | ||
|
|
e4b5b09932 | ||
|
|
9c852bc985 | ||
|
|
48c449b37e | ||
|
|
ff31026046 | ||
|
|
d623da43da | ||
|
|
5a0bf8fdba | ||
|
|
88ddce2cd7 | ||
|
|
26ac2fab9a | ||
|
|
0b23a6d481 | ||
|
|
2e9fb8d220 | ||
|
|
1278a71f80 | ||
|
|
17e1c4ce7e | ||
|
|
0d80994228 | ||
|
|
1a2fab532b | ||
|
|
84e729ce8c | ||
|
|
06686333c0 | ||
|
|
621355dffd | ||
|
|
88afd40a02 | ||
|
|
ec0d9b2006 | ||
|
|
c3758513b5 | ||
|
|
6fcddef411 | ||
|
|
b69661437f | ||
|
|
fc7cb0d32c | ||
|
|
05159b138f | ||
|
|
d29ed0d755 | ||
|
|
52bafeb8e3 | ||
|
|
e9ceb17d59 | ||
|
|
3861182e4f | ||
|
|
6d38f044b3 | ||
|
|
aac77907e3 | ||
|
|
ed1c70ffdf | ||
|
|
8ec664cfae | ||
|
|
5af4eaf534 | ||
|
|
4b68b50507 | ||
|
|
0999d2836d | ||
|
|
95e0cb0ec2 | ||
|
|
862f942124 | ||
|
|
fdf9c1bd04 | ||
|
|
1f72d04e67 |
@@ -6,4 +6,3 @@
|
||||
/.gitignore export-ignore
|
||||
/phpstan.neon.dist export-ignore
|
||||
/phpunit.xml.dist export-ignore
|
||||
/psalm.xml.dist export-ignore
|
||||
|
||||
@@ -12,4 +12,4 @@ updates:
|
||||
prefix: "github-actions"
|
||||
open-pull-requests-limit: 10
|
||||
labels:
|
||||
- "dependency"
|
||||
- "dependencies"
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
name: "Dependabot auto-merge"
|
||||
|
||||
on: [pull_request_target] # yamllint disable-line rule:truthy
|
||||
|
||||
permissions:
|
||||
contents: "write"
|
||||
|
||||
jobs:
|
||||
dependabot:
|
||||
runs-on: "ubuntu-latest"
|
||||
if: "${{ github.actor == 'dependabot[bot]' }}"
|
||||
steps:
|
||||
- name: "Dependabot metadata"
|
||||
id: "metadata"
|
||||
uses: "dependabot/[email protected]"
|
||||
with:
|
||||
github-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
- name: "Enable auto-merge for Dependabot PRs"
|
||||
if: "${{ steps.dependabot-metadata.outputs.update-type != 'version-update:semver-major' }}"
|
||||
run: "gh pr merge --auto --merge \"$PR_URL\""
|
||||
env:
|
||||
PR_URL: "${{github.event.pull_request.html_url}}"
|
||||
GITHUB_TOKEN: "${{secrets.GITHUB_TOKEN}}"
|
||||
+12
-12
@@ -4,25 +4,26 @@ on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- '1.x'
|
||||
- '2.x'
|
||||
- '3.x'
|
||||
- '4.x'
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
name: PHP ${{ matrix.php }}
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
php:
|
||||
- "8.0"
|
||||
- "8.1"
|
||||
- "8.2"
|
||||
- "8.3"
|
||||
- "8.4"
|
||||
- "8.5"
|
||||
include:
|
||||
- php: "8.6"
|
||||
composer-options: "--ignore-platform-req=php+"
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v6
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
@@ -32,7 +33,9 @@ jobs:
|
||||
env:
|
||||
COMPOSER_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- uses: "ramsey/composer-install@v2"
|
||||
- uses: "ramsey/composer-install@v4"
|
||||
with:
|
||||
composer-options: "${{ matrix.composer-options }}"
|
||||
|
||||
- name: Setup problem matchers for PHP
|
||||
run: echo "::add-matcher::${{ runner.tool_cache }}/php.json"
|
||||
@@ -40,9 +43,6 @@ jobs:
|
||||
- name: Run PHPStan
|
||||
run: vendor/bin/phpstan analyse --error-format=github
|
||||
|
||||
- name: Run Psalm
|
||||
run: vendor/bin/psalm --output-format=github
|
||||
|
||||
- name: Setup Problem Matchers for PHPUnit
|
||||
run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"
|
||||
|
||||
|
||||
@@ -4,4 +4,3 @@
|
||||
/composer.lock
|
||||
/phpstan.neon
|
||||
/phpunit.xml
|
||||
/psalm.xml
|
||||
|
||||
@@ -1,5 +1,22 @@
|
||||
# CHANGELOG
|
||||
|
||||
## Unreleased
|
||||
|
||||
## 4.0.0 - 2026-08-23
|
||||
|
||||
* Dropped support for PHP <8.3.
|
||||
* Removed the deprecated `Beste\Clock` interface. Clock implementations now implement
|
||||
`Psr\Clock\ClockInterface` directly.
|
||||
* `WrappingClock` will throw an `InvalidArgumentException` if the given object doesn't behave like
|
||||
a PSR-20 clock and will throw an `UnexpectedValueException` if a later call does not return
|
||||
`DateTimeImmutable`.
|
||||
|
||||
## 3.1.0 - 2026-08-22
|
||||
|
||||
Deprecated `Beste\Clock` in favor of `Psr\Clock\ClockInterface`. The implementations continue to implement
|
||||
the deprecated interface for backwards compatibility. Please type against `Psr\Clock\ClockInterface`.
|
||||
`Beste\Clock` will be removed in 4.0.
|
||||
|
||||
## 3.0.0 - 2022-11-26
|
||||
This release replaces `stella-maris/clock` with `psr/clock`.
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
[](https://packagist.org/packages/beste/clock)
|
||||
[](https://github.com/beste/clock/actions/workflows/tests.yml)
|
||||
|
||||
A collection of Clock implementations.
|
||||
A collection of [PSR-20](https://www.php-fig.org/psr/psr-20/) Clock implementations.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
@@ -114,8 +114,8 @@ printf("\nAfter turning back the clock 5 minutes, it's %s", $frozenClock->now()-
|
||||
|
||||
### `MinuteClock`
|
||||
|
||||
In some cases, microseconds, milliseconds, or even seconds are too precise for some use cases - sometimes it's just
|
||||
enough if something happened in the same minute. Using the minute
|
||||
In some cases, microseconds, milliseconds, or even seconds are too precise. Sometimes it is enough to know that
|
||||
something happened within the same minute. `MinuteClock` removes seconds and fractions of a second from another clock.
|
||||
|
||||
```php
|
||||
# examples/minute_clock.php
|
||||
@@ -141,10 +141,8 @@ printf("For %s, the minute clock still returns %s\n",
|
||||
|
||||
### `WrappingClock`
|
||||
|
||||
If you already have an object with a `now()` method returning a `DateTimeImmutable` object, you can wrap it
|
||||
in a `WrappingClock` to make it a "real" Clock.
|
||||
|
||||
as a "real" clock.
|
||||
If you already have an object with a `now()` method returning a `DateTimeImmutable`, you can wrap it in a
|
||||
`WrappingClock` to make it a PSR-20 clock.
|
||||
|
||||
```php
|
||||
# examples/wrapping_clock.php
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
## Security contact information
|
||||
|
||||
To report a security vulnerability, please use the
|
||||
[Tidelift security contact](https://tidelift.com/security).
|
||||
Tidelift will coordinate the fix and disclosure.
|
||||
+10
-17
@@ -11,29 +11,24 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^8.0",
|
||||
"php": "^8.3",
|
||||
"psr/clock": "^1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/extension-installer": "^1.2",
|
||||
"phpstan/phpstan": "^1.9.1",
|
||||
"phpstan/phpstan-phpunit": "^1.2.2",
|
||||
"phpstan/phpstan-strict-rules": "^1.4.4",
|
||||
"phpunit/phpunit": "^9.5.26",
|
||||
"psalm/plugin-phpunit": "^0.16.1",
|
||||
"vimeo/psalm": "^4.29"
|
||||
"phpstan/extension-installer": "^1.4.1",
|
||||
"phpstan/phpstan": "^2.1",
|
||||
"phpstan/phpstan-phpunit": "^2.0",
|
||||
"phpstan/phpstan-strict-rules": "^2.0",
|
||||
"phpunit/phpunit": "^12.5"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Beste\\Clock\\": "src/Clock"
|
||||
},
|
||||
"files": [
|
||||
"src/Clock.php"
|
||||
]
|
||||
"Beste\\Clock\\": "src"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Beste\\Clock\\Tests\\": "tests/Clock"
|
||||
"Beste\\Clock\\Tests\\": "tests"
|
||||
}
|
||||
},
|
||||
"provide": {
|
||||
@@ -48,11 +43,9 @@
|
||||
"scripts": {
|
||||
"test": [
|
||||
"@phpstan",
|
||||
"@psalm",
|
||||
"@phpunit"
|
||||
],
|
||||
"phpunit": "vendor/bin/phpunit",
|
||||
"phpstan": "vendor/bin/phpstan analyse",
|
||||
"psalm": "vendor/bin/psalm"
|
||||
"phpstan": "vendor/bin/phpstan analyse"
|
||||
}
|
||||
}
|
||||
|
||||
+7
-7
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
|
||||
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/12.5/phpunit.xsd"
|
||||
bootstrap="vendor/autoload.php"
|
||||
cacheResultFile=".build/phpunit/phpunit.cache"
|
||||
beStrictAboutCoversAnnotation="true"
|
||||
cacheDirectory=".build/phpunit/cache"
|
||||
beStrictAboutCoverageMetadata="true"
|
||||
colors="true"
|
||||
>
|
||||
<testsuites>
|
||||
@@ -12,10 +12,10 @@
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<coverage>
|
||||
<source>
|
||||
<include>
|
||||
<directory>src/Clock</directory>
|
||||
<directory>src</directory>
|
||||
</include>
|
||||
</coverage>
|
||||
</source>
|
||||
|
||||
</phpunit>
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<psalm
|
||||
xmlns="https://getpsalm.org/schema/config"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
|
||||
cacheDirectory=".build/psalm"
|
||||
errorLevel="1"
|
||||
resolveFromConfigFile="true"
|
||||
>
|
||||
<projectFiles>
|
||||
<directory name="src" />
|
||||
<directory name="tests" />
|
||||
<ignoreFiles>
|
||||
<directory name="vendor" />
|
||||
</ignoreFiles>
|
||||
</projectFiles>
|
||||
|
||||
<plugins>
|
||||
<pluginClass class="Psalm\PhpUnitPlugin\Plugin"/>
|
||||
</plugins>
|
||||
</psalm>
|
||||
@@ -1,12 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Beste;
|
||||
|
||||
use Psr\Clock\ClockInterface;
|
||||
|
||||
interface Clock extends ClockInterface
|
||||
{
|
||||
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Beste\Clock;
|
||||
|
||||
use Beste\Clock;
|
||||
use DateTimeImmutable;
|
||||
use Psr\Clock\ClockInterface;
|
||||
|
||||
final class WrappingClock implements Clock
|
||||
{
|
||||
private ClockInterface $wrappedClock;
|
||||
|
||||
private function __construct(ClockInterface $wrappedClock)
|
||||
{
|
||||
$this->wrappedClock = $wrappedClock;
|
||||
}
|
||||
|
||||
public static function wrapping(object $clock): self
|
||||
{
|
||||
if ($clock instanceof ClockInterface) {
|
||||
return new self($clock);
|
||||
}
|
||||
|
||||
if (!method_exists($clock, 'now')) {
|
||||
throw new \InvalidArgumentException('$clock must implement StellaMaris\Clock\ClockInterface or have a now() method');
|
||||
}
|
||||
|
||||
if (!($clock->now() instanceof DateTimeImmutable)) {
|
||||
throw new \InvalidArgumentException('$clock->now() must return a DateTimeImmutable');
|
||||
}
|
||||
|
||||
$wrappedClock = new class($clock) implements ClockInterface {
|
||||
private object $clock;
|
||||
|
||||
public function __construct(object $clock)
|
||||
{
|
||||
$this->clock = $clock;
|
||||
}
|
||||
|
||||
public function now(): \DateTimeImmutable
|
||||
{
|
||||
assert(method_exists($this->clock, 'now'));
|
||||
|
||||
$now = $this->clock->now();
|
||||
|
||||
assert($now instanceof \DateTimeImmutable);
|
||||
|
||||
return $now;
|
||||
}
|
||||
};
|
||||
|
||||
return new self($wrappedClock);
|
||||
}
|
||||
|
||||
public function now(): DateTimeImmutable
|
||||
{
|
||||
return $this->wrappedClock->now();
|
||||
}
|
||||
}
|
||||
@@ -4,12 +4,11 @@ declare(strict_types=1);
|
||||
|
||||
namespace Beste\Clock;
|
||||
|
||||
use Beste\Clock;
|
||||
use DateTimeImmutable;
|
||||
use DateTimeZone;
|
||||
use Psr\Clock\ClockInterface;
|
||||
|
||||
final class FrozenClock implements Clock
|
||||
final class FrozenClock implements ClockInterface
|
||||
{
|
||||
private DateTimeImmutable $frozenAt;
|
||||
|
||||
@@ -4,13 +4,13 @@ declare(strict_types=1);
|
||||
|
||||
namespace Beste\Clock;
|
||||
|
||||
use Beste\Clock;
|
||||
use DateTimeImmutable;
|
||||
use DateTimeZone;
|
||||
use InvalidArgumentException;
|
||||
use Psr\Clock\ClockInterface;
|
||||
use Throwable;
|
||||
|
||||
final class LocalizedClock implements Clock
|
||||
final class LocalizedClock implements ClockInterface
|
||||
{
|
||||
private DateTimeZone $timeZone;
|
||||
|
||||
@@ -19,10 +19,7 @@ final class LocalizedClock implements Clock
|
||||
$this->timeZone = $timeZone;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DateTimeZone|string $timeZone
|
||||
*/
|
||||
public static function in($timeZone): self
|
||||
public static function in(DateTimeZone|string $timeZone): self
|
||||
{
|
||||
if (is_string($timeZone)) {
|
||||
try {
|
||||
@@ -4,11 +4,10 @@ declare(strict_types=1);
|
||||
|
||||
namespace Beste\Clock;
|
||||
|
||||
use Beste\Clock;
|
||||
use DateTimeImmutable;
|
||||
use Psr\Clock\ClockInterface;
|
||||
|
||||
final class MinuteClock implements Clock
|
||||
final class MinuteClock implements ClockInterface
|
||||
{
|
||||
private ClockInterface $clock;
|
||||
|
||||
@@ -4,10 +4,10 @@ declare(strict_types=1);
|
||||
|
||||
namespace Beste\Clock;
|
||||
|
||||
use Beste\Clock;
|
||||
use DateTimeImmutable;
|
||||
use Psr\Clock\ClockInterface;
|
||||
|
||||
final class SystemClock implements Clock
|
||||
final class SystemClock implements ClockInterface
|
||||
{
|
||||
private function __construct()
|
||||
{
|
||||
@@ -4,11 +4,11 @@ declare(strict_types=1);
|
||||
|
||||
namespace Beste\Clock;
|
||||
|
||||
use Beste\Clock;
|
||||
use DateTimeImmutable;
|
||||
use DateTimeZone;
|
||||
use Psr\Clock\ClockInterface;
|
||||
|
||||
final class UTCClock implements Clock
|
||||
final class UTCClock implements ClockInterface
|
||||
{
|
||||
private DateTimeZone $timeZone;
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Beste\Clock;
|
||||
|
||||
use Closure;
|
||||
use DateTimeImmutable;
|
||||
use InvalidArgumentException;
|
||||
use Psr\Clock\ClockInterface;
|
||||
use ReflectionMethod;
|
||||
use UnexpectedValueException;
|
||||
|
||||
final class WrappingClock implements ClockInterface
|
||||
{
|
||||
private ClockInterface $wrappedClock;
|
||||
|
||||
private function __construct(ClockInterface $wrappedClock)
|
||||
{
|
||||
$this->wrappedClock = $wrappedClock;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException when the given object doesn't behave like a clock.
|
||||
*/
|
||||
public static function wrapping(object $clock): self
|
||||
{
|
||||
if ($clock instanceof ClockInterface) {
|
||||
return new self($clock);
|
||||
}
|
||||
|
||||
if (!method_exists($clock, 'now')) {
|
||||
throw new InvalidArgumentException('$clock must implement Psr\Clock\ClockInterface or have a now() method');
|
||||
}
|
||||
|
||||
$method = new ReflectionMethod($clock, 'now');
|
||||
|
||||
if (!$method->isPublic() || $method->getNumberOfRequiredParameters() > 0) {
|
||||
throw new InvalidArgumentException('$clock->now() must be public and accept no required parameters');
|
||||
}
|
||||
|
||||
$wrappedClock = new class($method->getClosure($clock)) implements ClockInterface {
|
||||
private Closure $now;
|
||||
|
||||
public function __construct(Closure $now)
|
||||
{
|
||||
$this->now = $now;
|
||||
}
|
||||
|
||||
public function now(): DateTimeImmutable
|
||||
{
|
||||
$now = ($this->now)();
|
||||
|
||||
if (!$now instanceof DateTimeImmutable) {
|
||||
throw new UnexpectedValueException('$clock->now() must return a DateTimeImmutable');
|
||||
}
|
||||
|
||||
return $now;
|
||||
}
|
||||
};
|
||||
|
||||
return new self($wrappedClock);
|
||||
}
|
||||
|
||||
public function now(): DateTimeImmutable
|
||||
{
|
||||
return $this->wrappedClock->now();
|
||||
}
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Beste\Clock\Tests;
|
||||
|
||||
use Beste\Clock\FrozenClock;
|
||||
use Beste\Clock\WrappingClock;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @coversDefaultClass \Beste\Clock\WrappingClock
|
||||
*/
|
||||
final class WrappingClockTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @test
|
||||
*
|
||||
* @covers ::__construct
|
||||
* @covers ::create
|
||||
* @covers ::now
|
||||
*/
|
||||
public function itWrapsAClockInterface(): void
|
||||
{
|
||||
$clock = FrozenClock::fromUTC();
|
||||
|
||||
$wrappedClock = WrappingClock::wrapping($clock);
|
||||
|
||||
self::assertSame(
|
||||
$clock->now()->format(DATE_ATOM),
|
||||
$wrappedClock->now()->format(DATE_ATOM)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*
|
||||
* @covers ::__construct
|
||||
* @covers ::create
|
||||
* @covers ::now
|
||||
*/
|
||||
public function itWrapsAnObjectWithANowMethod(): void
|
||||
{
|
||||
$now = FrozenClock::fromUTC()->now();
|
||||
|
||||
$clock = new class($now) {
|
||||
private \DateTimeImmutable $now;
|
||||
|
||||
public function __construct(\DateTimeImmutable $now)
|
||||
{
|
||||
$this->now = $now;
|
||||
}
|
||||
|
||||
public function now(): \DateTimeImmutable
|
||||
{
|
||||
return $this->now;
|
||||
}
|
||||
};
|
||||
|
||||
$wrappedClock = WrappingClock::wrapping($clock);
|
||||
|
||||
self::assertSame(
|
||||
$now->format(DATE_ATOM),
|
||||
$wrappedClock->now()->format(DATE_ATOM)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*
|
||||
* @covers ::create
|
||||
*/
|
||||
public function itRejectsObjectsWithANowMethodReturningANonDateTimeImmutable(): void
|
||||
{
|
||||
$clock = new class() {
|
||||
public function now(): string
|
||||
{
|
||||
return 'foo';
|
||||
}
|
||||
};
|
||||
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('$clock->now() must return a DateTimeImmutable');
|
||||
|
||||
WrappingClock::wrapping($clock);
|
||||
}
|
||||
}
|
||||
@@ -6,21 +6,18 @@ namespace Beste\Clock\Tests;
|
||||
|
||||
use Beste\Clock\FrozenClock;
|
||||
use DateTimeImmutable;
|
||||
use PHPUnit\Framework\Attributes\CoversClass;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psr\Clock\ClockInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @coversDefaultClass \Beste\Clock\FrozenClock
|
||||
*/
|
||||
#[CoversClass(FrozenClock::class)]
|
||||
final class FrozenClockTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @test
|
||||
* @covers ::__construct
|
||||
* @covers ::at
|
||||
* @covers ::now
|
||||
*/
|
||||
#[Test]
|
||||
public function itFreezesTime(): void
|
||||
{
|
||||
$now = new DateTimeImmutable('2021-03-21 22:16:00');
|
||||
@@ -29,14 +26,7 @@ final class FrozenClockTest extends TestCase
|
||||
self::assertEquals($now, $clock->now());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*
|
||||
* @uses \Beste\Clock\FrozenClock::__construct
|
||||
* @uses \Beste\Clock\FrozenClock::fromUTC()
|
||||
*
|
||||
* @covers ::now()
|
||||
*/
|
||||
#[Test]
|
||||
public function itReturnsAnEqualNowButNotTheSame(): void
|
||||
{
|
||||
$clock = FrozenClock::fromUTC();
|
||||
@@ -48,14 +38,7 @@ final class FrozenClockTest extends TestCase
|
||||
self::assertNotSame($first, $second);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*
|
||||
* @uses \Beste\Clock\FrozenClock::__construct
|
||||
* @uses \Beste\Clock\FrozenClock::now
|
||||
*
|
||||
* @covers ::withNowFrom
|
||||
*/
|
||||
#[Test]
|
||||
public function itFreezesTimeFromAnotherClock(): void
|
||||
{
|
||||
$now = new DateTimeImmutable('now');
|
||||
@@ -71,14 +54,7 @@ final class FrozenClockTest extends TestCase
|
||||
self::assertEquals($now, $frozenClock->now());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*
|
||||
* @uses \Beste\Clock\FrozenClock::__construct
|
||||
* @uses \Beste\Clock\FrozenClock::now
|
||||
*
|
||||
* @covers ::fromUTC
|
||||
*/
|
||||
#[Test]
|
||||
public function itFreezesTheCurrentUTCTime(): void
|
||||
{
|
||||
$clock = FrozenClock::fromUTC();
|
||||
@@ -87,15 +63,7 @@ final class FrozenClockTest extends TestCase
|
||||
self::assertEquals($clock->now(), $clock->now());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*
|
||||
* @uses \Beste\Clock\FrozenClock::__construct
|
||||
* @uses \Beste\Clock\FrozenClock::at
|
||||
* @uses \Beste\Clock\FrozenClock::now
|
||||
*
|
||||
* @covers ::setTo
|
||||
*/
|
||||
#[Test]
|
||||
public function itCanBeSet(): void
|
||||
{
|
||||
$now = new DateTimeImmutable('2021-03-21 18:18:18');
|
||||
@@ -7,18 +7,17 @@ namespace Beste\Clock\Tests;
|
||||
use Beste\Clock\LocalizedClock;
|
||||
use DateTimeZone;
|
||||
use InvalidArgumentException;
|
||||
use PHPUnit\Framework\Attributes\CoversClass;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @coversDefaultClass \Beste\Clock\LocalizedClock
|
||||
*/
|
||||
#[CoversClass(LocalizedClock::class)]
|
||||
final class LocalizedClockTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @test
|
||||
* @covers ::in
|
||||
*/
|
||||
#[Test]
|
||||
public function itRejectsAnInvalidTimeZone(): void
|
||||
{
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
@@ -26,12 +25,7 @@ final class LocalizedClockTest extends TestCase
|
||||
LocalizedClock::in('invalid');
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @covers ::__construct
|
||||
* @covers ::in
|
||||
* @covers ::now
|
||||
*/
|
||||
#[Test]
|
||||
public function itUsesTheGivenTimeZone(): void
|
||||
{
|
||||
$timeZone = new DateTimeZone('Asia/Bangkok');
|
||||
@@ -41,12 +35,7 @@ final class LocalizedClockTest extends TestCase
|
||||
self::assertSame($timeZone->getName(), $now->getTimezone()->getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @covers ::__construct
|
||||
* @covers ::in
|
||||
* @covers ::now
|
||||
*/
|
||||
#[Test]
|
||||
public function itAcceptsTheTimeZoneAsAString(): void
|
||||
{
|
||||
$timeZone = 'Pacific/Guam';
|
||||
@@ -6,30 +6,23 @@ namespace Beste\Clock\Tests;
|
||||
|
||||
use Beste\Clock\MinuteClock;
|
||||
use DateTimeImmutable;
|
||||
use PHPUnit\Framework\Attributes\CoversClass;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psr\Clock\ClockInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @coversDefaultClass \Beste\Clock\MinuteClock
|
||||
*/
|
||||
#[CoversClass(MinuteClock::class)]
|
||||
final class MinuteClockTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @test
|
||||
*
|
||||
* @covers ::__construct
|
||||
* @covers ::wrapping
|
||||
* @covers ::now
|
||||
* @covers ::floor
|
||||
*
|
||||
* @uses \Beste\Clock\FrozenClock
|
||||
*
|
||||
* @dataProvider dateTimeStringProvider
|
||||
*/
|
||||
#[Test]
|
||||
#[DataProvider('dateTimeStringProvider')]
|
||||
public function itDiscardsEverythingSmallerThanMinutes(string $format, string $input, string $expected): void
|
||||
{
|
||||
$mockClock = $this->createMock(ClockInterface::class);
|
||||
$mockClock = self::createStub(ClockInterface::class);
|
||||
$mockClock->method('now')->willReturn(new DateTimeImmutable($input));
|
||||
|
||||
// Just to be sure that the test input is valid ^^
|
||||
@@ -43,7 +36,7 @@ final class MinuteClockTest extends TestCase
|
||||
/**
|
||||
* @return array<string, string[]>
|
||||
*/
|
||||
public function dateTimeStringProvider(): array
|
||||
public static function dateTimeStringProvider(): array
|
||||
{
|
||||
return [
|
||||
'microseconds' => [
|
||||
@@ -5,12 +5,14 @@ declare(strict_types=1);
|
||||
namespace Beste\Clock\Tests;
|
||||
|
||||
use Beste\Clock\SystemClock;
|
||||
use PHPUnit\Framework\Attributes\CoversClass;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @coversDefaultClass \Beste\Clock\SystemClock
|
||||
*/
|
||||
#[CoversClass(SystemClock::class)]
|
||||
final class SystemClockTest extends TestCase
|
||||
{
|
||||
private string $defaultTimeZone;
|
||||
@@ -25,16 +27,10 @@ final class SystemClockTest extends TestCase
|
||||
date_default_timezone_set($this->defaultTimeZone);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*
|
||||
* @covers ::__construct
|
||||
* @covers ::create
|
||||
* @covers ::now
|
||||
*/
|
||||
#[Test]
|
||||
public function itUsesTheSystemTimeZone(): void
|
||||
{
|
||||
$timeZone = 'Europe/Berlin';
|
||||
$timeZone = $this->defaultTimeZone === 'Europe/Berlin' ? 'UTC' : 'Europe/Berlin';
|
||||
self::assertNotSame($timeZone, $this->defaultTimeZone);
|
||||
|
||||
date_default_timezone_set($timeZone);
|
||||
@@ -5,12 +5,14 @@ declare(strict_types=1);
|
||||
namespace Beste\Clock\Tests;
|
||||
|
||||
use Beste\Clock\UTCClock;
|
||||
use PHPUnit\Framework\Attributes\CoversClass;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @coversDefaultClass \Beste\Clock\UTCClock
|
||||
*/
|
||||
#[CoversClass(UTCClock::class)]
|
||||
final class UTCClockTest extends TestCase
|
||||
{
|
||||
private string $defaultTimeZone;
|
||||
@@ -25,13 +27,7 @@ final class UTCClockTest extends TestCase
|
||||
date_default_timezone_set($this->defaultTimeZone);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*
|
||||
* @covers ::__construct
|
||||
* @covers ::create
|
||||
* @covers ::now
|
||||
*/
|
||||
#[Test]
|
||||
public function itUsesUTCRegardlessOfTheSystemTimeZone(): void
|
||||
{
|
||||
date_default_timezone_set('Europe/Berlin');
|
||||
@@ -0,0 +1,147 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Beste\Clock\Tests;
|
||||
|
||||
use Beste\Clock\FrozenClock;
|
||||
use Beste\Clock\WrappingClock;
|
||||
use DateTimeImmutable;
|
||||
use DateTimeZone;
|
||||
use InvalidArgumentException;
|
||||
use PHPUnit\Framework\Attributes\CoversClass;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use PHPUnit\Framework\Attributes\UsesClass;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use UnexpectedValueException;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
#[CoversClass(WrappingClock::class)]
|
||||
#[UsesClass(FrozenClock::class)]
|
||||
final class WrappingClockTest extends TestCase
|
||||
{
|
||||
#[Test]
|
||||
public function itWrapsAClockInterface(): void
|
||||
{
|
||||
$clock = FrozenClock::fromUTC();
|
||||
|
||||
$wrappedClock = WrappingClock::wrapping($clock);
|
||||
|
||||
self::assertSame(
|
||||
$clock->now()->format(DATE_ATOM),
|
||||
$wrappedClock->now()->format(DATE_ATOM)
|
||||
);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itWrapsAnObjectWithANowMethod(): void
|
||||
{
|
||||
$now = FrozenClock::fromUTC()->now();
|
||||
|
||||
$clock = new class($now) {
|
||||
private DateTimeImmutable $now;
|
||||
|
||||
public function __construct(DateTimeImmutable $now)
|
||||
{
|
||||
$this->now = $now;
|
||||
}
|
||||
|
||||
public function now(): DateTimeImmutable
|
||||
{
|
||||
return $this->now;
|
||||
}
|
||||
};
|
||||
|
||||
$wrappedClock = WrappingClock::wrapping($clock);
|
||||
|
||||
self::assertSame(
|
||||
$now->format(DATE_ATOM),
|
||||
$wrappedClock->now()->format(DATE_ATOM)
|
||||
);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itDoesNotCallNowWhileWrapping(): void
|
||||
{
|
||||
$clock = new class() {
|
||||
public int $calls = 0;
|
||||
|
||||
public function now(): DateTimeImmutable
|
||||
{
|
||||
++$this->calls;
|
||||
|
||||
return new DateTimeImmutable();
|
||||
}
|
||||
};
|
||||
|
||||
$wrappedClock = WrappingClock::wrapping($clock);
|
||||
|
||||
self::assertSame(0, $clock->calls);
|
||||
|
||||
$wrappedClock->now();
|
||||
|
||||
self::assertSame(1, $clock->calls);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itRejectsObjectsWithANowMethodReturningANonDateTimeImmutable(): void
|
||||
{
|
||||
$clock = new class() {
|
||||
public function now(): string
|
||||
{
|
||||
return 'foo';
|
||||
}
|
||||
};
|
||||
|
||||
$this->expectException(UnexpectedValueException::class);
|
||||
$this->expectExceptionMessage('$clock->now() must return a DateTimeImmutable');
|
||||
|
||||
WrappingClock::wrapping($clock)->now();
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itRejectsObjectsWithoutANowMethod(): void
|
||||
{
|
||||
$clock = new class() {
|
||||
};
|
||||
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('$clock must implement Psr\Clock\ClockInterface or have a now() method');
|
||||
|
||||
WrappingClock::wrapping($clock);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itRejectsObjectsWithANonPublicNowMethod(): void
|
||||
{
|
||||
$clock = new class() {
|
||||
protected function now(): DateTimeImmutable
|
||||
{
|
||||
return new DateTimeImmutable();
|
||||
}
|
||||
};
|
||||
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('$clock->now() must be public and accept no required parameters');
|
||||
|
||||
WrappingClock::wrapping($clock);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itRejectsObjectsWithANowMethodRequiringArguments(): void
|
||||
{
|
||||
$clock = new class() {
|
||||
public function now(string $timeZone): DateTimeImmutable
|
||||
{
|
||||
return new DateTimeImmutable('now', new DateTimeZone($timeZone));
|
||||
}
|
||||
};
|
||||
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('$clock->now() must be public and accept no required parameters');
|
||||
|
||||
WrappingClock::wrapping($clock);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user