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. -[![Latest Version on Packagist](https://img.shields.io/github/release/selective-php/array-reader.svg?style=flat-square)](https://packagist.org/packages/selective/array-reader) -[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE.md) -[![Build Status](https://img.shields.io/travis/selective-php/array-reader/master.svg?style=flat-square)](https://travis-ci.org/selective-php/array-reader) -[![Coverage Status](https://img.shields.io/scrutinizer/coverage/g/selective-php/array-reader.svg?style=flat-square)](https://scrutinizer-ci.com/g/selective-php/array-reader/code-structure) -[![Quality Score](https://img.shields.io/scrutinizer/quality/g/selective-php/array-reader.svg?style=flat-square)](https://scrutinizer-ci.com/g/selective-php/array-reader/?branch=master) -[![Total Downloads](https://img.shields.io/packagist/dt/selective/array-reader.svg?style=flat-square)](https://packagist.org/packages/selective/array-reader/stats) - +[![Latest Version on Packagist](https://img.shields.io/github/release/selective-php/array-reader.svg)](https://packagist.org/packages/selective/array-reader) +[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg)](LICENSE) +[![Build Status](https://github.com/selective-php/array-reader/workflows/build/badge.svg)](https://github.com/selective-php/array-reader/actions) +[![Total Downloads](https://img.shields.io/packagist/dt/selective/array-reader.svg)](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 @@ -