82 Commits
Author SHA1 Message Date
Jérôme Gamez 0754922206 Release 4.0.0 2026-08-23 23:06:53 +02:00
Jérôme Gamez df585ab815 Update changelog 2026-08-23 20:09:20 +02:00
Jérôme Gamez c094cf87e3 Don't assume the local time zone is not Europe/Berlin 2026-08-23 20:03:38 +02:00
Jérôme Gamez c2243a5934 Harden WrappingClock validation
Wrapping an object should not call it just to validate the return value.
Instead, we check the method signature when the object is wrapped, and
the return value at runtime. We were implicitly checking it already
with the `assert()` calls.
2026-08-23 20:03:38 +02:00
Jérôme Gamez e8f20c112e Don't auto-merge dependabot PRs 2026-08-23 19:35:54 +02:00
Jérôme Gamez 7db7032723 Type time zone input 2026-08-23 19:33:15 +02:00
Jérôme Gamez 8f5274748c Update README 2026-08-23 19:30:12 +02:00
Jérôme Gamez e73aacc19c Update to PHPUnit 12 2026-08-22 23:51:52 +02:00
Jérôme Gamez c062aff727 Drop support for PHP versions before 8.3 2026-08-22 23:51:52 +02:00
Jérôme Gamez b4408036c1 Move source and test files up one level 2026-08-22 23:24:55 +02:00
Jérôme Gamez 0a55b7a019 Remove deprecated Clock interface 2026-08-22 23:24:55 +02:00
Jérôme Gamez 9818902960 Release 3.1.0 2026-08-22 23:09:57 +02:00
Jérôme Gamez 622d86328b Deprecate Clock interface 2026-08-22 00:11:17 +02:00
Jérôme Gamez bebff7fd27 Test against PHP 8.6 2026-08-21 00:00:48 +02:00
Jérôme Gamez 2c5bf134e4 Inherit funding information from the .github repo 2026-05-04 17:56:55 +02:00
github-actions[bot] 8f0a5a5f6c Merge pull request #23 from beste/dependabot/github_actions/dependabot/fetch-metadata-3.1.0
github-actions(deps): bump dependabot/fetch-metadata from 3.0.0 to 3.1.0
2026-04-20 20:38:22 +00:00
dependabot[bot] e7b50902b0 github-actions(deps): bump dependabot/fetch-metadata from 3.0.0 to 3.1.0
Bumps [dependabot/fetch-metadata](https://github.com/dependabot/fetch-metadata) from 3.0.0 to 3.1.0.
- [Release notes](https://github.com/dependabot/fetch-metadata/releases)
- [Commits](https://github.com/dependabot/fetch-metadata/compare/v3.0.0...v3.1.0)

---
updated-dependencies:
- dependency-name: dependabot/fetch-metadata
  dependency-version: 3.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2026-04-20 20:38:10 +00:00
Jérôme Gamez 264e712f65 Fix dependencies label 2026-03-29 00:43:27 +01:00
github-actions[bot] ebe8de302d Merge pull request #22 from beste/dependabot/github_actions/dependabot/fetch-metadata-3.0.0
github-actions(deps): bump dependabot/fetch-metadata from 2.5.0 to 3.0.0
2026-03-27 18:22:50 +00:00
dependabot[bot] 4bd164d047 github-actions(deps): bump dependabot/fetch-metadata from 2.5.0 to 3.0.0
Bumps [dependabot/fetch-metadata](https://github.com/dependabot/fetch-metadata) from 2.5.0 to 3.0.0.
- [Release notes](https://github.com/dependabot/fetch-metadata/releases)
- [Commits](https://github.com/dependabot/fetch-metadata/compare/v2.5.0...v3.0.0)

---
updated-dependencies:
- dependency-name: dependabot/fetch-metadata
  dependency-version: 3.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
2026-03-27 18:22:38 +00:00
github-actions[bot] 5ae0cc3a8d Merge pull request #21 from beste/dependabot/github_actions/ramsey/composer-install-4
github-actions(deps): bump ramsey/composer-install from 3 to 4
2026-03-16 18:55:39 +00:00
dependabot[bot] 0611b8339d github-actions(deps): bump ramsey/composer-install from 3 to 4
Bumps [ramsey/composer-install](https://github.com/ramsey/composer-install) from 3 to 4.
- [Release notes](https://github.com/ramsey/composer-install/releases)
- [Commits](https://github.com/ramsey/composer-install/compare/v3...v4)

---
updated-dependencies:
- dependency-name: ramsey/composer-install
  dependency-version: '4'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
2026-03-16 18:55:21 +00:00
Jérôme Gamez 729cb30697 Add thanks.dev 2026-02-12 09:28:42 +01:00
github-actions[bot] dc84fbe85a Merge pull request #20 from beste/dependabot/github_actions/dependabot/fetch-metadata-2.5.0
github-actions(deps): bump dependabot/fetch-metadata from 2.4.0 to 2.5.0
2026-01-05 18:39:43 +00:00
dependabot[bot] 93c6f6dca7 github-actions(deps): bump dependabot/fetch-metadata from 2.4.0 to 2.5.0
Bumps [dependabot/fetch-metadata](https://github.com/dependabot/fetch-metadata) from 2.4.0 to 2.5.0.
- [Release notes](https://github.com/dependabot/fetch-metadata/releases)
- [Commits](https://github.com/dependabot/fetch-metadata/compare/v2.4.0...v2.5.0)

---
updated-dependencies:
- dependency-name: dependabot/fetch-metadata
  dependency-version: 2.5.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2026-01-05 18:39:31 +00:00
github-actions[bot] e4b5b09932 Merge pull request #19 from beste/dependabot/github_actions/actions/checkout-6
github-actions(deps): bump actions/checkout from 5 to 6
2025-11-20 18:04:47 +00:00
dependabot[bot] 9c852bc985 github-actions(deps): bump actions/checkout from 5 to 6
Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
2025-11-20 18:04:36 +00:00
Jérôme Gamez 48c449b37e Import classes instead of using FQNs 2025-09-29 23:57:09 +02:00
Jérôme Gamez ff31026046 Test with PHP 8.5 2025-09-29 23:57:09 +02:00
Jérôme Gamez d623da43da Update PHPStan and fix wrong @covers annotations 2025-09-12 16:04:10 +02:00
github-actions[bot] 5a0bf8fdba Merge pull request #18 from beste/dependabot/github_actions/actions/checkout-5
github-actions(deps): bump actions/checkout from 4 to 5
2025-08-12 22:20:35 +00:00
dependabot[bot] 88ddce2cd7 github-actions(deps): bump actions/checkout from 4 to 5
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
2025-08-12 22:20:22 +00:00
github-actions[bot] 26ac2fab9a Merge pull request #17 from beste/dependabot/github_actions/dependabot/fetch-metadata-2.4.0
github-actions(deps): bump dependabot/fetch-metadata from 2.3.0 to 2.4.0
2025-05-12 18:42:40 +00:00
dependabot[bot] 0b23a6d481 github-actions(deps): bump dependabot/fetch-metadata from 2.3.0 to 2.4.0
Bumps [dependabot/fetch-metadata](https://github.com/dependabot/fetch-metadata) from 2.3.0 to 2.4.0.
- [Release notes](https://github.com/dependabot/fetch-metadata/releases)
- [Commits](https://github.com/dependabot/fetch-metadata/compare/v2.3.0...v2.4.0)

---
updated-dependencies:
- dependency-name: dependabot/fetch-metadata
  dependency-version: 2.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2025-05-12 18:42:28 +00:00
github-actions[bot] 2e9fb8d220 Merge pull request #16 from beste/dependabot/github_actions/dependabot/fetch-metadata-2.3.0
github-actions(deps): bump dependabot/fetch-metadata from 2.2.0 to 2.3.0
2025-01-27 18:21:32 +00:00
dependabot[bot] 1278a71f80 github-actions(deps): bump dependabot/fetch-metadata from 2.2.0 to 2.3.0
Bumps [dependabot/fetch-metadata](https://github.com/dependabot/fetch-metadata) from 2.2.0 to 2.3.0.
- [Release notes](https://github.com/dependabot/fetch-metadata/releases)
- [Commits](https://github.com/dependabot/fetch-metadata/compare/v2.2.0...v2.3.0)

---
updated-dependencies:
- dependency-name: dependabot/fetch-metadata
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2025-01-27 18:21:03 +00:00
Jérôme Gamez 17e1c4ce7e Remove Psalm 2024-08-17 01:11:54 +02:00
Jérôme Gamez 0d80994228 Test with PHP 8.3 and 8.4 2024-08-17 01:07:13 +02:00
Jérôme Gamez 1a2fab532b Bump dev-dependencies 2024-08-17 00:55:49 +02:00
Jérôme Gamez 84e729ce8c Create FUNDING.yml 2024-07-13 21:45:02 +02:00
Jérôme Gamez 06686333c0 Add security policy 2024-07-13 21:26:56 +02:00
github-actions[bot] 621355dffd Merge pull request #15 from beste/dependabot/github_actions/dependabot/fetch-metadata-2.2.0
github-actions(deps): bump dependabot/fetch-metadata from 2.1.0 to 2.2.0
2024-07-05 18:12:59 +00:00
dependabot[bot] 88afd40a02 github-actions(deps): bump dependabot/fetch-metadata from 2.1.0 to 2.2.0
Bumps [dependabot/fetch-metadata](https://github.com/dependabot/fetch-metadata) from 2.1.0 to 2.2.0.
- [Release notes](https://github.com/dependabot/fetch-metadata/releases)
- [Commits](https://github.com/dependabot/fetch-metadata/compare/v2.1.0...v2.2.0)

---
updated-dependencies:
- dependency-name: dependabot/fetch-metadata
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2024-07-05 18:12:48 +00:00
github-actions[bot] ec0d9b2006 Merge pull request #14 from beste/dependabot/github_actions/dependabot/fetch-metadata-2.1.0
github-actions(deps): bump dependabot/fetch-metadata from 2.0.0 to 2.1.0
2024-04-24 18:56:30 +00:00
dependabot[bot] c3758513b5 github-actions(deps): bump dependabot/fetch-metadata from 2.0.0 to 2.1.0
Bumps [dependabot/fetch-metadata](https://github.com/dependabot/fetch-metadata) from 2.0.0 to 2.1.0.
- [Release notes](https://github.com/dependabot/fetch-metadata/releases)
- [Commits](https://github.com/dependabot/fetch-metadata/compare/v2.0.0...v2.1.0)

---
updated-dependencies:
- dependency-name: dependabot/fetch-metadata
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2024-04-24 18:56:15 +00:00
github-actions[bot] 6fcddef411 Merge pull request #13 from beste/dependabot/github_actions/dependabot/fetch-metadata-2.0.0
github-actions(deps): bump dependabot/fetch-metadata from 1.7.0 to 2.0.0
2024-03-22 18:07:23 +00:00
dependabot[bot] b69661437f github-actions(deps): bump dependabot/fetch-metadata from 1.7.0 to 2.0.0
Bumps [dependabot/fetch-metadata](https://github.com/dependabot/fetch-metadata) from 1.7.0 to 2.0.0.
- [Release notes](https://github.com/dependabot/fetch-metadata/releases)
- [Commits](https://github.com/dependabot/fetch-metadata/compare/v1.7.0...v2.0.0)

---
updated-dependencies:
- dependency-name: dependabot/fetch-metadata
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
2024-03-22 18:07:09 +00:00
github-actions[bot] fc7cb0d32c Merge pull request #12 from beste/dependabot/github_actions/dependabot/fetch-metadata-1.7.0
github-actions(deps): bump dependabot/fetch-metadata from 1.6.0 to 1.7.0
2024-03-21 18:54:35 +00:00
dependabot[bot] 05159b138f github-actions(deps): bump dependabot/fetch-metadata from 1.6.0 to 1.7.0
Bumps [dependabot/fetch-metadata](https://github.com/dependabot/fetch-metadata) from 1.6.0 to 1.7.0.
- [Release notes](https://github.com/dependabot/fetch-metadata/releases)
- [Commits](https://github.com/dependabot/fetch-metadata/compare/v1.6.0...v1.7.0)

---
updated-dependencies:
- dependency-name: dependabot/fetch-metadata
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2024-03-21 18:54:23 +00:00
Jérôme Gamez d29ed0d755 Link to the PSR-20 Standard 2024-03-02 00:32:58 +01:00
github-actions[bot] 52bafeb8e3 Merge pull request #11 from beste/dependabot/github_actions/ramsey/composer-install-3
github-actions(deps): bump ramsey/composer-install from 2 to 3
2024-02-29 18:51:24 +00:00
dependabot[bot] e9ceb17d59 github-actions(deps): bump ramsey/composer-install from 2 to 3
Bumps [ramsey/composer-install](https://github.com/ramsey/composer-install) from 2 to 3.
- [Release notes](https://github.com/ramsey/composer-install/releases)
- [Commits](https://github.com/ramsey/composer-install/compare/v2...v3)

---
updated-dependencies:
- dependency-name: ramsey/composer-install
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
2024-02-29 18:51:10 +00:00
github-actions[bot] 3861182e4f Merge pull request #10 from beste/dependabot/github_actions/actions/checkout-4
github-actions(deps): bump actions/checkout from 3 to 4
2023-09-04 18:52:35 +00:00
dependabot[bot] 6d38f044b3 github-actions(deps): bump actions/checkout from 3 to 4
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
2023-09-04 18:52:21 +00:00
github-actions[bot] aac77907e3 Merge pull request #9 from beste/dependabot/github_actions/dependabot/fetch-metadata-1.6.0
github-actions(deps): bump dependabot/fetch-metadata from 1.5.1 to 1.6.0
2023-06-27 19:02:02 +00:00
dependabot[bot] ed1c70ffdf github-actions(deps): bump dependabot/fetch-metadata from 1.5.1 to 1.6.0
Bumps [dependabot/fetch-metadata](https://github.com/dependabot/fetch-metadata) from 1.5.1 to 1.6.0.
- [Release notes](https://github.com/dependabot/fetch-metadata/releases)
- [Commits](https://github.com/dependabot/fetch-metadata/compare/v1.5.1...v1.6.0)

---
updated-dependencies:
- dependency-name: dependabot/fetch-metadata
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2023-06-27 19:01:49 +00:00
github-actions[bot] 8ec664cfae Merge pull request #8 from beste/dependabot/github_actions/dependabot/fetch-metadata-1.5.1
github-actions(deps): bump dependabot/fetch-metadata from 1.5.0 to 1.5.1
2023-05-24 19:02:10 +00:00
dependabot[bot] 5af4eaf534 github-actions(deps): bump dependabot/fetch-metadata from 1.5.0 to 1.5.1
Bumps [dependabot/fetch-metadata](https://github.com/dependabot/fetch-metadata) from 1.5.0 to 1.5.1.
- [Release notes](https://github.com/dependabot/fetch-metadata/releases)
- [Commits](https://github.com/dependabot/fetch-metadata/compare/v1.5.0...v1.5.1)

---
updated-dependencies:
- dependency-name: dependabot/fetch-metadata
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2023-05-24 19:01:53 +00:00
github-actions[bot] 4b68b50507 Merge pull request #7 from beste/dependabot/github_actions/dependabot/fetch-metadata-1.5.0
github-actions(deps): bump dependabot/fetch-metadata from 1.4.0 to 1.5.0
2023-05-22 19:06:20 +00:00
dependabot[bot] 0999d2836d github-actions(deps): bump dependabot/fetch-metadata from 1.4.0 to 1.5.0
Bumps [dependabot/fetch-metadata](https://github.com/dependabot/fetch-metadata) from 1.4.0 to 1.5.0.
- [Release notes](https://github.com/dependabot/fetch-metadata/releases)
- [Commits](https://github.com/dependabot/fetch-metadata/compare/v1.4.0...v1.5.0)

---
updated-dependencies:
- dependency-name: dependabot/fetch-metadata
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2023-05-22 19:06:02 +00:00
github-actions[bot] 95e0cb0ec2 Merge pull request #6 from beste/dependabot/github_actions/dependabot/fetch-metadata-1.4.0
github-actions(deps): bump dependabot/fetch-metadata from 1.3.6 to 1.4.0
2023-04-18 18:13:07 +00:00
dependabot[bot] 862f942124 github-actions(deps): bump dependabot/fetch-metadata from 1.3.6 to 1.4.0
Bumps [dependabot/fetch-metadata](https://github.com/dependabot/fetch-metadata) from 1.3.6 to 1.4.0.
- [Release notes](https://github.com/dependabot/fetch-metadata/releases)
- [Commits](https://github.com/dependabot/fetch-metadata/compare/v1.3.6...v1.4.0)

---
updated-dependencies:
- dependency-name: dependabot/fetch-metadata
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2023-04-18 18:12:52 +00:00
github-actions[bot] fdf9c1bd04 Merge pull request #5 from beste/dependabot/github_actions/dependabot/fetch-metadata-1.3.6
github-actions(deps): bump dependabot/fetch-metadata from 1.3.5 to 1.3.6
2023-01-24 18:09:52 +00:00
dependabot[bot] 1f72d04e67 github-actions(deps): bump dependabot/fetch-metadata from 1.3.5 to 1.3.6
Bumps [dependabot/fetch-metadata](https://github.com/dependabot/fetch-metadata) from 1.3.5 to 1.3.6.
- [Release notes](https://github.com/dependabot/fetch-metadata/releases)
- [Commits](https://github.com/dependabot/fetch-metadata/compare/v1.3.5...v1.3.6)

---
updated-dependencies:
- dependency-name: dependabot/fetch-metadata
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2023-01-24 18:09:36 +00:00
Jérôme Gamez 7004b55fcd Release 3.0.0 2022-11-26 19:03:05 +01:00
Jérôme Gamez ebd491dc6e Replace stella-maris/clock with psr/clock 2022-11-26 19:01:32 +01:00
Jérôme Gamez cf2cc16e66 Release 2.3.1 2022-11-25 19:24:28 +01:00
Jérôme Gamez 6ee3f318f8 Switch back to stella-maris/clock
This reverts commit a5e62affe0.
2022-11-25 19:22:56 +01:00
Jérôme Gamez 2ba952cc73 Release 2.3.0 2022-11-25 19:02:11 +01:00
Jérôme Gamez 2861280adc Re-introduce support for PHP ^8.0 (instead of only 8.1.x and 8.2.x) 2022-11-25 19:02:11 +01:00
Jérôme Gamez a5e62affe0 Switch to the now released psr/clock-interface 2022-11-25 18:08:55 +01:00
github-actions[bot] 5acec1192d Merge pull request #4 from beste/dependabot/github_actions/ramsey/composer-install-2
github-actions(deps): bump ramsey/composer-install from 1 to 2
2022-11-17 18:20:41 +00:00
dependabot[bot] fd2e55f899 github-actions(deps): bump ramsey/composer-install from 1 to 2
Bumps [ramsey/composer-install](https://github.com/ramsey/composer-install) from 1 to 2.
- [Release notes](https://github.com/ramsey/composer-install/releases)
- [Commits](https://github.com/ramsey/composer-install/compare/v1...v2)

---
updated-dependencies:
- dependency-name: ramsey/composer-install
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-11-17 18:20:16 +00:00
Jérôme Gamez 24b2fc5716 Release 2.2.0 2022-11-05 00:08:15 +01:00
github-actions[bot] 04017e5bc2 Merge pull request #3 from beste/dependabot/github_actions/actions/checkout-3
github-actions(deps): bump actions/checkout from 2 to 3
2022-11-04 23:05:44 +00:00
dependabot[bot] b47e76adf3 github-actions(deps): bump actions/checkout from 2 to 3
Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 3.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v2...v3)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-11-04 23:05:32 +00:00
Jérôme Gamez 4b16082f08 Set up Dependabot with auto-merge 2022-11-05 00:05:05 +01:00
Jérôme Gamez 317ed729ce Drop support for PHP <8.1 2022-11-05 00:03:47 +01:00
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
27 changed files with 411 additions and 203 deletions
-1
View File
@@ -6,4 +6,3 @@
/.gitignore export-ignore /.gitignore export-ignore
/phpstan.neon.dist export-ignore /phpstan.neon.dist export-ignore
/phpunit.xml.dist export-ignore /phpunit.xml.dist export-ignore
/psalm.xml.dist export-ignore
+15
View File
@@ -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:
- "dependencies"
+2
View File
@@ -0,0 +1,2 @@
# https://probot.github.io/apps/settings/
_extends: beste/.github
+10 -14
View File
@@ -4,26 +4,26 @@ on:
pull_request: pull_request:
push: push:
branches: branches:
- 'main' - '4.x'
jobs: jobs:
tests: tests:
name: PHP ${{ matrix.php }} name: PHP ${{ matrix.php }}
runs-on: ubuntu-20.04 runs-on: ubuntu-latest
strategy: strategy:
matrix: matrix:
php: php:
- "7.4" - "8.3"
- "8.0" - "8.4"
- "8.1" - "8.5"
dependencies: include:
- "lowest" - php: "8.6"
- "highest" composer-options: "--ignore-platform-req=php+"
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v2 uses: actions/checkout@v6
- name: Setup PHP - name: Setup PHP
uses: shivammathur/setup-php@v2 uses: shivammathur/setup-php@v2
with: with:
@@ -33,9 +33,8 @@ jobs:
env: env:
COMPOSER_TOKEN: ${{ secrets.GITHUB_TOKEN }} COMPOSER_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- uses: "ramsey/composer-install@v1" - uses: "ramsey/composer-install@v4"
with: with:
dependency-versions: "${{ matrix.dependencies }}"
composer-options: "${{ matrix.composer-options }}" composer-options: "${{ matrix.composer-options }}"
- name: Setup problem matchers for PHP - name: Setup problem matchers for PHP
@@ -44,9 +43,6 @@ jobs:
- name: Run PHPStan - name: Run PHPStan
run: vendor/bin/phpstan analyse --error-format=github run: vendor/bin/phpstan analyse --error-format=github
- name: Run Psalm
run: vendor/bin/psalm --output-format=github
- name: Setup Problem Matchers for PHPUnit - name: Setup Problem Matchers for PHPUnit
run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"
-1
View File
@@ -4,4 +4,3 @@
/composer.lock /composer.lock
/phpstan.neon /phpstan.neon
/phpunit.xml /phpunit.xml
/psalm.xml
+36 -3
View File
@@ -1,8 +1,41 @@
# CHANGELOG # CHANGELOG
## [Unreleased] ## 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`.
## 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
+37 -3
View File
@@ -4,7 +4,7 @@
[![Packagist PHP Version Support](https://img.shields.io/packagist/php-v/beste/clock)](https://packagist.org/packages/beste/clock) [![Packagist PHP Version Support](https://img.shields.io/packagist/php-v/beste/clock)](https://packagist.org/packages/beste/clock)
[![Tests](https://github.com/beste/clock/actions/workflows/tests.yml/badge.svg)](https://github.com/beste/clock/actions/workflows/tests.yml) [![Tests](https://github.com/beste/clock/actions/workflows/tests.yml/badge.svg)](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 ## Table of Contents
@@ -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
@@ -113,8 +114,8 @@ printf("\nAfter turning back the clock 5 minutes, it's %s", $frozenClock->now()-
### `MinuteClock` ### `MinuteClock`
In some cases, microseconds, milliseconds, or even seconds are too precise for some use cases - sometimes it's just In some cases, microseconds, milliseconds, or even seconds are too precise. Sometimes it is enough to know that
enough if something happened in the same minute. Using the minute something happened within the same minute. `MinuteClock` removes seconds and fractions of a second from another clock.
```php ```php
# examples/minute_clock.php # examples/minute_clock.php
@@ -136,7 +137,40 @@ 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`, you can wrap it in a
`WrappingClock` to make it a PSR-20 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
+5
View File
@@ -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.
+15 -19
View File
@@ -11,45 +11,41 @@
} }
], ],
"require": { "require": {
"php": "~7.4.0 || ~8.0.0 || ~8.1.0" "php": "^8.3",
"psr/clock": "^1.0"
}, },
"require-dev": { "require-dev": {
"phpstan/extension-installer": "^1.1", "phpstan/extension-installer": "^1.4.1",
"phpstan/phpstan": "^1.0", "phpstan/phpstan": "^2.1",
"phpstan/phpstan-phpunit": "^1.0", "phpstan/phpstan-phpunit": "^2.0",
"phpstan/phpstan-strict-rules": "^1.0", "phpstan/phpstan-strict-rules": "^2.0",
"phpunit/phpunit": "^9.5.10", "phpunit/phpunit": "^12.5"
"psalm/plugin-phpunit": "^0.16.0",
"vimeo/psalm": "^4.15"
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {
"Beste\\Clock\\": "src/Clock" "Beste\\Clock\\": "src"
}, }
"files": [
"src/psr-polyfill.php",
"src/Clock.php"
]
}, },
"autoload-dev": { "autoload-dev": {
"psr-4": { "psr-4": {
"Beste\\Clock\\Tests\\": "tests/Clock" "Beste\\Clock\\Tests\\": "tests"
} }
}, },
"provide": { "provide": {
"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": [
"@phpstan", "@phpstan",
"@psalm",
"@phpunit" "@phpunit"
], ],
"phpunit": "vendor/bin/phpunit", "phpunit": "vendor/bin/phpunit",
"phpstan": "vendor/bin/phpstan analyse", "phpstan": "vendor/bin/phpstan analyse"
"psalm": "vendor/bin/psalm"
} }
} }
+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
+7 -7
View File
@@ -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" <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" bootstrap="vendor/autoload.php"
cacheResultFile=".build/phpunit/phpunit.cache" cacheDirectory=".build/phpunit/cache"
beStrictAboutCoversAnnotation="true" beStrictAboutCoverageMetadata="true"
colors="true" colors="true"
> >
<testsuites> <testsuites>
@@ -12,10 +12,10 @@
</testsuite> </testsuite>
</testsuites> </testsuites>
<coverage> <source>
<include> <include>
<directory>src/Clock</directory> <directory>src</directory>
</include> </include>
</coverage> </source>
</phpunit> </phpunit>
-21
View File
@@ -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>
-12
View File
@@ -1,12 +0,0 @@
<?php
declare(strict_types=1);
namespace Beste;
use Psr\Clock\ClockInterface;
interface Clock extends ClockInterface
{
}
@@ -4,12 +4,11 @@ declare(strict_types=1);
namespace Beste\Clock; namespace Beste\Clock;
use Beste\Clock;
use DateTimeImmutable; use DateTimeImmutable;
use DateTimeZone; use DateTimeZone;
use Psr\Clock\ClockInterface; use Psr\Clock\ClockInterface;
final class FrozenClock implements Clock final class FrozenClock implements ClockInterface
{ {
private DateTimeImmutable $frozenAt; private DateTimeImmutable $frozenAt;
@@ -4,13 +4,13 @@ declare(strict_types=1);
namespace Beste\Clock; namespace Beste\Clock;
use Beste\Clock;
use DateTimeImmutable; use DateTimeImmutable;
use DateTimeZone; use DateTimeZone;
use InvalidArgumentException; use InvalidArgumentException;
use Psr\Clock\ClockInterface;
use Throwable; use Throwable;
final class LocalizedClock implements Clock final class LocalizedClock implements ClockInterface
{ {
private DateTimeZone $timeZone; private DateTimeZone $timeZone;
@@ -19,10 +19,7 @@ final class LocalizedClock implements Clock
$this->timeZone = $timeZone; $this->timeZone = $timeZone;
} }
/** public static function in(DateTimeZone|string $timeZone): self
* @param DateTimeZone|string $timeZone
*/
public static function in($timeZone): self
{ {
if (is_string($timeZone)) { if (is_string($timeZone)) {
try { try {
@@ -4,11 +4,10 @@ declare(strict_types=1);
namespace Beste\Clock; namespace Beste\Clock;
use Beste\Clock;
use DateTimeImmutable; use DateTimeImmutable;
use Psr\Clock\ClockInterface; use Psr\Clock\ClockInterface;
final class MinuteClock implements Clock final class MinuteClock implements ClockInterface
{ {
private ClockInterface $clock; private ClockInterface $clock;
@@ -4,10 +4,10 @@ declare(strict_types=1);
namespace Beste\Clock; namespace Beste\Clock;
use Beste\Clock;
use DateTimeImmutable; use DateTimeImmutable;
use Psr\Clock\ClockInterface;
final class SystemClock implements Clock final class SystemClock implements ClockInterface
{ {
private function __construct() private function __construct()
{ {
+2 -2
View File
@@ -4,11 +4,11 @@ declare(strict_types=1);
namespace Beste\Clock; namespace Beste\Clock;
use Beste\Clock;
use DateTimeImmutable; use DateTimeImmutable;
use DateTimeZone; use DateTimeZone;
use Psr\Clock\ClockInterface;
final class UTCClock implements Clock final class UTCClock implements ClockInterface
{ {
private DateTimeZone $timeZone; private DateTimeZone $timeZone;
+69
View File
@@ -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();
}
}
-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;
}
@@ -6,21 +6,18 @@ namespace Beste\Clock\Tests;
use Beste\Clock\FrozenClock; use Beste\Clock\FrozenClock;
use DateTimeImmutable; use DateTimeImmutable;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Psr\Clock\ClockInterface; use Psr\Clock\ClockInterface;
/** /**
* @internal * @internal
* @coversDefaultClass \Beste\Clock\FrozenClock
*/ */
#[CoversClass(FrozenClock::class)]
final class FrozenClockTest extends TestCase final class FrozenClockTest extends TestCase
{ {
/** #[Test]
* @test
* @covers ::__construct
* @covers ::at
* @covers ::now
*/
public function itFreezesTime(): void public function itFreezesTime(): void
{ {
$now = new DateTimeImmutable('2021-03-21 22:16:00'); $now = new DateTimeImmutable('2021-03-21 22:16:00');
@@ -29,14 +26,7 @@ final class FrozenClockTest extends TestCase
self::assertEquals($now, $clock->now()); self::assertEquals($now, $clock->now());
} }
/** #[Test]
* @test
*
* @uses \Beste\Clock\FrozenClock::__construct
* @uses \Beste\Clock\FrozenClock::fromUTC()
*
* @covers ::now()
*/
public function itReturnsAnEqualNowButNotTheSame(): void public function itReturnsAnEqualNowButNotTheSame(): void
{ {
$clock = FrozenClock::fromUTC(); $clock = FrozenClock::fromUTC();
@@ -48,14 +38,7 @@ final class FrozenClockTest extends TestCase
self::assertNotSame($first, $second); self::assertNotSame($first, $second);
} }
/** #[Test]
* @test
*
* @uses \Beste\Clock\FrozenClock::__construct
* @uses \Beste\Clock\FrozenClock::now
*
* @covers ::withNowFrom
*/
public function itFreezesTimeFromAnotherClock(): void public function itFreezesTimeFromAnotherClock(): void
{ {
$now = new DateTimeImmutable('now'); $now = new DateTimeImmutable('now');
@@ -71,14 +54,7 @@ final class FrozenClockTest extends TestCase
self::assertEquals($now, $frozenClock->now()); self::assertEquals($now, $frozenClock->now());
} }
/** #[Test]
* @test
*
* @uses \Beste\Clock\FrozenClock::__construct
* @uses \Beste\Clock\FrozenClock::now
*
* @covers ::fromUTC
*/
public function itFreezesTheCurrentUTCTime(): void public function itFreezesTheCurrentUTCTime(): void
{ {
$clock = FrozenClock::fromUTC(); $clock = FrozenClock::fromUTC();
@@ -87,15 +63,7 @@ final class FrozenClockTest extends TestCase
self::assertEquals($clock->now(), $clock->now()); self::assertEquals($clock->now(), $clock->now());
} }
/** #[Test]
* @test
*
* @uses \Beste\Clock\FrozenClock::__construct
* @uses \Beste\Clock\FrozenClock::at
* @uses \Beste\Clock\FrozenClock::now
*
* @covers ::setTo
*/
public function itCanBeSet(): void public function itCanBeSet(): void
{ {
$now = new DateTimeImmutable('2021-03-21 18:18:18'); $now = new DateTimeImmutable('2021-03-21 18:18:18');
@@ -7,18 +7,17 @@ namespace Beste\Clock\Tests;
use Beste\Clock\LocalizedClock; use Beste\Clock\LocalizedClock;
use DateTimeZone; use DateTimeZone;
use InvalidArgumentException; use InvalidArgumentException;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
/** /**
* @internal * @internal
* @coversDefaultClass \Beste\Clock\LocalizedClock
*/ */
#[CoversClass(LocalizedClock::class)]
final class LocalizedClockTest extends TestCase final class LocalizedClockTest extends TestCase
{ {
/** #[Test]
* @test
* @covers ::in
*/
public function itRejectsAnInvalidTimeZone(): void public function itRejectsAnInvalidTimeZone(): void
{ {
$this->expectException(InvalidArgumentException::class); $this->expectException(InvalidArgumentException::class);
@@ -26,12 +25,7 @@ final class LocalizedClockTest extends TestCase
LocalizedClock::in('invalid'); LocalizedClock::in('invalid');
} }
/** #[Test]
* @test
* @covers ::__construct
* @covers ::in
* @covers ::now
*/
public function itUsesTheGivenTimeZone(): void public function itUsesTheGivenTimeZone(): void
{ {
$timeZone = new DateTimeZone('Asia/Bangkok'); $timeZone = new DateTimeZone('Asia/Bangkok');
@@ -41,12 +35,7 @@ final class LocalizedClockTest extends TestCase
self::assertSame($timeZone->getName(), $now->getTimezone()->getName()); self::assertSame($timeZone->getName(), $now->getTimezone()->getName());
} }
/** #[Test]
* @test
* @covers ::__construct
* @covers ::in
* @covers ::now
*/
public function itAcceptsTheTimeZoneAsAString(): void public function itAcceptsTheTimeZoneAsAString(): void
{ {
$timeZone = 'Pacific/Guam'; $timeZone = 'Pacific/Guam';
@@ -6,30 +6,23 @@ namespace Beste\Clock\Tests;
use Beste\Clock\MinuteClock; use Beste\Clock\MinuteClock;
use DateTimeImmutable; use DateTimeImmutable;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Psr\Clock\ClockInterface; use Psr\Clock\ClockInterface;
/** /**
* @internal * @internal
* @coversDefaultClass \Beste\Clock\MinuteClock
*/ */
#[CoversClass(MinuteClock::class)]
final class MinuteClockTest extends TestCase final class MinuteClockTest extends TestCase
{ {
/** #[Test]
* @test #[DataProvider('dateTimeStringProvider')]
*
* @covers ::__construct
* @covers ::wrapping
* @covers ::now
* @covers ::floor
*
* @uses \Beste\Clock\FrozenClock
*
* @dataProvider dateTimeStringProvider
*/
public function itDiscardsEverythingSmallerThanMinutes(string $format, string $input, string $expected): void 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)); $mockClock->method('now')->willReturn(new DateTimeImmutable($input));
// Just to be sure that the test input is valid ^^ // Just to be sure that the test input is valid ^^
@@ -43,7 +36,7 @@ final class MinuteClockTest extends TestCase
/** /**
* @return array<string, string[]> * @return array<string, string[]>
*/ */
public function dateTimeStringProvider(): array public static function dateTimeStringProvider(): array
{ {
return [ return [
'microseconds' => [ 'microseconds' => [
@@ -5,12 +5,14 @@ declare(strict_types=1);
namespace Beste\Clock\Tests; namespace Beste\Clock\Tests;
use Beste\Clock\SystemClock; use Beste\Clock\SystemClock;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
/** /**
* @internal * @internal
* @coversDefaultClass \Beste\Clock\SystemClock
*/ */
#[CoversClass(SystemClock::class)]
final class SystemClockTest extends TestCase final class SystemClockTest extends TestCase
{ {
private string $defaultTimeZone; private string $defaultTimeZone;
@@ -25,16 +27,10 @@ final class SystemClockTest extends TestCase
date_default_timezone_set($this->defaultTimeZone); date_default_timezone_set($this->defaultTimeZone);
} }
/** #[Test]
* @test
*
* @covers ::__construct
* @covers ::create
* @covers ::now
*/
public function itUsesTheSystemTimeZone(): void public function itUsesTheSystemTimeZone(): void
{ {
$timeZone = 'Europe/Berlin'; $timeZone = $this->defaultTimeZone === 'Europe/Berlin' ? 'UTC' : 'Europe/Berlin';
self::assertNotSame($timeZone, $this->defaultTimeZone); self::assertNotSame($timeZone, $this->defaultTimeZone);
date_default_timezone_set($timeZone); date_default_timezone_set($timeZone);
@@ -5,12 +5,14 @@ declare(strict_types=1);
namespace Beste\Clock\Tests; namespace Beste\Clock\Tests;
use Beste\Clock\UTCClock; use Beste\Clock\UTCClock;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
/** /**
* @internal * @internal
* @coversDefaultClass \Beste\Clock\UTCClock
*/ */
#[CoversClass(UTCClock::class)]
final class UTCClockTest extends TestCase final class UTCClockTest extends TestCase
{ {
private string $defaultTimeZone; private string $defaultTimeZone;
@@ -25,13 +27,7 @@ final class UTCClockTest extends TestCase
date_default_timezone_set($this->defaultTimeZone); date_default_timezone_set($this->defaultTimeZone);
} }
/** #[Test]
* @test
*
* @covers ::__construct
* @covers ::create
* @covers ::now
*/
public function itUsesUTCRegardlessOfTheSystemTimeZone(): void public function itUsesUTCRegardlessOfTheSystemTimeZone(): void
{ {
date_default_timezone_set('Europe/Berlin'); date_default_timezone_set('Europe/Berlin');
+147
View File
@@ -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);
}
}