diff --git a/.cs.php b/.cs.php
index e983838..c0e1811 100644
--- a/.cs.php
+++ b/.cs.php
@@ -1,42 +1,82 @@
setUsingCache(false)
->setRiskyAllowed(true)
- //->setCacheFile(__DIR__ . '/.php_cs.cache')
- ->setRules([
- '@PSR1' => true,
- '@PSR2' => true,
- '@Symfony' => true,
- 'psr4' => true,
- // custom rules
- 'align_multiline_comment' => ['comment_type' => 'phpdocs_only'], // psr-5
- 'phpdoc_to_comment' => false,
- 'array_indentation' => true,
- 'array_syntax' => ['syntax' => 'short'],
- 'cast_spaces' => ['space' => 'none'],
- 'concat_space' => ['spacing' => 'one'],
- 'compact_nullable_typehint' => true,
- 'declare_equal_normalize' => ['space' => 'single'],
- 'increment_style' => ['style' => 'post'],
- 'list_syntax' => ['syntax' => 'long'],
- 'no_short_echo_tag' => true,
- 'phpdoc_add_missing_param_annotation' => ['only_untyped' => false],
- 'phpdoc_align' => false,
- 'phpdoc_no_empty_return' => false,
- 'phpdoc_order' => true, // psr-5
- 'phpdoc_no_useless_inheritdoc' => false,
- 'protected_to_private' => false,
- 'yoda_style' => false,
- 'method_argument_space' => ['on_multiline' => 'ensure_fully_multiline'],
- 'ordered_imports' => [
- 'sort_algorithm' => 'alpha',
- 'imports_order' => ['class', 'const', 'function']
- ],
- ])
- ->setFinder(PhpCsFixer\Finder::create()
- ->in(__DIR__ . '/src')
- ->in(__DIR__ . '/tests')
- ->name('*.php')
- ->ignoreDotFiles(true)
- ->ignoreVCS(true));
+ ->setRules(
+ [
+ '@PSR1' => true,
+ '@PSR2' => true,
+ // custom rules
+ 'psr_autoloading' => true,
+ 'align_multiline_comment' => ['comment_type' => 'phpdocs_only'], // psr-5
+ 'phpdoc_to_comment' => false,
+ 'no_superfluous_phpdoc_tags' => false,
+ 'array_indentation' => true,
+ 'array_syntax' => ['syntax' => 'short'],
+ 'cast_spaces' => ['space' => 'none'],
+ 'concat_space' => ['spacing' => 'one'],
+ 'compact_nullable_type_declaration' => true,
+ 'declare_equal_normalize' => ['space' => 'single'],
+ 'general_phpdoc_annotation_remove' => [
+ 'annotations' => [
+ 'author',
+ 'package',
+ ],
+ ],
+ 'increment_style' => ['style' => 'post'],
+ 'list_syntax' => ['syntax' => 'short'],
+ 'echo_tag_syntax' => ['format' => 'long'],
+ 'phpdoc_add_missing_param_annotation' => ['only_untyped' => false],
+ 'phpdoc_align' => false,
+ 'phpdoc_no_empty_return' => false,
+ 'phpdoc_order' => true, // psr-5
+ 'phpdoc_no_useless_inheritdoc' => false,
+ 'protected_to_private' => false,
+ 'yoda_style' => [
+ 'equal' => false,
+ 'identical' => false,
+ 'less_and_greater' => false
+ ],
+ 'method_argument_space' => ['on_multiline' => 'ensure_fully_multiline'],
+ 'ordered_imports' => [
+ 'sort_algorithm' => 'alpha',
+ 'imports_order' => ['class', 'const', 'function'],
+ ],
+ 'single_line_throw' => false,
+ 'declare_strict_types' => false,
+ 'blank_line_between_import_groups' => true,
+ 'fully_qualified_strict_types' => true,
+ 'no_null_property_initialization' => false,
+ 'nullable_type_declaration_for_default_null_value' => false,
+ 'operator_linebreak' => [
+ 'only_booleans' => true,
+ 'position' => 'beginning',
+ ],
+ 'global_namespace_import' => [
+ 'import_classes' => true,
+ 'import_constants' => null,
+ 'import_functions' => null
+ ],
+ 'class_definition' => [
+ 'space_before_parenthesis' => true,
+ ],
+ 'trailing_comma_in_multiline' => [
+ 'after_heredoc' => true,
+ 'elements' => ['array_destructuring', 'arrays', 'match']
+ ],
+ 'function_declaration' => [
+ 'closure_fn_spacing' => 'none',
+ ]
+ ]
+ )
+ ->setFinder(
+ PhpCsFixer\Finder::create()
+ ->in(__DIR__ . '/src')
+ ->in(__DIR__ . '/tests')
+ ->name('*.php')
+ ->ignoreDotFiles(true)
+ ->ignoreVCS(true)
+ );
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..a628c91
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,22 @@
+root = true
+
+[*]
+charset = utf-8
+indent_style = space
+end_of_line = lf
+
+[composer.json]
+indent_size = 4
+
+[*.js]
+indent_size = 4
+
+[*.neon]
+indent_size = 4
+indent_style = tab
+
+[*.xml]
+indent_size = 4
+
+[*.yml]
+indent_size = 2
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 0000000..36bb68e
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,51 @@
+name: build
+
+on: [ push, pull_request ]
+
+jobs:
+ run:
+ runs-on: ${{ matrix.operating-system }}
+ strategy:
+ matrix:
+ operating-system: [ ubuntu-latest ]
+ php-versions: [ '8.4', '8.5' ]
+ name: PHP ${{ matrix.php-versions }} Test on ${{ matrix.operating-system }}
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v1
+
+ - name: Setup PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: ${{ matrix.php-versions }}
+ extensions: mbstring, intl, zip
+
+ - name: Check PHP Version
+ run: php -v
+
+ - name: Check Composer Version
+ run: composer -V
+
+ - name: Check PHP Extensions
+ run: php -m
+
+ - name: Validate composer.json and composer.lock
+ run: composer validate
+
+ - name: Install dependencies
+ run: composer install --prefer-dist --no-progress --no-suggest
+
+ - name: Run PHP CodeSniffer
+ run: composer sniffer:check
+
+ - name: Run PHPStan
+ run: composer stan
+
+ - name: Run tests
+ if: ${{ matrix.php-versions != '8.4' }}
+ run: composer test
+
+ - name: Run tests with coverage
+ if: ${{ matrix.php-versions == '8.4' }}
+ run: composer test:coverage
diff --git a/.gitignore b/.gitignore
index a27d9ba..a4689b6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,4 +2,6 @@
composer.lock
nbproject/
vendor/
-build/
\ No newline at end of file
+build/
+.phpunit.cache/
+.phpunit.result.cache
\ No newline at end of file
diff --git a/.scrutinizer.yml b/.scrutinizer.yml
deleted file mode 100644
index d3ff45f..0000000
--- a/.scrutinizer.yml
+++ /dev/null
@@ -1,40 +0,0 @@
-filter:
- paths: ["src/*"]
- excluded_paths: ["vendor/*", "tests/*"]
-
-checks:
- php:
- code_rating: true
- duplication: true
-
-tools:
- external_code_coverage: false
-
-build:
- environment:
- php: 7.2
- mysql: false
- node: false
- postgresql: false
- mongodb: false
- elasticsearch: false
- redis: false
- memcached: false
- neo4j: false
- rabbitmq: false
- nodes:
- analysis:
- tests:
- override:
- - php-scrutinizer-run
- dependencies:
- before:
- - composer self-update
- - composer update --no-interaction --prefer-dist --no-progress
- tests:
- before:
- -
- command: composer test-coverage
- coverage:
- file: 'build/logs/clover.xml'
- format: 'clover'
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 45adfdd..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,21 +0,0 @@
-language: php
-
-php:
- - 7.0
- - 7.1
- - 7.2
- - 7.3
-
-dist: xenial
-
-cache:
- directories:
- - $HOME/.composer/cache
-
-before_script:
- - composer self-update
- - composer install --no-interaction --prefer-dist --no-progress
- - cd $TRAVIS_BUILD_DIR
-
-script:
- - composer check-all
diff --git a/LICENSE b/LICENSE
index 30d7b83..2013f40 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
The MIT License (MIT)
-Copyright (c) 2019 odan
+Copyright (c) 2026 odan
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/README.md b/README.md
index 6effcd4..b147ba5 100644
--- a/README.md
+++ b/README.md
@@ -2,17 +2,14 @@
A strictly typed array reader for PHP.
-[](https://packagist.org/packages/selective/array-reader)
-[](LICENSE.md)
-[](https://travis-ci.org/selective-php/array-reader)
-[](https://scrutinizer-ci.com/g/selective-php/array-reader/code-structure)
-[](https://scrutinizer-ci.com/g/selective-php/array-reader/?branch=master)
-[](https://packagist.org/packages/selective/array-reader/stats)
-
+[](https://packagist.org/packages/selective/array-reader)
+[](LICENSE)
+[](https://github.com/selective-php/array-reader/actions)
+[](https://packagist.org/packages/selective/array-reader/stats)
## Requirements
-* PHP 7.0+
+* PHP 8.4 - 8.5
## Installation
@@ -51,9 +48,29 @@ $arrayReader = new ArrayReader([
echo $arrayReader->getString('key1.key2.key3');
```
+## Better Code Quality
+
+Converting complex data with simple PHP works by using a lot of type casting and `if` conditions etc.
+This leads to very high cyclomatic complexity and nesting depth, and thus poor "code rating".
+
+**Before**: Conditions: 10, Paths: 512, CRAP Score: 10
+
+ Click to expand!
+
+
+
+**After**: Conditions: 1, Paths: 1, CRAP Score: 1
+
+ Click to expand!
+
+
+
## Similar libraries
+* https://github.com/michaelpetri/typed-input
* https://github.com/codeliner/array-reader
+* https://github.com/adbario/php-dot-notation
+* https://symfony.com/doc/current/components/property_access.html
## License
diff --git a/composer.json b/composer.json
index 641abdb..6e5eab3 100644
--- a/composer.json
+++ b/composer.json
@@ -1,7 +1,8 @@
{
"name": "selective/array-reader",
- "type": "library",
"description": "A strictly typed array reader",
+ "license": "MIT",
+ "type": "library",
"keywords": [
"array",
"reader",
@@ -11,45 +12,52 @@
"strong"
],
"homepage": "https://github.com/selective-php/array-reader",
- "license": "MIT",
"require": {
- "php": "^7.0",
- "cakephp/chronos": "^1.2|^2"
+ "php": "~8.4.0 || ~8.5.0"
},
"require-dev": {
- "overtrue/phplint": "^1.1",
- "phpstan/phpstan-shim": "^0.9|^0.10|^0.11",
- "phpunit/phpunit": "^6|^7",
- "squizlabs/php_codesniffer": "^3.4"
+ "cakephp/chronos": "^2 || ^3",
+ "friendsofphp/php-cs-fixer": "^3",
+ "phpstan/phpstan": "^1 || ^2",
+ "phpunit/phpunit": "^11",
+ "squizlabs/php_codesniffer": "^3"
},
- "scripts": {
- "test": "phpunit --configuration phpunit.xml",
- "test-coverage": "phpunit --configuration phpunit.xml --coverage-clover build/logs/clover.xml --coverage-html build/coverage",
- "check-style": "phpcs --standard=phpcs.xml",
- "fix-style": "phpcbf --standard=phpcs.xml",
- "phpstan": "phpstan analyse src tests --level=max -c phpstan.neon --no-progress",
- "lint": "phplint ./ --exclude=vendor --no-interaction --no-cache",
- "install-cs": "php -r \"@mkdir('build'); copy('https://cs.symfony.com/download/php-cs-fixer-v2.phar', 'build/php-cs-fixer-v2.phar');\"",
- "fix-cs": "php build/php-cs-fixer-v2.phar fix --config=.cs.php",
- "check-cs": "php build/php-cs-fixer-v2.phar fix --dry-run --format=txt --verbose --diff --diff-format=udiff --config=.cs.php",
- "check-all": [
- "@lint",
- "@check-style",
- "@phpstan",
- "@test-coverage"
- ]
+ "suggest": {
+ "cakephp/chronos": "Required to use ArrayReader::getChronos() and ArrayReader::findChronos()"
},
"autoload": {
"psr-4": {
- "Selective\\ArrayReader\\": "src"
+ "Selective\\ArrayReader\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
- "Selective\\ArrayReader\\Test\\": "tests"
+ "Selective\\ArrayReader\\Test\\": "tests/"
}
},
"config": {
"sort-packages": true
+ },
+ "scripts": {
+ "cs:check": [
+ "php-cs-fixer fix --dry-run --format=txt --verbose --diff --config=.cs.php --ansi --allow-unsupported-php-version=yes"
+ ],
+ "cs:fix": [
+ "php-cs-fixer fix --config=.cs.php --ansi --verbose --allow-unsupported-php-version=yes"
+ ],
+ "sniffer:check": "phpcs --standard=phpcs.xml",
+ "sniffer:fix": "phpcbf --standard=phpcs.xml",
+ "stan": "phpstan analyse -c phpstan.neon --no-progress --ansi",
+ "test": "phpunit --configuration phpunit.xml --do-not-cache-result --colors=always --display-warnings --display-deprecations --no-coverage",
+ "test:all": [
+ "@cs:check",
+ "@sniffer:check",
+ "@stan",
+ "@test"
+ ],
+ "test:coverage": [
+ "@putenv XDEBUG_MODE=coverage",
+ "phpunit --configuration phpunit.xml --do-not-cache-result --colors=always --display-warnings --display-deprecations --coverage-clover build/coverage/clover.xml --coverage-html build/coverage --coverage-text"
+ ]
}
}
diff --git a/phpcs.xml b/phpcs.xml
index c825571..f2f42d9 100644
--- a/phpcs.xml
+++ b/phpcs.xml
@@ -9,27 +9,8 @@
./src
./tests
-
-
+
-
-
- warning
- */tests/
-
-
- warning
-
-
- warning
- */config/
-
-
- warning
-
-
- warning
-
warning
diff --git a/phpstan.neon b/phpstan.neon
index e69de29..5045b69 100644
--- a/phpstan.neon
+++ b/phpstan.neon
@@ -0,0 +1,5 @@
+parameters:
+ level: 8
+ reportUnmatchedIgnoredErrors: false
+ paths:
+ - src
\ No newline at end of file
diff --git a/phpunit.xml b/phpunit.xml
index e3d5d55..7b7ad74 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -1,21 +1,24 @@
-
+ xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/11.5/phpunit.xsd"
+ cacheDirectory=".phpunit.cache"
+ backupStaticProperties="false">
+
tests
-
-
+
+
src
-
- vendor
- build
-
-
-
+
+
+ vendor
+ build
+
+
diff --git a/src/ArrayReader.php b/src/ArrayReader.php
index b52ee6f..93de210 100644
--- a/src/ArrayReader.php
+++ b/src/ArrayReader.php
@@ -3,7 +3,8 @@
namespace Selective\ArrayReader;
use Cake\Chronos\Chronos;
-use Exception;
+use DateTimeImmutable;
+use DateTimeInterface;
use InvalidArgumentException;
/**
@@ -12,20 +13,32 @@
final class ArrayReader
{
/**
- * @var array The data
+ * @var array
*/
- private $data;
+ private array $data;
/**
- * Constructor.
+ * The constructor.
*
- * @param array $data Data
+ * @param array $data Data
*/
public function __construct(array $data = [])
{
$this->data = $data;
}
+ /**
+ * Crate instance from array.
+ *
+ * @param array $data The data
+ *
+ * @return self The new instance
+ */
+ public static function createFromArray(array $data = []): self
+ {
+ return new static($data);
+ }
+
/**
* Get value as integer.
*
@@ -36,7 +49,7 @@ public function __construct(array $data = [])
*
* @return int The value
*/
- public function getInt(string $key, int $default = null): int
+ public function getInt(string $key, ?int $default = null): int
{
$value = $this->find($key, $default);
@@ -55,7 +68,7 @@ public function getInt(string $key, int $default = null): int
*
* @return int|null The value
*/
- public function findInt(string $key, int $default = null)
+ public function findInt(string $key, ?int $default = null): ?int
{
$value = $this->find($key, $default);
@@ -76,7 +89,7 @@ public function findInt(string $key, int $default = null)
*
* @return string The value
*/
- public function getString(string $key, string $default = null): string
+ public function getString(string $key, ?string $default = null): string
{
$value = $this->find($key, $default);
@@ -91,11 +104,11 @@ public function getString(string $key, string $default = null): string
* Get value as string or null.
*
* @param string $key The key
- * @param string $default The default value
+ * @param string|null $default The default value
*
* @return string|null The value
*/
- public function findString(string $key, string $default = null)
+ public function findString(string $key, ?string $default = null): ?string
{
$value = $this->find($key, $default);
@@ -110,13 +123,13 @@ public function findString(string $key, string $default = null)
* Get value as array.
*
* @param string $key The key
- * @param array|null $default The default value
+ * @param array|null $default The default value
*
* @throws InvalidArgumentException
*
- * @return array The value
+ * @return array The value
*/
- public function getArray(string $key, array $default = null): array
+ public function getArray(string $key, ?array $default = null): array
{
$value = $this->find($key, $default);
@@ -131,11 +144,11 @@ public function getArray(string $key, array $default = null): array
* Get value as array or null.
*
* @param string $key The key
- * @param array $default The default value
+ * @param array|null $default The default value
*
- * @return array|null The value
+ * @return array|null The value
*/
- public function findArray(string $key, array $default = null)
+ public function findArray(string $key, ?array $default = null): ?array
{
$value = $this->find($key, $default);
@@ -156,7 +169,7 @@ public function findArray(string $key, array $default = null)
*
* @return float The value
*/
- public function getFloat(string $key, float $default = null): float
+ public function getFloat(string $key, ?float $default = null): float
{
$value = $this->find($key, $default);
@@ -171,11 +184,11 @@ public function getFloat(string $key, float $default = null): float
* Get value as float or null.
*
* @param string $key The key
- * @param float $default The default value
+ * @param float|null $default The default value
*
* @return float|null The value
*/
- public function findFloat(string $key, float $default = null)
+ public function findFloat(string $key, ?float $default = null): ?float
{
$value = $this->find($key, $default);
@@ -196,7 +209,7 @@ public function findFloat(string $key, float $default = null)
*
* @return bool The value
*/
- public function getBool(string $key, bool $default = null): bool
+ public function getBool(string $key, ?bool $default = null): bool
{
$value = $this->find($key, $default);
@@ -215,7 +228,7 @@ public function getBool(string $key, bool $default = null): bool
*
* @return bool|null The value
*/
- public function findBool(string $key, bool $default = null)
+ public function findBool(string $key, ?bool $default = null): ?bool
{
$value = $this->find($key, $default);
@@ -226,6 +239,46 @@ public function findBool(string $key, bool $default = null)
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.
*
@@ -236,7 +289,7 @@ public function findBool(string $key, bool $default = null)
*
* @return Chronos The value
*/
- public function getChronos(string $key, Chronos $default = null): Chronos
+ public function getChronos(string $key, ?Chronos $default = null): Chronos
{
$value = $this->find($key, $default);
@@ -255,13 +308,11 @@ public function getChronos(string $key, Chronos $default = null): Chronos
* Get value as Chronos or null.
*
* @param string $key The key
- * @param Chronos $default The default value
- *
- * @throws Exception Chronos date time parsing error
+ * @param Chronos|null $default The default value
*
* @return Chronos|null The value
*/
- public function findChronos(string $key, Chronos $default = null)
+ public function findChronos(string $key, ?Chronos $default = null): ?Chronos
{
$value = $this->find($key, $default);
@@ -284,8 +335,16 @@ public function findChronos(string $key, Chronos $default = null)
*
* @return mixed|null The value
*/
- public function find(string $path, $default = null)
+ public function find(string $path, mixed $default = null): mixed
{
+ if (array_key_exists($path, $this->data)) {
+ return $this->data[$path] ?? $default;
+ }
+
+ if (!str_contains($path, '.')) {
+ return $default;
+ }
+
$pathKeys = explode('.', $path);
$arrayCopyOrValue = $this->data;
@@ -303,7 +362,7 @@ public function find(string $path, $default = null)
/**
* Return all data as array.
*
- * @return array The data
+ * @return array The data
*/
public function all(): array
{
@@ -311,7 +370,7 @@ public function all(): array
}
/**
- * Test whether or not a given path exists in $data.
+ * Test whether a given path exists in $data.
* This method uses the same path syntax as Hash::extract().
*
* Checking for paths that could target more than one element will
@@ -356,8 +415,39 @@ public function isEmpty(string $path): bool
*
* @return bool The status
*/
- private function isNullOrBlank($value): bool
+ 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) || (is_float($value) && is_finite($value))) {
+ return DateTimeImmutable::createFromTimestamp($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 321f295..32bdc28 100644
--- a/tests/ArrayReaderTest.php
+++ b/tests/ArrayReaderTest.php
@@ -2,7 +2,11 @@
namespace Selective\ArrayReader\Test;
+use Cake\Chronos\Chronos;
+use DateTime;
+use DateTimeImmutable;
use InvalidArgumentException;
+use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;
use Selective\ArrayReader\ArrayReader;
@@ -14,7 +18,222 @@ class ArrayReaderTest extends TestCase
/**
* Test.
*
- * @dataProvider providerGetString
+ * @param mixed $data The data
+ * @param string|int $key The lookup key
+ * @param mixed $default The default value
+ * @param mixed $expected The expected value
+ *
+ * @return void
+ */
+ #[DataProvider('providerGetInt')]
+ public function testCreateFromArray($data, $key, $default, $expected): void
+ {
+ $reader = ArrayReader::createFromArray($data);
+ $this->assertSame($expected, $reader->getInt($key, $default));
+ }
+
+ /**
+ * Test.
+ *
+ * @param mixed $data The data
+ * @param string|key $key The lookup key
+ * @param mixed $default The default value
+ * @param mixed $expected The expected value
+ *
+ * @return void
+ */
+ #[DataProvider('providerGetInt')]
+ public function testGetInt($data, $key, $default, $expected): void
+ {
+ $reader = new ArrayReader($data);
+ $this->assertSame($expected, $reader->getInt($key, $default));
+ }
+
+ /**
+ * Test.
+ *
+ * @param mixed $data The data
+ * @param string|int $key The lookup key
+ * @param mixed $default The default value
+ *
+ * @return void
+ */
+ #[DataProvider('providerGetIntError')]
+ public function testGetIntError($data, $key, $default): void
+ {
+ $reader = new ArrayReader($data);
+
+ $this->expectException(InvalidArgumentException::class);
+
+ $reader->getInt($key, $default);
+ }
+
+ /**
+ * Test.
+ *
+ * @param mixed $data The data
+ * @param string|int $key The lookup key
+ * @param mixed $default The default value
+ * @param mixed $expected The expected value
+ *
+ * @return void
+ */
+ #[DataProvider('providerFindInt')]
+ public function testFindInt($data, $key, $default, $expected): void
+ {
+ $reader = new ArrayReader($data);
+ $this->assertSame($expected, $reader->findInt($key, $default));
+ }
+
+ /**
+ * Test.
+ *
+ * @param mixed $data The data
+ * @param string|key $key The lookup key
+ * @param mixed $default The default value
+ * @param mixed $expected The expected value
+ *
+ * @return void
+ */
+ #[DataProvider('providerGetFloat')]
+ public function testGetFloat($data, $key, $default, $expected): void
+ {
+ $reader = new ArrayReader($data);
+ $this->assertSame($expected, $reader->getFloat($key, $default));
+ }
+
+ /**
+ * Test.
+ *
+ * @param mixed $data The data
+ * @param string|int $key The lookup key
+ * @param mixed $default The default value
+ *
+ * @return void
+ */
+ #[DataProvider('providerGetFloatError')]
+ public function testGetFloatError($data, $key, $default): void
+ {
+ $reader = new ArrayReader($data);
+
+ $this->expectException(InvalidArgumentException::class);
+
+ $reader->getFloat($key, $default);
+ }
+
+ /**
+ * Test.
+ *
+ * @param mixed $data The data
+ * @param string|int $key The lookup key
+ * @param mixed $default The default value
+ * @param mixed $expected The expected value
+ *
+ * @return void
+ */
+ #[DataProvider('providerFindFloat')]
+ public function testFindFloat($data, $key, $default, $expected): void
+ {
+ $reader = new ArrayReader($data);
+ $this->assertSame($expected, $reader->findFloat($key, $default));
+ }
+
+ /**
+ * Provider.
+ *
+ * @return array[] The test data
+ */
+ public static function providerGetInt(): array
+ {
+ return [
+ [[0, 1, 2, 3], 0, null, 0],
+ [[0, 1, 2, 3], 1, null, 1],
+ [[0, 1, 2, 3], 2, null, 2],
+ [[0, 1, 2, 3], 3, null, 3],
+ ];
+ }
+
+ /**
+ * Provider.
+ *
+ * @return array[] The test data
+ */
+ public static function providerGetIntError(): array
+ {
+ return [
+ [[0, 1, 2, 3], 4, null],
+ [[0, 1, 2, 3, null], 4, null],
+ [[0, 1, 2, null, 4], 3, null],
+ ];
+ }
+
+ /**
+ * Provider.
+ *
+ * @return array[] The test data
+ */
+ public static function providerFindInt(): array
+ {
+ return [
+ [[0, 1, 2, 3], 0, null, 0],
+ [[0, 1, 2, 3], 1, null, 1],
+ [[0, 1, 2, 3], 2, null, 2],
+ [[0, 1, 2, 3], 3, null, 3],
+ [[0, 1, 2, 3], 4, null, null],
+ [[0, 1, 2, 3, null], 4, null, null],
+ [[0, 1, 2, null, 4], 3, null, null],
+ ];
+ }
+
+ /**
+ * Provider.
+ *
+ * @return array[] The test data
+ */
+ public static function providerGetFloat(): array
+ {
+ return [
+ [[0.1, 1.2, 2.3, 3.4], 0, null, 0.1],
+ [[0.1, 1.2, 2.3, 3.4], 1, null, 1.2],
+ [[0.1, 1.2, 2.3, 3.4], 2, null, 2.3],
+ [[0.1, 1.2, 2.3, 3.4], 3, null, 3.4],
+ ];
+ }
+
+ /**
+ * Provider.
+ *
+ * @return array[] The test data
+ */
+ public static function providerGetFloatError(): array
+ {
+ return [
+ [[0.1, 1.2, 2.3, 3.4], 4, null],
+ [[0.1, 1.2, 2.3, 3.4, null], 4, null],
+ [[0.1, 1.2, 2.3, null, 4.4], 3, null],
+ ];
+ }
+
+ /**
+ * Provider.
+ *
+ * @return array[] The test data
+ */
+ public static function providerFindFloat(): array
+ {
+ return [
+ [[0.1, 1.2, 2.3, 3.4], 0, null, 0.1],
+ [[0.1, 1.2, 2.3, 3.4], 1, null, 1.2],
+ [[0.1, 1.2, 2.3, 3.4], 2, null, 2.3],
+ [[0.1, 1.2, 2.3, 3.4], 3, null, 3.4],
+ [[0.1, 1.2, 2.3, 3.4], 4, null, null],
+ [[0.1, 1.2, 2.3, 3.4, null], 4, null, null],
+ [[0.1, 1.2, 2.3, null, 4.4], 3, null, null],
+ ];
+ }
+
+ /**
+ * Test.
*
* @param mixed $data The data
* @param string $key The lookup key
@@ -23,11 +242,12 @@ class ArrayReaderTest extends TestCase
*
* @return void
*/
- public function testGetString($data, string $key, $default, $expected)
+ #[DataProvider('providerGetString')]
+ public function testGetString($data, string $key, $default, $expected): void
{
$reader = new ArrayReader($data);
- static::assertSame($expected, $reader->findString($key, $default));
- static::assertSame($expected, $reader->getString($key, $default));
+ $this->assertSame($expected, $reader->findString($key, $default));
+ $this->assertSame($expected, $reader->getString($key, $default));
}
/**
@@ -35,7 +255,7 @@ public function testGetString($data, string $key, $default, $expected)
*
* @return array[] The test data
*/
- public function providerGetString(): array
+ public static function providerGetString(): array
{
return [
[['key' => 'value'], 'key', null, 'value'],
@@ -49,21 +269,20 @@ public function providerGetString(): array
/**
* Test.
*
- * @dataProvider providerGetStringError
- *
* @param mixed $data The data
* @param string $key The lookup key
*
* @return void
*/
- public function testGetStringError($data, string $key)
+ #[DataProvider('providerGetStringError')]
+ public function testGetStringError($data, string $key): void
{
$this->expectException(InvalidArgumentException::class);
$reader = new ArrayReader($data);
$reader->getString($key);
- static::assertTrue(true);
+ $this->assertTrue(true);
}
/**
@@ -71,7 +290,7 @@ public function testGetStringError($data, string $key)
*
* @return array[] The test data
*/
- public function providerGetStringError(): array
+ public static function providerGetStringError(): array
{
return [
[['key' => 'value'], 'nope'],
@@ -84,8 +303,6 @@ public function providerGetStringError(): array
/**
* Test.
*
- * @dataProvider providerFindString
- *
* @param mixed $data The data
* @param string $key The lookup key
* @param mixed $default The default value
@@ -93,10 +310,11 @@ public function providerGetStringError(): array
*
* @return void
*/
- public function testFindString($data, string $key, $default, $expected)
+ #[DataProvider('providerFindString')]
+ public function testFindString($data, string $key, $default, $expected): void
{
$reader = new ArrayReader($data);
- static::assertSame($expected, $reader->findString($key, $default));
+ $this->assertSame($expected, $reader->findString($key, $default));
}
/**
@@ -104,7 +322,7 @@ public function testFindString($data, string $key, $default, $expected)
*
* @return array[] The test data
*/
- public function providerFindString(): array
+ public static function providerFindString(): array
{
return [
[['key' => 'value'], 'key', null, 'value'],
@@ -115,4 +333,223 @@ public function providerFindString(): array
[['key' => ['key2' => 'value']], 'key.nope', 'default', 'default'],
];
}
+
+ /**
+ * Test.
+ *
+ * @param mixed $data The data
+ * @param string|int $key The lookup key
+ * @param mixed $default The default value
+ * @param mixed $expected The expected value
+ *
+ * @return void
+ */
+ #[DataProvider('providerGetArray')]
+ public function testGetArray($data, $key, $default, $expected): void
+ {
+ $reader = new ArrayReader($data);
+ $this->assertSame($expected, $reader->getArray($key, $default));
+ }
+
+ /**
+ * Provider.
+ *
+ * @return array[] The test data
+ */
+ public static function providerGetArray(): array
+ {
+ return [
+ [[[1, 2, 3, 4], [2, 3, 4, 5, 6]], 0, null, [1, 2, 3, 4]],
+ [[[1, 2, 3, 4], [2, 3, 4, 5, 6]], 1, null, [2, 3, 4, 5, 6]],
+ [[['key' => 'value'], ['key2' => 'value2']], 0, null, ['key' => 'value']],
+ [[['key' => 'value'], ['key2' => 'value2']], 1, null, ['key2' => 'value2']],
+ ];
+ }
+
+ /**
+ * Test.
+ *
+ * @param mixed $data The data
+ * @param string|int $key The lookup key
+ * @param mixed $default The default value
+ *
+ * @return void
+ */
+ #[DataProvider('providerGetArrayError')]
+ public function testGetArrayError($data, $key, $default): void
+ {
+ $reader = new ArrayReader($data);
+
+ $this->expectException(InvalidArgumentException::class);
+
+ $reader->getArray($key, $default);
+ }
+
+ /**
+ * Provider.
+ *
+ * @return array[] The test data
+ */
+ public static function providerGetArrayError(): array
+ {
+ return [
+ [[[1, 2, 3, 4], [2, 3, 4, 5, 6]], 2, null],
+ [[['key' => 'value'], ['key2' => 'value2']], 2, null],
+ ];
+ }
+
+ /**
+ * Test.
+ *
+ * @param mixed $data The data
+ * @param string|int $key The lookup key
+ * @param mixed $default The default value
+ * @param mixed $expected The expected value
+ *
+ * @return void
+ */
+ #[DataProvider('providerFindArray')]
+ public function testFindArray($data, $key, $default, $expected): void
+ {
+ $reader = new ArrayReader($data);
+ $this->assertSame($expected, $reader->findArray($key, $default));
+ }
+
+ /**
+ * Provider.
+ *
+ * @return array[] The test data
+ */
+ public static function providerFindArray(): array
+ {
+ return [
+ [[[1, 2, 3, 4], [2, 3, 4, 5, 6]], 0, null, [1, 2, 3, 4]],
+ [[[1, 2, 3, 4], [2, 3, 4, 5, 6]], 1, null, [2, 3, 4, 5, 6]],
+ [[[1, 2, 3, 4], [2, 3, 4, 5, 6]], 2, [], []],
+ [[[1, 2, 3, 4], [2, 3, 4, 5, 6]], 2, null, null],
+ [[['key' => 'value'], ['key2' => 'value2']], 0, null, ['key' => 'value']],
+ [[['key' => 'value'], ['key2' => 'value2']], 1, null, ['key2' => 'value2']],
+ [[['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');
+ $fraction = new DateTimeImmutable('2020-01-02T03:04:05.500000+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],
+ [['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],
+ [['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],
+ [['created' => INF], 'created', null],
+ [['created' => NAN], '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],
+ ];
+ }
}
diff --git a/tests/boostrap.php b/tests/boostrap.php
deleted file mode 100644
index d21c14d..0000000
--- a/tests/boostrap.php
+++ /dev/null
@@ -1,3 +0,0 @@
-