mirror of
https://github.com/beste/clock.git
synced 2026-10-17 19:27:35 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf2cc16e66 | ||
|
|
6ee3f318f8 | ||
|
|
2ba952cc73 | ||
|
|
2861280adc | ||
|
|
a5e62affe0 | ||
|
|
5acec1192d | ||
|
|
fd2e55f899 | ||
|
|
24b2fc5716 | ||
|
|
04017e5bc2 | ||
|
|
b47e76adf3 | ||
|
|
4b16082f08 | ||
|
|
317ed729ce | ||
|
|
201ed087ca | ||
|
|
f3edef9cbc | ||
|
|
98d1e6f1d8 | ||
|
|
731f4c9651 |
@@ -0,0 +1,15 @@
|
|||||||
|
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||||
|
|
||||||
|
version: 2
|
||||||
|
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "github-actions"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "daily"
|
||||||
|
commit-message:
|
||||||
|
include: "scope"
|
||||||
|
prefix: "github-actions"
|
||||||
|
open-pull-requests-limit: 10
|
||||||
|
labels:
|
||||||
|
- "dependency"
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
# https://probot.github.io/apps/settings/
|
||||||
|
_extends: beste/.github
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
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}}"
|
||||||
@@ -4,7 +4,8 @@ on:
|
|||||||
pull_request:
|
pull_request:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- 'main'
|
- '1.x'
|
||||||
|
- '2.x'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
tests:
|
tests:
|
||||||
@@ -14,16 +15,13 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
php:
|
php:
|
||||||
- "7.4"
|
|
||||||
- "8.0"
|
- "8.0"
|
||||||
- "8.1"
|
- "8.1"
|
||||||
dependencies:
|
- "8.2"
|
||||||
- "lowest"
|
|
||||||
- "highest"
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
- name: Setup PHP
|
- name: Setup PHP
|
||||||
uses: shivammathur/setup-php@v2
|
uses: shivammathur/setup-php@v2
|
||||||
with:
|
with:
|
||||||
@@ -33,10 +31,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
COMPOSER_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
COMPOSER_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- uses: "ramsey/composer-install@v1"
|
- uses: "ramsey/composer-install@v2"
|
||||||
with:
|
|
||||||
dependency-versions: "${{ matrix.dependencies }}"
|
|
||||||
composer-options: "${{ matrix.composer-options }}"
|
|
||||||
|
|
||||||
- name: Setup problem matchers for PHP
|
- name: Setup problem matchers for PHP
|
||||||
run: echo "::add-matcher::${{ runner.tool_cache }}/php.json"
|
run: echo "::add-matcher::${{ runner.tool_cache }}/php.json"
|
||||||
|
|||||||
+16
-3
@@ -1,8 +1,21 @@
|
|||||||
# CHANGELOG
|
# CHANGELOG
|
||||||
|
|
||||||
## [Unreleased]
|
## 2.3.1 - 2022-11-25
|
||||||
|
This release reverts the change to implement `psr/clock` directly, to not break dependents who rely on `stella-maris/clock`
|
||||||
|
|
||||||
|
## 2.3.0 - 2022-11-25
|
||||||
|
This release re-introduces support for PHP ^8.0 and implements the freshly released `psr/clock` (PSR-20) directly.
|
||||||
|
|
||||||
|
## 2.2.0 - 2022-11-04
|
||||||
|
This release drops support for PHP <8.1.
|
||||||
|
|
||||||
|
## 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
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
+13
-10
@@ -11,23 +11,23 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"php": "~7.4.0 || ~8.0.0 || ~8.1.0"
|
"php": "^8.0",
|
||||||
|
"stella-maris/clock": "^0.1.6"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpstan/extension-installer": "^1.1",
|
"phpstan/extension-installer": "^1.2",
|
||||||
"phpstan/phpstan": "^1.0",
|
"phpstan/phpstan": "^1.9.1",
|
||||||
"phpstan/phpstan-phpunit": "^1.0",
|
"phpstan/phpstan-phpunit": "^1.2.2",
|
||||||
"phpstan/phpstan-strict-rules": "^1.0",
|
"phpstan/phpstan-strict-rules": "^1.4.4",
|
||||||
"phpunit/phpunit": "^9.5.10",
|
"phpunit/phpunit": "^9.5.26",
|
||||||
"psalm/plugin-phpunit": "^0.16.0",
|
"psalm/plugin-phpunit": "^0.16.1",
|
||||||
"vimeo/psalm": "^4.15"
|
"vimeo/psalm": "^4.29"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
"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": [
|
||||||
|
|||||||
@@ -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));
|
||||||
@@ -7,5 +7,3 @@ parameters:
|
|||||||
|
|
||||||
tmpDir: .build/phpstan/
|
tmpDir: .build/phpstan/
|
||||||
|
|
||||||
bootstrapFiles:
|
|
||||||
- ./src/psr-polyfill.php
|
|
||||||
|
|||||||
+1
-1
@@ -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
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user