From 999616818da38e548a250e4c100afa3b9be88646 Mon Sep 17 00:00:00 2001 From: odan Date: Sun, 25 Oct 2026 17:35:03 +0100 Subject: [PATCH 1/2] Add getDateTime and findDateTime --- src/ArrayReader.php | 73 +++++++++++++++++++++++ tests/ArrayReaderTest.php | 119 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 192 insertions(+) diff --git a/src/ArrayReader.php b/src/ArrayReader.php index aa039c5..31c915d 100644 --- a/src/ArrayReader.php +++ b/src/ArrayReader.php @@ -3,6 +3,8 @@ namespace Selective\ArrayReader; use Cake\Chronos\Chronos; +use DateTimeImmutable; +use DateTimeInterface; use InvalidArgumentException; /** @@ -237,6 +239,46 @@ public function findBool(string $key, ?bool $default = null): ?bool return (bool)$value; } + /** + * Get value as DateTimeImmutable. + * + * @param string $key The key + * @param DateTimeImmutable|null $default The default value + * + * @throws InvalidArgumentException + * + * @return DateTimeImmutable The value + */ + public function getDateTime(string $key, ?DateTimeImmutable $default = null): DateTimeImmutable + { + $value = $this->find($key, $default); + + if ($this->isNullOrBlank($value)) { + throw new InvalidArgumentException(sprintf('No value found for key "%s"', $key)); + } + + return $this->toDateTimeImmutable($key, $value); + } + + /** + * Get value as DateTimeImmutable or null. + * + * @param string $key The key + * @param DateTimeImmutable|null $default The default value + * + * @return DateTimeImmutable|null The value + */ + public function findDateTime(string $key, ?DateTimeImmutable $default = null): ?DateTimeImmutable + { + $value = $this->find($key, $default); + + if ($this->isNullOrBlank($value)) { + return null; + } + + return $this->toDateTimeImmutable($key, $value); + } + /** * Get value as Chronos. * @@ -377,4 +419,35 @@ private function isNullOrBlank(mixed $value): bool { return $value === null || $value === ''; } + + /** + * Cast a value to DateTimeImmutable. + * + * @param string $key The key + * @param mixed $value The value + * + * @throws InvalidArgumentException + * + * @return DateTimeImmutable The date + */ + private function toDateTimeImmutable(string $key, mixed $value): DateTimeImmutable + { + if ($value instanceof DateTimeImmutable) { + return $value; + } + + if ($value instanceof DateTimeInterface) { + return DateTimeImmutable::createFromInterface($value); + } + + if (is_int($value)) { + return new DateTimeImmutable('@' . $value); + } + + if (!is_string($value)) { + throw new InvalidArgumentException(sprintf('Invalid date value for key "%s"', $key)); + } + + return new DateTimeImmutable($value); + } } diff --git a/tests/ArrayReaderTest.php b/tests/ArrayReaderTest.php index f92df94..3b7c69f 100644 --- a/tests/ArrayReaderTest.php +++ b/tests/ArrayReaderTest.php @@ -2,6 +2,9 @@ namespace Selective\ArrayReader\Test; +use Cake\Chronos\Chronos; +use DateTime; +use DateTimeImmutable; use InvalidArgumentException; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; @@ -429,4 +432,120 @@ public static function providerFindArray(): array [[['key' => 'value'], ['key2' => 'value2']], 2, null, null], ]; } + + /** + * Test. + * + * @param mixed $data The data + * @param string $key The lookup key + * @param mixed $default The default value + * @param mixed $expected The expected value + * + * @return void + */ + #[DataProvider('providerGetDateTime')] + public function testGetDateTime($data, string $key, $default, $expected): void + { + $reader = new ArrayReader($data); + + $this->assertEquals($expected, $reader->findDateTime($key, $default)); + $this->assertEquals($expected, $reader->getDateTime($key, $default)); + } + + /** + * Provider. + * + * @return array[] The test data + */ + public static function providerGetDateTime(): array + { + $date = new DateTimeImmutable('2020-01-02T03:04:05+00:00'); + $chronos = new Chronos('2020-01-02T03:04:05+00:00'); + + return [ + [['created' => '2020-01-02T03:04:05+00:00'], 'created', null, $date], + [['created' => $date], 'created', null, $date], + [['created' => new DateTime('2020-01-02T03:04:05+00:00')], 'created', null, $date], + [['created' => 1577934245], 'created', null, $date], + [['user' => ['created' => '2020-01-02T03:04:05+00:00']], 'user.created', null, $date], + [['created' => $chronos], 'created', null, $chronos], + [[], 'missing', $date, $date], + [['created' => null], 'created', $date, $date], + ]; + } + + /** + * Test. + * + * @param mixed $data The data + * @param string $key The lookup key + * @param mixed $default The default value + * + * @return void + */ + #[DataProvider('providerGetDateTimeError')] + public function testGetDateTimeError($data, string $key, $default): void + { + $reader = new ArrayReader($data); + + $this->expectException(InvalidArgumentException::class); + + $reader->getDateTime($key, $default); + } + + /** + * Provider. + * + * @return array[] The test data + */ + public static function providerGetDateTimeError(): array + { + return [ + [['created' => null], 'created', null], + [['created' => ''], 'created', null], + [[], 'missing', null], + [['created' => ['not-a-date']], 'created', null], + [['user' => ['created' => null]], 'user.created', null], + ]; + } + + /** + * Test. + * + * @param mixed $data The data + * @param string $key The lookup key + * @param mixed $default The default value + * @param mixed $expected The expected value + * + * @return void + */ + #[DataProvider('providerFindDateTime')] + public function testFindDateTime($data, string $key, $default, $expected): void + { + $reader = new ArrayReader($data); + + $this->assertEquals($expected, $reader->findDateTime($key, $default)); + } + + /** + * Provider. + * + * @return array[] The test data + */ + public static function providerFindDateTime(): array + { + $date = new DateTimeImmutable('2020-01-02T03:04:05+00:00'); + + return [ + [['created' => '2020-01-02T03:04:05+00:00'], 'created', null, $date], + [['created' => null], 'created', null, null], + [['created' => ''], 'created', null, null], + [['created' => ''], 'created', $date, null], + [[], 'missing', null, null], + [[], 'missing', $date, $date], + [['created' => null], 'created', $date, $date], + [['user' => ['created' => '2020-01-02T03:04:05+00:00']], 'user.created', null, $date], + [['user' => ['created' => null]], 'user.created', null, null], + ]; + } } From 2dabf9c0139c22982a6b61048248d5da76d39903 Mon Sep 17 00:00:00 2001 From: odan Date: Sun, 25 Oct 2026 17:35:09 +0100 Subject: [PATCH 2/2] Add getDateTime and findDateTime --- .github/workflows/build.yml | 2 +- README.md | 2 +- composer.json | 2 +- src/ArrayReader.php | 4 ++-- tests/ArrayReaderTest.php | 4 ++++ 5 files changed, 9 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 429673a..36bb68e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,7 +8,7 @@ jobs: strategy: matrix: operating-system: [ ubuntu-latest ] - php-versions: [ '8.2', '8.3', '8.4', '8.5' ] + php-versions: [ '8.4', '8.5' ] name: PHP ${{ matrix.php-versions }} Test on ${{ matrix.operating-system }} steps: diff --git a/README.md b/README.md index 019ac65..b147ba5 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ A strictly typed array reader for PHP. ## Requirements -* PHP 8.2 - 8.5 +* PHP 8.4 - 8.5 ## Installation diff --git a/composer.json b/composer.json index 1b9045f..6e5eab3 100644 --- a/composer.json +++ b/composer.json @@ -13,7 +13,7 @@ ], "homepage": "https://github.com/selective-php/array-reader", "require": { - "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" + "php": "~8.4.0 || ~8.5.0" }, "require-dev": { "cakephp/chronos": "^2 || ^3", diff --git a/src/ArrayReader.php b/src/ArrayReader.php index 31c915d..93de210 100644 --- a/src/ArrayReader.php +++ b/src/ArrayReader.php @@ -440,8 +440,8 @@ private function toDateTimeImmutable(string $key, mixed $value): DateTimeImmutab return DateTimeImmutable::createFromInterface($value); } - if (is_int($value)) { - return new DateTimeImmutable('@' . $value); + if (is_int($value) || (is_float($value) && is_finite($value))) { + return DateTimeImmutable::createFromTimestamp($value); } if (!is_string($value)) { diff --git a/tests/ArrayReaderTest.php b/tests/ArrayReaderTest.php index 3b7c69f..32bdc28 100644 --- a/tests/ArrayReaderTest.php +++ b/tests/ArrayReaderTest.php @@ -460,6 +460,7 @@ public function testGetDateTime($data, string $key, $default, $expected): void public static function providerGetDateTime(): array { $date = new DateTimeImmutable('2020-01-02T03:04:05+00:00'); + $fraction = new DateTimeImmutable('2020-01-02T03:04:05.500000+00:00'); $chronos = new Chronos('2020-01-02T03:04:05+00:00'); return [ @@ -467,6 +468,7 @@ public static function providerGetDateTime(): array [['created' => $date], 'created', null, $date], [['created' => new DateTime('2020-01-02T03:04:05+00:00')], 'created', null, $date], [['created' => 1577934245], 'created', null, $date], + [['created' => 1577934245.5], 'created', null, $fraction], [['user' => ['created' => '2020-01-02T03:04:05+00:00']], 'user.created', null, $date], [['created' => $chronos], 'created', null, $chronos], [[], 'missing', $date, $date], @@ -505,6 +507,8 @@ public static function providerGetDateTimeError(): array [['created' => ''], 'created', null], [[], 'missing', null], [['created' => ['not-a-date']], 'created', null], + [['created' => INF], 'created', null], + [['created' => NAN], 'created', null], [['user' => ['created' => null]], 'user.created', null], ]; }