4 Commits
Author SHA1 Message Date
Jérôme Gamez 201ed087ca Re-enable tests for the 1.x and 2.x branches 2022-04-24 15:39:46 +02:00
Jérôme Gamez f3edef9cbc Add a WrappingClock
The `WrappingClock` allows using an object with a `now()` method
returning a `DateTimeImmutable` object as a "real" clock.
2022-04-24 15:38:25 +02:00
Jérôme Gamez 98d1e6f1d8 Release 2.0.0 2022-04-20 16:28:31 +02:00
Andreas Heigl 731f4c9651 Implement forward compatible Clock-Interface
The stella-maris/clock package provides an interface based on the
currently proposed status of PSR20. Due to the inactivity of the PSR20 working
group this is a way to already provide interoperability while still
maintaining forward compatibility. When the current status of PSR20 will
be released at one point in time the stella-maris/clock package will
extend the PSR20 interface so that this package becomes immeadiately
PSR20 compatible without any further work necessary. In the long run the
stella-maris/clock package will then be marked deprecated so that people
can then use the PSR20 provided implementation.

Should the implementation of PSR20 change between now and a possible
release then this interface will still exist and a possible
implementation will need more code-changes anyhow so this interface will
still provide some way of interoperability.

The implementation of the PSR20 polyfill has been moved to the
stella-maris/clock interface which can be implemented independentyl from
PSR20 and might allow some interoperability before the working group of
the FIG manages to decide upon something.
2022-04-20 16:21:40 +02:00
14 changed files with 233 additions and 31 deletions
+2 -1
View File
@@ -4,7 +4,8 @@ on:
pull_request: pull_request:
push: push:
branches: branches:
- 'main' - '1.x'
- '2.x'
jobs: jobs:
tests: tests:
+7 -3
View File
@@ -1,8 +1,12 @@
# CHANGELOG # CHANGELOG
## [Unreleased] ## 2.1.0 - 2022-04-22
Adds the `WrappingClock` which allows using an object with a `now()` method returning a `DateTimeImmutable` object
as a "real" clock.
## 2.0.0 - 2022-04-20
This release introduces a compatibility layer with the PSR-20 draft, allowing us to already
get some interoperability by depending on a shared interface.
## 1.0.0 - 2021-12-10 ## 1.0.0 - 2021-12-10
Initial Release Initial Release
[Unreleased]: https://github.com/beste/clock/compare/1.0.0...main
+36
View File
@@ -15,6 +15,7 @@ A collection of Clock implementations.
- [`UTCClock`](#utcclock) - The clock that you should™ use - [`UTCClock`](#utcclock) - The clock that you should™ use
- [`FrozenClock`](#frozenclock) - A clock that stopped moving (perfect for tests) - [`FrozenClock`](#frozenclock) - A clock that stopped moving (perfect for tests)
- [`MinuteClock`](#minuteclock) - Who cares about seconds or even less? - [`MinuteClock`](#minuteclock) - Who cares about seconds or even less?
- [`WrappingClock`](#wrappingclock) - Allows wrapping a non-clock with a `now()` method in a clock
- [Running Tests](#running-tests) - [Running Tests](#running-tests)
## Installation ## Installation
@@ -136,7 +137,42 @@ printf("For %s, the minute clock still returns %s\n",
$frozenClock->now()->format('H:i:s'), $frozenClock->now()->format('H:i:s'),
$clock->now()->format('H:i:s') $clock->now()->format('H:i:s')
); );
```
### `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.
```php
# examples/wrapping_clock.php
use Beste\Clock\WrappingClock;
// Create a frozen $now so that we can test the wrapping clock.
$now = new DateTimeImmutable('2012-04-24 12:00:00');
// Create an object that is NOT a clock, but has a now() method returning the frozen $now.
$clock = new class($now) {
private \DateTimeImmutable $now;
public function __construct(\DateTimeImmutable $now)
{
$this->now = $now;
}
public function now(): \DateTimeImmutable
{
return $this->now;
}
};
// We can now wrap the object in a clock.
$wrappedClock = WrappingClock::wrapping($clock);
assert($now->format(DATE_ATOM) === $wrappedClock->now()->format(DATE_ATOM));
``` ```
## Running tests ## Running tests
+6 -3
View File
@@ -11,7 +11,8 @@
} }
], ],
"require": { "require": {
"php": "~7.4.0 || ~8.0.0 || ~8.1.0" "php": "~7.4.0 || ~8.0.0 || ~8.1.0",
"stella-maris/clock": "^0.1.4"
}, },
"require-dev": { "require-dev": {
"phpstan/extension-installer": "^1.1", "phpstan/extension-installer": "^1.1",
@@ -27,7 +28,6 @@
"Beste\\Clock\\": "src/Clock" "Beste\\Clock\\": "src/Clock"
}, },
"files": [ "files": [
"src/psr-polyfill.php",
"src/Clock.php" "src/Clock.php"
] ]
}, },
@@ -40,7 +40,10 @@
"psr/clock-implementation": "1.0" "psr/clock-implementation": "1.0"
}, },
"config": { "config": {
"sort-packages": true "sort-packages": true,
"allow-plugins": {
"phpstan/extension-installer": true
}
}, },
"scripts": { "scripts": {
"test": [ "test": [
+28
View File
@@ -0,0 +1,28 @@
<?php
require __DIR__.'/../vendor/autoload.php';
use Beste\Clock\WrappingClock;
// Create a frozen $now so that we can test the wrapping clock.
$now = new DateTimeImmutable('2012-04-24 12:00:00');
// Create an object that is NOT a clock, but has a now() method returning the frozen $now.
$clock = new class($now) {
private \DateTimeImmutable $now;
public function __construct(\DateTimeImmutable $now)
{
$this->now = $now;
}
public function now(): \DateTimeImmutable
{
return $this->now;
}
};
// We can now wrap the object in a clock.
$wrappedClock = WrappingClock::wrapping($clock);
assert($now->format(DATE_ATOM) === $wrappedClock->now()->format(DATE_ATOM));
-2
View File
@@ -7,5 +7,3 @@ parameters:
tmpDir: .build/phpstan/ tmpDir: .build/phpstan/
bootstrapFiles:
- ./src/psr-polyfill.php
+1 -1
View File
@@ -4,7 +4,7 @@ declare(strict_types=1);
namespace Beste; namespace Beste;
use Psr\Clock\ClockInterface; use StellaMaris\Clock\ClockInterface;
interface Clock extends ClockInterface interface Clock extends ClockInterface
{ {
+1 -1
View File
@@ -7,7 +7,7 @@ namespace Beste\Clock;
use Beste\Clock; use Beste\Clock;
use DateTimeImmutable; use DateTimeImmutable;
use DateTimeZone; use DateTimeZone;
use Psr\Clock\ClockInterface; use StellaMaris\Clock\ClockInterface;
final class FrozenClock implements Clock final class FrozenClock implements Clock
{ {
+1 -1
View File
@@ -6,7 +6,7 @@ namespace Beste\Clock;
use Beste\Clock; use Beste\Clock;
use DateTimeImmutable; use DateTimeImmutable;
use Psr\Clock\ClockInterface; use StellaMaris\Clock\ClockInterface;
final class MinuteClock implements Clock final class MinuteClock implements Clock
{ {
+61
View File
@@ -0,0 +1,61 @@
<?php
declare(strict_types=1);
namespace Beste\Clock;
use Beste\Clock;
use DateTimeImmutable;
use StellaMaris\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();
}
}
-17
View File
@@ -1,17 +0,0 @@
<?php
namespace Psr\Clock;
if (class_exists('\Psr\Clock\ClockInterface')) {
return;
}
use DateTimeImmutable;
interface ClockInterface
{
/**
* Returns the current time as a DateTimeImmutable Object
*/
public function now(): DateTimeImmutable;
}
+1 -1
View File
@@ -7,7 +7,7 @@ namespace Beste\Clock\Tests;
use Beste\Clock\FrozenClock; use Beste\Clock\FrozenClock;
use DateTimeImmutable; use DateTimeImmutable;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Psr\Clock\ClockInterface; use StellaMaris\Clock\ClockInterface;
/** /**
* @internal * @internal
+1 -1
View File
@@ -7,7 +7,7 @@ namespace Beste\Clock\Tests;
use Beste\Clock\MinuteClock; use Beste\Clock\MinuteClock;
use DateTimeImmutable; use DateTimeImmutable;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Psr\Clock\ClockInterface; use StellaMaris\Clock\ClockInterface;
/** /**
* @internal * @internal
+88
View File
@@ -0,0 +1,88 @@
<?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);
}
}