From 131f069deb1539e5b08e875dbbd4199644ea1371 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20Gon=C3=A7alves?= Date: Wed, 13 Mar 2024 11:01:59 +0000 Subject: [PATCH] Drop support for PHP 8.0 and make 8.1 the minimum version (#28) Change CI/CD pipelines to test with PHP 8.1, 8.2 and 8.3 Bump PHPUnit to 10.5 Refactor code to make all properties read-only where possible Add `FilterOperatorXor` Remove getters from `MapperCallers` and use public read-only properties instead `AbstractItem::getBuilders` is now an abstract method that should always be implemented in subclasses Remove useless `catch` blocks in `CollectionTrait` Refactor some code to use more PHP 8.1 features Update tests to PHPUnit 10.5 Update documentation --- .github/workflows/continuous-integration.yml | 6 +- .gitignore | 7 ++- composer.json | 8 +-- docs/abstract-collections.md | 6 ++ docs/abstract-item.md | 41 +++++++++---- docs/autosortable-offsetset-trait.md | 16 +++-- docs/collection-trait.md | 7 ++- docs/filterable-collection-trait.md | 12 +++- docs/mapper.md | 18 +++--- phpunit.xml.dist | 18 +++--- src/AbstractItem.php | 36 +++++------ src/AbstractItemBuildersTrait.php | 64 +++++++++++++++----- src/CollectionTrait.php | 9 +-- src/Filter/BaseFilter.php | 2 +- src/Filter/CompositeFilter.php | 9 ++- src/Filter/FilterOperatorXor.php | 22 +++++++ src/Mapper/DefaultMapper.php | 6 +- src/Mapper/MapperCallers.php | 12 +--- tests/AbstractItemTest.php | 15 ++--- tests/CollectionTest.php | 47 +++++++------- tests/Filter/FilterOperatorXOrTest.php | 22 +++++++ tests/Stub/AbstractItemToArrayStub.php | 2 +- tests/Stub/AbstractItemToArrayStubBase.php | 4 +- tests/Stub/AutoSortedCollectionStub.php | 5 +- tests/Stub/DTOCollectionStub.php | 2 +- tests/Stub/DTOStub.php | 12 +--- tests/Stub/FilterItemStub.php | 2 +- tests/Stub/FromArrayStub.php | 12 +--- tests/Stub/MapperStub.php | 4 +- tests/Stub/ToArrayStub.php | 2 +- tests/Stub/ToIntStub.php | 2 +- tests/Stub/ToStringStub.php | 2 +- 32 files changed, 254 insertions(+), 178 deletions(-) create mode 100644 src/Filter/FilterOperatorXor.php create mode 100644 tests/Filter/FilterOperatorXOrTest.php diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 1a23a96..3eff8f2 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -17,7 +17,9 @@ jobs: strategy: matrix: php-version: - - 8.0 + - 8.1 + - 8.2 + - 8.3 dependencies: - lowest - highest @@ -58,7 +60,7 @@ jobs: - uses: actions/download-artifact@v4 with: - name: build-8-highest-coverage + name: build-8.1-highest-coverage path: tests/.results/ - name: Fix Code Coverage Paths diff --git a/.gitignore b/.gitignore index 50a0dae..54fe6ba 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ .idea +.phpunit.cache +.phpunit.result.cache +composer.lock +phpunit.log tests/.results vendor -composer.lock -.php_cs.cache -.phpunit.result.cache diff --git a/composer.json b/composer.json index 563b68c..2e69d36 100644 --- a/composer.json +++ b/composer.json @@ -14,11 +14,11 @@ } ], "require": { - "php": ">=8.0" + "php": ">=8.1" }, "require-dev": { "kununu/scripts": ">=4.0", - "phpunit/phpunit": "^9.6" + "phpunit/phpunit": "^10.5" }, "autoload": { "psr-4": { @@ -31,8 +31,8 @@ } }, "scripts": { - "test": "phpunit --no-coverage tests", - "test-coverage": "XDEBUG_MODE=coverage phpunit --coverage-clover tests/.results/coverage.xml --coverage-html tests/.results/html/ tests" + "test": "phpunit --log-events-text phpunit.log --no-coverage --no-logging --no-progress --testsuite Full", + "test-coverage": "XDEBUG_MODE=coverage phpunit --log-events-text phpunit.log --no-progress --testsuite Full" }, "scripts-descriptions": { "test": "Run all tests", diff --git a/docs/abstract-collections.md b/docs/abstract-collections.md index 0b51e98..4c7b32d 100644 --- a/docs/abstract-collections.md +++ b/docs/abstract-collections.md @@ -5,6 +5,9 @@ This is an abstract base class that you can use for your collections. It extends `ArrayIterator` (and already uses the `CollectionTrait`) so you just need to extend it to have a proper collection class. ```php + fn(array $data) => $data['id'] ?? null, - 'name' => fn(array $data) => $data['name'] ?? null, - 'createdAt' => fn(array $data) => $data['createdAt'] ?? null, + 'id' => static fn(array $data): ?int => $data['id'] ?? null, + 'name' => static fn(array $data): ?string => $data['name'] ?? null, + 'createdAt' => static fn(array $data)? DateTime => $data['createdAt'] ?? null, 'simpleName' => self::buildStringGetter('simpleName'), 'verified' => self::buildBoolGetter('verified'), 'industryId' => self::buildIntGetter('industryId'), @@ -56,6 +62,9 @@ If you want to use different prefixes for setters/getters you can! Just override Example: ```php + `callable` to get data for the property: ```php [ - 'itemProperty' => fn(array $data) => $valueForTheProperty, + 'itemProperty' => static fn(array $data): mixed => $valueForTheProperty, ] ``` @@ -326,6 +341,8 @@ If `$useSnakeCase` is `true` the `$sourceField` will be converted to snake case Example: ```php +offsetSet($value, $value); - break; - default: - parent::append($value); - } + match (true) { + is_int($value) => $this->offsetSet($value, $value), + default => parent::append($value) + }; } } diff --git a/docs/collection-trait.md b/docs/collection-trait.md index 396bf71..78abe9e 100644 --- a/docs/collection-trait.md +++ b/docs/collection-trait.md @@ -114,6 +114,9 @@ This applies also to collections that are defined inside a class that is an item Example: ```php +isSatisfiedBy(...) && $myFilter2->isSatisfiedBy(...) && $myFilter3-> So to wrap it up, on how to filter/group a collection: ```php +filter( // Filter1 AND Filter2 AND Filter3 new CompositeFilter( @@ -145,12 +151,12 @@ $groupByResults = $collection->groupBy( ), // Second group new CompositeFilter( - // Filter1 AND Filter3 AND (Filter2 OR Filter4) + // Filter1 AND Filter3 AND (Filter2 XOR Filter4) new FilterOperatorAnd(), new MyFilter1(), new MyFilter3(), new CompositeFilter( - new FilterOperatorOr(), + new FilterOperatorXor(), new MyFilter2(), new MyFilter4() ) diff --git a/docs/mapper.md b/docs/mapper.md index fad794a..1ba14d5 100644 --- a/docs/mapper.md +++ b/docs/mapper.md @@ -45,19 +45,17 @@ The `$fnGetValue` closure should receive an instance of your collection item and ### Example ```php +key = $key; - $this->value = $value; } } @@ -71,8 +69,8 @@ final class MyMapper extends DefaultMapper { if (MyCollection::class === $collectionClass) { return new MapperCallers( - fn(MyCollectionItem $item): int => $item->key, - fn(MyCollectionItem $item): string => $item->value() + fn(MyCollectionItem $item): string => sprintf('ID %s', $item->key), + fn(MyCollectionItem $item): string => $item->value ); } @@ -91,8 +89,8 @@ $map = $mapper->map( /* Value of $map: [ - 1 => 'Item 1', - 2 => 'Item 2' + 'ID 1' => 'Item 1', + 'ID 2' => 'Item 2' ] */ diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 2801a54..6ff5f05 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,23 +1,25 @@ - + - - - src - + xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.5/phpunit.xsd" bootstrap="vendor/autoload.php" + colors="true" beStrictAboutChangesToGlobalState="true" testdox="true" cacheDirectory=".phpunit.cache"> + - + tests + + + src + + diff --git a/src/AbstractItem.php b/src/AbstractItem.php index e84f9f4..788c97a 100644 --- a/src/AbstractItem.php +++ b/src/AbstractItem.php @@ -41,38 +41,32 @@ public static function build(array $data): self|static public function __call(string $method, array $args) { - switch (true) { - case static::SETTER_PREFIX === substr($method, 0, $setterPrefixLen = strlen(static::SETTER_PREFIX)): - $set = true; - $attribute = lcfirst(substr($method, $setterPrefixLen)); - $value = current($args); - break; - case static::GETTER_PREFIX === substr($method, 0, $getterPrefixLen = strlen(static::GETTER_PREFIX)): - $set = false; - $attribute = lcfirst(substr($method, $getterPrefixLen)); - $value = null; - break; - default: - throw new BadMethodCallException(sprintf('%s: Invalid method "%s" called', static::class, $method)); - } + [$set, $attribute, $value] = match (true) { + static::SETTER_PREFIX === substr($method, 0, $setterPrefixLen = strlen(static::SETTER_PREFIX)) => [ + true, + lcfirst(substr($method, $setterPrefixLen)), + current($args), + ], + static::GETTER_PREFIX === substr($method, 0, $getterPrefixLen = strlen(static::GETTER_PREFIX)) => [ + false, + lcfirst(substr($method, $getterPrefixLen)), + null, + ], + default => throw new BadMethodCallException(sprintf('%s: Invalid method "%s" called', static::class, $method)) + }; return $set ? $this->setAttribute($attribute, $value) : $this->getAttribute($attribute); } /** - * @codeCoverageIgnore - * - * Ready to be rewritten in your subclass! + * Must be implemented in your subclass! * * Array format: * [ * 'itemProperty' => fn(array $data) => $valueForTheProperty * ] */ - protected static function getBuilders(): array - { - return []; - } + abstract protected static function getBuilders(): array; protected function setAttributes(array $attributes): self|static { diff --git a/src/AbstractItemBuildersTrait.php b/src/AbstractItemBuildersTrait.php index 1ae083c..2659433 100644 --- a/src/AbstractItemBuildersTrait.php +++ b/src/AbstractItemBuildersTrait.php @@ -26,7 +26,11 @@ protected static function buildStringGetter( protected static function buildRequiredStringGetter(string $fieldName, bool $useSnakeCase = false): callable { - return self::buildGetterRequiredField($fieldName, fn($value): string => (string) $value, $useSnakeCase); + return self::buildGetterRequiredField( + $fieldName, + static fn(mixed $value): string => (string) $value, + $useSnakeCase + ); } protected static function buildBoolGetter( @@ -34,12 +38,21 @@ protected static function buildBoolGetter( ?bool $default = null, bool $useSnakeCase = false ): callable { - return self::buildGetterOptionalField($fieldName, fn($value): bool => (bool) $value, $default, $useSnakeCase); + return self::buildGetterOptionalField( + $fieldName, + static fn(mixed $value): bool => (bool) $value, + $default, + $useSnakeCase + ); } protected static function buildRequiredBoolGetter(string $fieldName, bool $useSnakeCase = false): callable { - return self::buildGetterRequiredField($fieldName, fn($value): bool => (bool) $value, $useSnakeCase); + return self::buildGetterRequiredField( + $fieldName, + static fn(mixed $value): bool => (bool) $value, + $useSnakeCase + ); } protected static function buildIntGetter( @@ -47,12 +60,21 @@ protected static function buildIntGetter( ?int $default = null, bool $useSnakeCase = false ): callable { - return self::buildGetterOptionalField($fieldName, fn($value): int => (int) $value, $default, $useSnakeCase); + return self::buildGetterOptionalField( + $fieldName, + static fn(mixed $value): int => (int) $value, + $default, + $useSnakeCase + ); } protected static function buildRequiredIntGetter(string $fieldName, bool $useSnakeCase = false): callable { - return self::buildGetterRequiredField($fieldName, fn($value): int => (int) $value, $useSnakeCase); + return self::buildGetterRequiredField( + $fieldName, + static fn(mixed $value): int => (int) $value, + $useSnakeCase + ); } protected static function buildFloatGetter( @@ -60,12 +82,21 @@ protected static function buildFloatGetter( ?float $default = null, bool $useSnakeCase = false ): callable { - return self::buildGetterOptionalField($fieldName, fn($value): float => (float) $value, $default, $useSnakeCase); + return self::buildGetterOptionalField( + $fieldName, + static fn(mixed $value): float => (float) $value, + $default, + $useSnakeCase + ); } protected static function buildRequiredFloatGetter(string $fieldName, bool $useSnakeCase = false): callable { - return self::buildGetterRequiredField($fieldName, fn($value): float => (float) $value, $useSnakeCase); + return self::buildGetterRequiredField( + $fieldName, + static fn(mixed $value): float => (float) $value, + $useSnakeCase + ); } protected static function buildDateTimeGetter( @@ -76,7 +107,7 @@ protected static function buildDateTimeGetter( ): callable { return self::buildGetterOptionalField( $fieldName, - fn($value): ?DateTimeInterface => DateTime::createFromFormat($dateFormat, $value) ?: $default, + static fn(mixed $value): ?DateTimeInterface => DateTime::createFromFormat($dateFormat, $value) ?: $default, $default ? DateTime::createFromInterface($default) : null, $useSnakeCase ); @@ -89,7 +120,7 @@ protected static function buildRequiredDateTimeGetter( ): callable { return self::buildGetterRequiredField( $fieldName, - fn($value): DateTimeInterface => DateTime::createFromFormat($dateFormat, $value), + static fn(mixed $value): DateTimeInterface => DateTime::createFromFormat($dateFormat, $value), $useSnakeCase ); } @@ -102,7 +133,8 @@ protected static function buildDateTimeImmutableGetter( ): callable { return self::buildGetterOptionalField( $fieldName, - fn($value): ?DateTimeInterface => DateTimeImmutable::createFromFormat($dateFormat, $value) ?: $default, + static fn(mixed $value): ?DateTimeInterface => DateTimeImmutable::createFromFormat($dateFormat, $value) + ?: $default, $default ? DateTimeImmutable::createFromInterface($default) : null, $useSnakeCase ); @@ -115,7 +147,7 @@ protected static function buildRequiredDateTimeImmutableGetter( ): callable { return self::buildGetterRequiredField( $fieldName, - fn($value): DateTimeInterface => DateTimeImmutable::createFromFormat($dateFormat, $value), + static fn(mixed $value): DateTimeInterface => DateTimeImmutable::createFromFormat($dateFormat, $value), $useSnakeCase ); } @@ -128,7 +160,7 @@ protected static function buildGetterOptionalField( ): callable { $fieldName = $useSnakeCase ? self::camelToSnake($fieldName) : $fieldName; - return fn(array $data): mixed => isset($data[$fieldName]) ? $converter($data[$fieldName]) : $default; + return static fn(array $data): mixed => isset($data[$fieldName]) ? $converter($data[$fieldName]) : $default; } protected static function buildGetterRequiredField( @@ -138,7 +170,7 @@ protected static function buildGetterRequiredField( ): callable { $fieldName = $useSnakeCase ? self::camelToSnake($fieldName) : $fieldName; - return fn(array $data) => match (isset($data[$fieldName])) { + return static fn(array $data) => match (isset($data[$fieldName])) { true => $converter($data[$fieldName]), default => throw new InvalidArgumentException(sprintf('Missing "%s" field', $fieldName)) }; @@ -152,7 +184,7 @@ protected static function buildFromArrayGetter( ): callable { $fieldName = $useSnakeCase ? self::camelToSnake($fieldName) : $fieldName; - return fn(array $data): ?FromArray => match (true) { + return static fn(array $data): ?FromArray => match (true) { !is_a($fromArrayClass, FromArray::class, true) => null, isset($data[$fieldName]) => self::invoke( $fromArrayClass, @@ -171,7 +203,7 @@ protected static function buildCollectionGetter( ): callable { $fieldName = $useSnakeCase ? self::camelToSnake($fieldName) : $fieldName; - return fn(array $data): ?AbstractCollection => match (true) { + return static fn(array $data): ?AbstractCollection => match (true) { !is_a($collectionClass, AbstractCollection::class, true) => null, isset($data[$fieldName]) => self::invoke( $collectionClass, @@ -189,7 +221,7 @@ protected static function buildConditionalGetter( ): callable { $sourceField = $useSnakeCase ? self::camelToSnake($sourceField) : $sourceField; - return function(array $data) use ($sourceField, $sources) { + return static function(array $data) use ($sourceField, $sources): mixed { foreach ($sources as $source => $getter) { if ($source === ($data[$sourceField] ?? null)) { return is_callable($getter) ? $getter($data) : null; diff --git a/src/CollectionTrait.php b/src/CollectionTrait.php index 8c08aad..19a1b17 100644 --- a/src/CollectionTrait.php +++ b/src/CollectionTrait.php @@ -6,7 +6,6 @@ use Kununu\Collection\Convertible\ToArray; use Kununu\Collection\Convertible\ToInt; use Kununu\Collection\Convertible\ToString; -use Throwable; trait CollectionTrait { @@ -64,8 +63,6 @@ public function each(callable $function, bool $rewind = true): self|static foreach ($this as $element) { $function($element, $this->key()); } - } catch (Throwable $e) { - throw $e; } finally { if ($rewind) { $this->rewind(); @@ -82,8 +79,6 @@ public function map(callable $function, bool $rewind = true): array foreach ($this as $element) { $map[] = $function($element, $this->key()); } - } catch (Throwable $e) { - throw $e; } finally { if ($rewind) { $this->rewind(); @@ -99,8 +94,6 @@ public function reduce(callable $function, mixed $initial = null, bool $rewind = foreach ($this as $element) { $initial = $function($initial, $element, $this->key()); } - } catch (Throwable $e) { - throw $e; } finally { if ($rewind) { $this->rewind(); @@ -118,7 +111,7 @@ public function toArray(): array protected function mapToArray(bool $withKeys = true): array { return array_map( - fn($element) => match (true) { + static fn(mixed $element): mixed => match (true) { $element instanceof ToArray => $element->toArray(), $element instanceof ToString => $element->toString(), $element instanceof ToInt => $element->toInt(), diff --git a/src/Filter/BaseFilter.php b/src/Filter/BaseFilter.php index 0ac8ef6..95665d2 100644 --- a/src/Filter/BaseFilter.php +++ b/src/Filter/BaseFilter.php @@ -7,7 +7,7 @@ abstract class BaseFilter implements CollectionFilter { private ?array $customGroupByData = null; - public function __construct(private string $key) + public function __construct(private readonly string $key) { } diff --git a/src/Filter/CompositeFilter.php b/src/Filter/CompositeFilter.php index aaa021c..9b4c327 100644 --- a/src/Filter/CompositeFilter.php +++ b/src/Filter/CompositeFilter.php @@ -9,10 +9,13 @@ final class CompositeFilter extends BaseFilter { /** @var CollectionFilter[] */ - private array $filters; + private readonly array $filters; - public function __construct(string $key, private FilterOperator $filterOperator, CollectionFilter ...$filters) - { + public function __construct( + string $key, + private readonly FilterOperator $filterOperator, + CollectionFilter ...$filters + ) { parent::__construct($key); $this->filters = $filters; } diff --git a/src/Filter/FilterOperatorXor.php b/src/Filter/FilterOperatorXor.php new file mode 100644 index 0000000..b5f32b5 --- /dev/null +++ b/src/Filter/FilterOperatorXor.php @@ -0,0 +1,22 @@ + */ private array $callers = []; public function __construct(string ...$collectionClasses) @@ -24,13 +24,13 @@ public function __construct(string ...$collectionClasses) public function map(AbstractCollection $collection): array { - if (!array_key_exists($collectionClass = get_class($collection), $this->callers)) { + if (!array_key_exists($collectionClass = $collection::class, $this->callers)) { throw new InvalidArgumentException('Invalid collection'); } $collectionCallers = $this->callers[$collectionClass]; - return $this->mapCollection($collection, $collectionCallers->fnGetId(), $collectionCallers->fnGetValue()); + return $this->mapCollection($collection, $collectionCallers->fnGetId, $collectionCallers->fnGetValue); } abstract protected function getCallers(string $collectionClass): ?MapperCallers; diff --git a/src/Mapper/MapperCallers.php b/src/Mapper/MapperCallers.php index 5a2d046..c1db1b1 100644 --- a/src/Mapper/MapperCallers.php +++ b/src/Mapper/MapperCallers.php @@ -7,17 +7,7 @@ final class MapperCallers { - public function __construct(private Closure $fnGetId, private Closure $fnGetValue) + public function __construct(public readonly Closure $fnGetId, public readonly Closure $fnGetValue) { } - - public function fnGetId(): Closure - { - return $this->fnGetId; - } - - public function fnGetValue(): Closure - { - return $this->fnGetValue; - } } diff --git a/tests/AbstractItemTest.php b/tests/AbstractItemTest.php index 4e8cb77..96cc70d 100644 --- a/tests/AbstractItemTest.php +++ b/tests/AbstractItemTest.php @@ -17,6 +17,7 @@ use Kununu\Collection\Tests\Stub\DTOStub; use Kununu\Collection\Tests\Stub\FromArrayStub; use OutOfBoundsException; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; final class AbstractItemTest extends TestCase @@ -52,7 +53,7 @@ public function testItemCreation(): void $this->assertSame(1500.29, $item->getSalary()); } - /** @dataProvider itemBuildDataProvider */ + #[DataProvider('itemBuildDataProvider')] public function testItemBuild( array $data, ?int $expectedId, @@ -157,7 +158,7 @@ public static function itemBuildDataProvider(): array ]; } - /** @dataProvider itemBuildRequiredDataProvider */ + #[DataProvider('itemBuildRequiredDataProvider')] public function testItemBuildRequired(array $data, ?string $expectedExceptionMessage): void { if (null !== $expectedExceptionMessage) { @@ -252,12 +253,12 @@ public function testItemBuildFromArray(): void ]); $this->assertInstanceOf(FromArrayStub::class, $item->fromArray()); - $this->assertEquals(1, $item->fromArray()->id()); - $this->assertEquals('The Name', $item->fromArray()->name()); + $this->assertEquals(1, $item->fromArray()->id); + $this->assertEquals('The Name', $item->fromArray()->name); $this->assertNull($item->notFromArray()); $this->assertInstanceOf(FromArrayStub::class, $item->defaultFromArray()); - $this->assertEquals(0, $item->defaultFromArray()->id()); - $this->assertEquals('', $item->defaultFromArray()->name()); + $this->assertEquals(0, $item->defaultFromArray()->id); + $this->assertEquals('', $item->defaultFromArray()->name); } public function testItemBuildCollection(): void @@ -294,7 +295,7 @@ public function testItemBuildCollection(): void $this->assertEmpty($item->defaultCollection()); } - /** @dataProvider itemBuildConditionalDataProvider */ + #[DataProvider('itemBuildConditionalDataProvider')] public function testItemBuildConditional(mixed $expected): void { $item = AbstractItemWithConditionalBuilderStub::build([ diff --git a/tests/CollectionTest.php b/tests/CollectionTest.php index d85dc93..48e648b 100644 --- a/tests/CollectionTest.php +++ b/tests/CollectionTest.php @@ -11,12 +11,13 @@ use Kununu\Collection\Tests\Stub\ToArrayStub; use Kununu\Collection\Tests\Stub\ToIntStub; use Kununu\Collection\Tests\Stub\ToStringStub; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Throwable; final class CollectionTest extends TestCase { - /** @dataProvider fromIterableDataProvider */ + #[DataProvider('fromIterableDataProvider')] public function testFromIterable(iterable $data, array $expected): void { $this->assertEquals($expected, CollectionStub::fromIterable($data)->toArray()); @@ -25,27 +26,27 @@ public function testFromIterable(iterable $data, array $expected): void public static function fromIterableDataProvider(): array { return [ - [ + 'simple_integers_array_case_1' => [ [1, 2, 3], [1, 2, 3], ], - [ + 'simple_integers_array_case_2' => [ range(5, 10), [5, 6, 7, 8, 9, 10], ], - [ + 'generator_of_integers' => [ self::getGenerator(5, 9, 14), [5, 9, 14], ], - [ + 'array_iterator_of_integers' => [ self::getArrayIterator(10, 20, 30, 40), [10, 20, 30, 40], ], - [ + 'empty_array_iterator' => [ self::getArrayIterator(), [], ], - [ + 'generator_of_complex_objects' => [ self::getGenerator( ToArrayStub::create(ToIntStub::fromInt(1), ToStringStub::create(ToIntStub::fromInt(7), 'PHP')), ToArrayStub::create(ToIntStub::fromInt(2), ToStringStub::create(ToIntStub::fromInt(13), 'Java')), @@ -66,7 +67,7 @@ public static function fromIterableDataProvider(): array ], ], ], - [ + 'array_of_objects' => [ [ ToIntStub::fromInt(1), ToIntStub::fromInt(2), @@ -78,7 +79,7 @@ public static function fromIterableDataProvider(): array 3, ], ], - [ + 'generator_of_simple_objects' => [ self::getGenerator( ToStringStub::create(ToIntStub::fromInt(1), 'ABC'), ToStringStub::create(ToIntStub::fromInt(2), 'DEF'), @@ -132,7 +133,7 @@ public function testDiff(): void $this->assertEquals([6, 7], $collection2->diff($collection1)->toArray()); } - /** @dataProvider eachDataProvider */ + #[DataProvider('eachDataProvider')] public function testEach(bool $rewind, ?int $expectedCurrent, bool $expectException): void { $exceptionWasThrown = false; @@ -186,7 +187,7 @@ public static function eachDataProvider(): array ]; } - /** @dataProvider mapDataProvider */ + #[DataProvider('mapDataProvider')] public function testMap(bool $rewind, ?array $expectedCurrent, bool $expectException): void { $exceptionWasThrown = false; @@ -242,7 +243,7 @@ public static function mapDataProvider(): array ]; } - /** @dataProvider reduceDataProvider */ + #[DataProvider('reduceDataProvider')] public function testReduce(bool $rewind, ?int $expectedCurrent, bool $expectException): void { $exceptionWasThrown = false; @@ -299,7 +300,7 @@ public static function reduceDataProvider(): array ]; } - /** @dataProvider autoSortedCollectionDataProvider */ + #[DataProvider('autoSortedCollectionDataProvider')] public function testAutoSortedCollection(iterable $data, array $expected): void { $this->assertEquals($expected, AutoSortedCollectionStub::fromIterable($data)->toArray()); @@ -308,41 +309,45 @@ public function testAutoSortedCollection(iterable $data, array $expected): void public static function autoSortedCollectionDataProvider(): array { return [ - [ + 'generator_of_integers_with_duplicated_items' => [ self::getGenerator(1, 2, 3, 4, 1, 2, 2, 3, 3, 4, 2, 1, 2, 1, 1, 2), [1, 2, 3, 4], ], - [ + 'array_of_integers' => [ range(5, 10), [5, 6, 7, 8, 9, 10], ], - [ + 'generator_of_unsorted_integers' => [ self::getGenerator(9, 14, 5), [5, 9, 14], ], - [ + 'array_iterator_of_integers' => [ self::getArrayIterator(10, 20, 30, 40), [10, 20, 30, 40], ], - [ + 'empty_array_iterator' => [ self::getArrayIterator(), [], ], - [ + 'empty_generator' => [ + self::getGenerator(), + [], + ], + 'generator_of_unsorted_strings' => [ self::getGenerator('x', 'm', 'd', 'h', 'f'), ['d', 'f', 'h', 'm', 'x'], ], ]; } - private static function getGenerator(...$items): Generator + private static function getGenerator(mixed ...$items): Generator { foreach ($items as $item) { yield $item; } } - private static function getArrayIterator(...$items): ArrayIterator + private static function getArrayIterator(mixed ...$items): ArrayIterator { $arrayIterator = new ArrayIterator(); diff --git a/tests/Filter/FilterOperatorXOrTest.php b/tests/Filter/FilterOperatorXOrTest.php new file mode 100644 index 0000000..b6356d7 --- /dev/null +++ b/tests/Filter/FilterOperatorXOrTest.php @@ -0,0 +1,22 @@ +assertFalse($operator->initialValue()); + $this->assertTrue($operator->exitConditionValue()); + $this->assertFalse($operator->calculate(false, false)); + $this->assertFalse($operator->calculate(true, true)); + $this->assertTrue($operator->calculate(true, false)); + $this->assertTrue($operator->calculate(false, true)); + } +} diff --git a/tests/Stub/AbstractItemToArrayStub.php b/tests/Stub/AbstractItemToArrayStub.php index 732188f..8c52a02 100644 --- a/tests/Stub/AbstractItemToArrayStub.php +++ b/tests/Stub/AbstractItemToArrayStub.php @@ -19,7 +19,7 @@ protected static function getBuilders(): array [ 'extraData' => self::buildGetterRequiredField( 'extraData', - fn(array $value): ToArrayStub => ToArrayStub::create( + static fn(array $value): ToArrayStub => ToArrayStub::create( $id = ToIntStub::fromInt((int) $value['id']), ToStringStub::create($id, $value['description']) ) diff --git a/tests/Stub/AbstractItemToArrayStubBase.php b/tests/Stub/AbstractItemToArrayStubBase.php index d1139a2..6853fd9 100644 --- a/tests/Stub/AbstractItemToArrayStubBase.php +++ b/tests/Stub/AbstractItemToArrayStubBase.php @@ -26,12 +26,12 @@ protected static function getBuilders(): array 'id' => self::buildIntGetter('id'), 'name' => self::buildGetterRequiredField( 'name', - fn(string $value): ToStringStub => ToStringStub::create(ToIntStub::fromInt(1000), $value) + static fn(string $value): ToStringStub => ToStringStub::create(ToIntStub::fromInt(1000), $value) ), 'verified' => self::buildBoolGetter('verified'), 'industryId' => self::buildGetterRequiredField( 'industryId', - fn(int $value): ToIntStub => ToIntStub::fromInt($value) + static fn(int $value): ToIntStub => ToIntStub::fromInt($value) ), ]; } diff --git a/tests/Stub/AutoSortedCollectionStub.php b/tests/Stub/AutoSortedCollectionStub.php index 282ad84..bf2994a 100644 --- a/tests/Stub/AutoSortedCollectionStub.php +++ b/tests/Stub/AutoSortedCollectionStub.php @@ -13,8 +13,9 @@ final class AutoSortedCollectionStub extends AbstractCollection public function append($value): void { match (true) { - is_string($value), is_int($value) => $this->offsetSet($value, $value), - default => parent::append($value) + is_string($value), + is_int($value) => $this->offsetSet($value, $value), + default => parent::append($value) }; } diff --git a/tests/Stub/DTOCollectionStub.php b/tests/Stub/DTOCollectionStub.php index e321192..c7630f9 100644 --- a/tests/Stub/DTOCollectionStub.php +++ b/tests/Stub/DTOCollectionStub.php @@ -40,7 +40,7 @@ public function append($value): void { match (true) { is_array($value) => $this->append(DTOStub::fromArray($value)), - $value instanceof DTOStub => $this->offsetSet($value->field(), $value), + $value instanceof DTOStub => $this->offsetSet($value->field, $value), default => throw new InvalidArgumentException(sprintf(self::INVALID, DTOStub::class)) }; } diff --git a/tests/Stub/DTOStub.php b/tests/Stub/DTOStub.php index be88455..7d5d57e 100644 --- a/tests/Stub/DTOStub.php +++ b/tests/Stub/DTOStub.php @@ -7,7 +7,7 @@ final class DTOStub implements ToArray { - public function __construct(private string $field, private mixed $value) + public function __construct(public readonly string $field, public readonly mixed $value) { } @@ -16,16 +16,6 @@ public static function fromArray(array $data): self return new self($data['field'], $data['value']); } - public function field(): string - { - return $this->field; - } - - public function value(): mixed - { - return $this->value; - } - public function toArray(): array { return [ diff --git a/tests/Stub/FilterItemStub.php b/tests/Stub/FilterItemStub.php index 6a6ae23..9b029a6 100644 --- a/tests/Stub/FilterItemStub.php +++ b/tests/Stub/FilterItemStub.php @@ -7,7 +7,7 @@ final class FilterItemStub implements FilterItem { - public function __construct(private string $itemKey, private ?string $extra = null) + public function __construct(private readonly string $itemKey, private readonly ?string $extra = null) { } diff --git a/tests/Stub/FromArrayStub.php b/tests/Stub/FromArrayStub.php index 9e2efe3..bc0d9a1 100644 --- a/tests/Stub/FromArrayStub.php +++ b/tests/Stub/FromArrayStub.php @@ -7,7 +7,7 @@ final class FromArrayStub implements FromArray { - public function __construct(private int $id, private string $name) + public function __construct(public readonly int $id, public readonly string $name) { } @@ -15,14 +15,4 @@ public static function fromArray(array $data): self { return new self((int) $data['id'], (string) $data['name']); } - - public function id(): int - { - return $this->id; - } - - public function name(): string - { - return $this->name; - } } diff --git a/tests/Stub/MapperStub.php b/tests/Stub/MapperStub.php index f34e5b6..735624d 100644 --- a/tests/Stub/MapperStub.php +++ b/tests/Stub/MapperStub.php @@ -12,8 +12,8 @@ protected function getCallers(string $collectionClass): ?MapperCallers { if (DTOCollectionStub::class === $collectionClass) { return new MapperCallers( - fn(DTOStub $dto): string => $dto->field(), - fn(DTOStub $dto): int => $dto->value() + fn(DTOStub $dto): string => $dto->field, + fn(DTOStub $dto): int => $dto->value ); } diff --git a/tests/Stub/ToArrayStub.php b/tests/Stub/ToArrayStub.php index 7303af2..cf0eb4d 100644 --- a/tests/Stub/ToArrayStub.php +++ b/tests/Stub/ToArrayStub.php @@ -7,7 +7,7 @@ final class ToArrayStub implements ToArray { - private function __construct(private ToIntStub $id, private ToStringStub $data) + private function __construct(private readonly ToIntStub $id, private readonly ToStringStub $data) { } diff --git a/tests/Stub/ToIntStub.php b/tests/Stub/ToIntStub.php index 2be4acf..cb67d01 100644 --- a/tests/Stub/ToIntStub.php +++ b/tests/Stub/ToIntStub.php @@ -7,7 +7,7 @@ final class ToIntStub implements ToInt { - private function __construct(private int $value) + private function __construct(private readonly int $value) { } diff --git a/tests/Stub/ToStringStub.php b/tests/Stub/ToStringStub.php index b639517..c23a904 100644 --- a/tests/Stub/ToStringStub.php +++ b/tests/Stub/ToStringStub.php @@ -7,7 +7,7 @@ final class ToStringStub implements ToString { - private function __construct(private ToIntStub $id, private string $value) + private function __construct(private readonly ToIntStub $id, private readonly string $value) { }