From 814838dbb226068391152cfa1438e435ab5f8b82 Mon Sep 17 00:00:00 2001 From: codeliner Date: Tue, 18 Feb 2020 22:19:53 +0100 Subject: [PATCH 01/21] Add info about versions and missing tests in README --- README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.md b/README.md index 82a0e5d..9ff9fb0 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,12 @@ Generate Immutable Objects with ease! composer require event-engine/php-data ``` +## Versions +- 1.x uses method return type hints to detect ImmutableRecord property types + - Use this version in PHP 7.2 - PHP 7.3 environments +- 2.x makes use of PHP 7.4 property type hints + - Use this version in >= PHP 7.4 environments + ## PHPStorm Templates The `EventEngine\Data` package contains a set of live templates specifically designed to work together with the `EventEngine\Data\ImmutableRecord`. @@ -22,3 +28,9 @@ Please find the `settings.zip` [here](https://github.com/event-engine/php-data/b ## Usage Usage is described in the [documentation](https://event-engine.io/api/immutable_state.html) + +## No Tests? + +Looks strange, right? A stable open source package without a single test can only be a joke ... +You're right! The situation isn't ideal. However, this is a low level package used by [event-engine/php-json-schema](https://github.com/event-engine/php-json-schema). +The tests included there also cover the implementation of this package. Anyway, a pull request that adds some tests is always welcome! From da9ea08f2c7190254fbdc5e5437a568a852ef9b6 Mon Sep 17 00:00:00 2001 From: codeliner Date: Tue, 18 Feb 2020 22:50:30 +0100 Subject: [PATCH 02/21] Fix deprication message for reflection type --- src/ImmutableRecordLogic.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/ImmutableRecordLogic.php b/src/ImmutableRecordLogic.php index d271844..b250282 100644 --- a/src/ImmutableRecordLogic.php +++ b/src/ImmutableRecordLogic.php @@ -277,9 +277,19 @@ private static function buildPropTypeMap() continue; } + if (! $prop->hasType()) { + throw new \RuntimeException( + \sprintf( + 'Missing type hint for property %s of record %s', + $prop->getName(), + __CLASS__ + ) + ); + } + $type = $prop->getType(); - $propTypeMap[$prop->getName()] = [(string) $type, self::isScalarType((string) $type), $type->allowsNull()]; + $propTypeMap[$prop->getName()] = [$type->getName(), self::isScalarType($type->getName()), $type->allowsNull()]; } return $propTypeMap; From 5c466647e8e17a077ca05b6bf07ef27783803dfa Mon Sep 17 00:00:00 2001 From: Arne De Smedt Date: Mon, 24 Feb 2020 13:10:42 +0100 Subject: [PATCH 03/21] extract type from property in seperate method --- src/ImmutableRecordLogic.php | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/src/ImmutableRecordLogic.php b/src/ImmutableRecordLogic.php index b250282..615219d 100644 --- a/src/ImmutableRecordLogic.php +++ b/src/ImmutableRecordLogic.php @@ -277,17 +277,7 @@ private static function buildPropTypeMap() continue; } - if (! $prop->hasType()) { - throw new \RuntimeException( - \sprintf( - 'Missing type hint for property %s of record %s', - $prop->getName(), - __CLASS__ - ) - ); - } - - $type = $prop->getType(); + $type = self::typeFromProperty($prop, $refObj); $propTypeMap[$prop->getName()] = [$type->getName(), self::isScalarType($type->getName()), $type->allowsNull()]; } @@ -295,6 +285,30 @@ private static function buildPropTypeMap() return $propTypeMap; } + private static function typeFromProperty(\ReflectionProperty $prop, \ReflectionClass $refObj) : \ReflectionType + { + if ($prop->hasType()) { + return $prop->getType(); + } + + if ($refObj->hasMethod($prop->getName())) { + $method = $refObj->getMethod($prop->getName()); + + if ($method->hasReturnType()) { + return $method->getReturnType(); + } + } + + throw new \RuntimeException( + \sprintf( + 'Could not find a type for property %s of record %s. ' . + 'No type hint or getter method with a return type is given.', + $prop->getName(), + __CLASS__ + ) + ); + } + private static function isScalarType(string $type): bool { switch ($type) { From e1e1235ef2ea7f784e03d32f0c9f7c58cef8af2f Mon Sep 17 00:00:00 2001 From: Arne De Smedt Date: Mon, 24 Feb 2020 15:33:18 +0100 Subject: [PATCH 04/21] write test for extracting the type of a property --- README.md | 6 -- tests/ImmutableRecordLogicTest.php | 73 +++++++++++++++++++ tests/Stub/ImmutableRecordWithNoTypes.php | 32 ++++++++ .../Stub/ImmutableRecordWithTypedGetters.php | 32 ++++++++ tests/Stub/TypeHintedImmutableRecord.php | 32 ++++++++ 5 files changed, 169 insertions(+), 6 deletions(-) create mode 100644 tests/ImmutableRecordLogicTest.php create mode 100644 tests/Stub/ImmutableRecordWithNoTypes.php create mode 100644 tests/Stub/ImmutableRecordWithTypedGetters.php create mode 100644 tests/Stub/TypeHintedImmutableRecord.php diff --git a/README.md b/README.md index 9ff9fb0..3bc38af 100644 --- a/README.md +++ b/README.md @@ -28,9 +28,3 @@ Please find the `settings.zip` [here](https://github.com/event-engine/php-data/b ## Usage Usage is described in the [documentation](https://event-engine.io/api/immutable_state.html) - -## No Tests? - -Looks strange, right? A stable open source package without a single test can only be a joke ... -You're right! The situation isn't ideal. However, this is a low level package used by [event-engine/php-json-schema](https://github.com/event-engine/php-json-schema). -The tests included there also cover the implementation of this package. Anyway, a pull request that adds some tests is always welcome! diff --git a/tests/ImmutableRecordLogicTest.php b/tests/ImmutableRecordLogicTest.php new file mode 100644 index 0000000..19a7515 --- /dev/null +++ b/tests/ImmutableRecordLogicTest.php @@ -0,0 +1,73 @@ +data = [ + 'name' => 'test', + 'version' => 1, + ]; + } + + /** + * @test + */ + public function it_detects_type_hinted_properties() + { + $typeHinted = TypeHintedImmutableRecord::fromArray($this->data); + + $this->data['type'] = null; + + $this->assertEquals( + $this->data, + $typeHinted->toArray() + ); + } + + /** + * @test + */ + public function it_detects_coupled_getters_for_properties() + { + $typedGetters = ImmutableRecordWithTypedGetters::fromArray($this->data); + + $this->data['type'] = null; + + $this->assertEquals( + $this->data, + $typedGetters->toArray() + ); + } + + /** + * @test + */ + public function it_throws_an_exception_if_no_type_hint_was_found() + { + $this->expectException(\RuntimeException::class); + $this->expectExceptionMessage( + \sprintf( + 'Could not find a type for property %s of record %s. ' . + 'No type hint or getter method with a return type is given.', + 'name', + ImmutableRecordWithNoTypes::class + ) + ); + + ImmutableRecordWithNoTypes::fromArray($this->data); + } +} diff --git a/tests/Stub/ImmutableRecordWithNoTypes.php b/tests/Stub/ImmutableRecordWithNoTypes.php new file mode 100644 index 0000000..202355c --- /dev/null +++ b/tests/Stub/ImmutableRecordWithNoTypes.php @@ -0,0 +1,32 @@ +name; + } + + public function type() + { + return $this->type; + } + + public function version() + { + return $this->version; + } +} diff --git a/tests/Stub/ImmutableRecordWithTypedGetters.php b/tests/Stub/ImmutableRecordWithTypedGetters.php new file mode 100644 index 0000000..20553bc --- /dev/null +++ b/tests/Stub/ImmutableRecordWithTypedGetters.php @@ -0,0 +1,32 @@ +name; + } + + public function type() : ?string + { + return $this->type; + } + + public function version() : int + { + return $this->version; + } +} diff --git a/tests/Stub/TypeHintedImmutableRecord.php b/tests/Stub/TypeHintedImmutableRecord.php new file mode 100644 index 0000000..df58fef --- /dev/null +++ b/tests/Stub/TypeHintedImmutableRecord.php @@ -0,0 +1,32 @@ +name; + } + + public function type() + { + return $this->type; + } + + public function version() + { + return $this->version; + } +} From a0118d47c3874fa44192501d5201644e71e59b74 Mon Sep 17 00:00:00 2001 From: codeliner Date: Sat, 7 Mar 2020 17:30:48 +0100 Subject: [PATCH 05/21] Add psalm annotations --- src/ImmutableRecord.php | 6 ++++++ src/ImmutableRecordLogic.php | 26 ++++++++++++++++---------- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/ImmutableRecord.php b/src/ImmutableRecord.php index 9aae1c6..ffb8b86 100644 --- a/src/ImmutableRecord.php +++ b/src/ImmutableRecord.php @@ -11,6 +11,12 @@ namespace EventEngine\Data; +/** + * Interface ImmutableRecord + * + * @package EventEngine\Data + * @psalm-immutable + */ interface ImmutableRecord { const PHP_TYPE_STRING = 'string'; diff --git a/src/ImmutableRecordLogic.php b/src/ImmutableRecordLogic.php index 615219d..07a9610 100644 --- a/src/ImmutableRecordLogic.php +++ b/src/ImmutableRecordLogic.php @@ -11,6 +11,12 @@ namespace EventEngine\Data; +/** + * Trait ImmutableRecordLogic + * @package EventEngine\Data + * + * @psalm-immutable + */ trait ImmutableRecordLogic { /** @@ -36,7 +42,7 @@ private function init(): void * @param array $recordData * @return self */ - public static function fromRecordData(array $recordData) + public static function fromRecordData(array $recordData): self { return new self($recordData); } @@ -45,7 +51,7 @@ public static function fromRecordData(array $recordData) * @param array $nativeData * @return self */ - public static function fromArray(array $nativeData) + public static function fromArray(array $nativeData): self { return new self(null, $nativeData); } @@ -73,7 +79,7 @@ private function __construct(array $recordData = null, array $nativeData = null) * @param array $recordData * @return self */ - public function with(array $recordData) + public function with(array $recordData): self { $copy = clone $this; $copy->setRecordData($recordData); @@ -127,7 +133,7 @@ public function equals(ImmutableRecord $other): bool return $this->toArray() === $other->toArray(); } - private function setRecordData(array $recordData) + private function setRecordData(array $recordData): void { foreach ($recordData as $key => $value) { $this->assertType($key, $value); @@ -135,7 +141,7 @@ private function setRecordData(array $recordData) } } - private function setNativeData(array $nativeData) + private function setNativeData(array $nativeData): void { $recordData = []; $arrayPropItemTypeMap = self::getArrayPropItemTypeMapFromMethodOrCache(); @@ -182,7 +188,7 @@ private function setNativeData(array $nativeData) $this->setRecordData($recordData); } - private function assertAllNotNull() + private function assertAllNotNull(): void { foreach (self::$__propTypeMap as $key => [$type, $isNative, $isNullable]) { if (null === $this->{$key} && ! $isNullable) { @@ -195,7 +201,7 @@ private function assertAllNotNull() } } - private function assertType(string $key, $value) + private function assertType(string $key, $value): void { if (! isset(self::$__propTypeMap[$key])) { throw new \InvalidArgumentException(\sprintf( @@ -264,7 +270,7 @@ private function isType(string $type, string $key, $value): bool } } - private static function buildPropTypeMap() + private static function buildPropTypeMap(): array { $refObj = new \ReflectionClass(__CLASS__); @@ -383,12 +389,12 @@ private static function getArrayPropItemTypeMapFromMethodOrCache(): array } /** - * @var array + * @var array|null */ private static $__propTypeMap; /** - * @var array + * @var array|null */ private static $__arrayPropItemTypeMap; } From 62e4115449b914d66ca1f21763c51948a8852444 Mon Sep 17 00:00:00 2001 From: codeliner Date: Sat, 7 Mar 2020 18:15:41 +0100 Subject: [PATCH 06/21] More tests and bugfixes --- .php_cs.cache | 1 + .travis.yml | 38 ++++++ composer.json | 4 +- src/DataConverter.php | 2 +- src/ImmutableRecord.php | 2 +- src/ImmutableRecordDataConverter.php | 4 +- src/ImmutableRecordLogic.php | 16 +-- tests/ImmutableRecordLogicTest.php | 59 +++++++- tests/Stub/ImmutableItem.php | 26 ++++ tests/Stub/ImmutableRecordWithNoTypes.php | 26 +++- .../Stub/ImmutableRecordWithTypedGetters.php | 38 +++++- tests/Stub/TypeHintedImmutableRecord.php | 62 ++++++++- ...eHintedImmutableRecordWithValueObjects.php | 92 +++++++++++++ tests/Stub/ValueObject/Access.php | 48 +++++++ tests/Stub/ValueObject/ItemList.php | 129 ++++++++++++++++++ tests/Stub/ValueObject/Name.php | 48 +++++++ tests/Stub/ValueObject/Percentage.php | 48 +++++++ tests/Stub/ValueObject/Type.php | 48 +++++++ tests/Stub/ValueObject/Version.php | 48 +++++++ 19 files changed, 718 insertions(+), 21 deletions(-) create mode 100644 .php_cs.cache create mode 100644 .travis.yml create mode 100644 tests/Stub/ImmutableItem.php create mode 100644 tests/Stub/TypeHintedImmutableRecordWithValueObjects.php create mode 100644 tests/Stub/ValueObject/Access.php create mode 100644 tests/Stub/ValueObject/ItemList.php create mode 100644 tests/Stub/ValueObject/Name.php create mode 100644 tests/Stub/ValueObject/Percentage.php create mode 100644 tests/Stub/ValueObject/Type.php create mode 100644 tests/Stub/ValueObject/Version.php diff --git a/.php_cs.cache b/.php_cs.cache new file mode 100644 index 0000000..36f8ed5 --- /dev/null +++ b/.php_cs.cache @@ -0,0 +1 @@ +{"php":"7.4.1","version":"2.16.1:v2.16.1#c8afb599858876e95e8ebfcd97812d383fa23f02","indent":" ","lineEnding":"\n","rules":{"blank_line_after_namespace":true,"braces":true,"class_definition":true,"constant_case":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline"},"no_break_comment":true,"no_closing_tag":true,"no_spaces_after_function_name":true,"no_spaces_inside_parenthesis":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_import_per_statement":true,"single_line_after_imports":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"visibility_required":true,"encoding":true,"full_opening_tag":true},"hashes":{"src\/DataConverter.php":3585776980,"src\/ImmutableRecordDataConverter.php":2264693083,"src\/ImmutableRecord.php":2694122341,"src\/ImmutableRecordLogic.php":3915115104}} \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..bbaf530 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,38 @@ +language: php + +matrix: + fast_finish: true + include: + - php: 7.4 + env: + - DEPENDENCIES="" + - EXECUTE_CS_CHECK=true + - TEST_COVERAGE=true + +cache: + directories: + - $HOME/.composer/cache + - $HOME/.php-cs-fixer + - $HOME/.local + +before_script: + - mkdir -p "$HOME/.php-cs-fixer" + - phpenv config-rm xdebug.ini + - composer self-update + - composer update --prefer-source $DEPENDENCIES + +script: + - if [[ $TEST_COVERAGE == 'true' ]]; then php -dzend_extension=xdebug.so ./vendor/bin/phpunit --coverage-text --coverage-clover ./build/logs/clover.xml; else ./vendor/bin/phpunit; fi +# - if [[ $EXECUTE_CS_CHECK == 'true' ]]; then ./vendor/bin/php-cs-fixer fix -v --diff --dry-run; fi +# - if [[ $EXECUTE_CS_CHECK == 'true' ]]; then ./vendor/bin/docheader check examples/ src/ tests/; fi + +after_success: + - if [[ $TEST_COVERAGE == 'true' ]]; then php vendor/bin/coveralls -v; fi + +notifications: + webhooks: + urls: + - https://webhooks.gitter.im/e/61c75218816eebde4486 + on_success: change # options: [always|never|change] default: always + on_failure: always # options: [always|never|change] default: always + on_start: never # options: [always|never|change] default: always diff --git a/composer.json b/composer.json index 9f955e1..82f6145 100644 --- a/composer.json +++ b/composer.json @@ -43,8 +43,8 @@ "@test" ], "docheader": "vendor/bin/docheader check src/ tests/", - "cs": "php-cs-fixer fix -v --diff --dry-run", - "cs-fix": "php-cs-fixer fix -v --diff", + "cs": "php-cs-fixer fix src -v --diff --dry-run", + "cs-fix": "php-cs-fixer fix src -v --diff", "test": "vendor/bin/phpunit" } } diff --git a/src/DataConverter.php b/src/DataConverter.php index f9f3194..6b5c229 100644 --- a/src/DataConverter.php +++ b/src/DataConverter.php @@ -1,7 +1,7 @@ + * (c) 2018-2020 prooph software GmbH * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. diff --git a/src/ImmutableRecord.php b/src/ImmutableRecord.php index ffb8b86..0abe53e 100644 --- a/src/ImmutableRecord.php +++ b/src/ImmutableRecord.php @@ -1,7 +1,7 @@ + * (c) 2018-2020 prooph software GmbH * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. diff --git a/src/ImmutableRecordDataConverter.php b/src/ImmutableRecordDataConverter.php index adb3165..73526aa 100644 --- a/src/ImmutableRecordDataConverter.php +++ b/src/ImmutableRecordDataConverter.php @@ -1,7 +1,7 @@ + * (c) 2018-2020 prooph software GmbH * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -37,7 +37,7 @@ public function canConvertTypeToData(string $type): bool { $class = $this->getClassOfType($type); - if(!class_exists($class)) { + if (!class_exists($class)) { return false; } diff --git a/src/ImmutableRecordLogic.php b/src/ImmutableRecordLogic.php index 07a9610..717647b 100644 --- a/src/ImmutableRecordLogic.php +++ b/src/ImmutableRecordLogic.php @@ -1,7 +1,7 @@ + * (c) 2018-2020 prooph software GmbH * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -100,24 +100,24 @@ public function toArray(): array case ImmutableRecord::PHP_TYPE_BOOL: case ImmutableRecord::PHP_TYPE_ARRAY: if (\array_key_exists($key, $arrayPropItemTypeMap) && ! self::isScalarType($arrayPropItemTypeMap[$key])) { - if ($isNullable && $this->{$key}() === null) { + if ($isNullable && $this->{$key} === null) { $nativeData[$key] = null; continue 2; } $nativeData[$key] = \array_map(function ($item) use ($key, &$arrayPropItemTypeMap) { return $this->voTypeToNative($item, $key, $arrayPropItemTypeMap[$key]); - }, $this->{$key}()); + }, $this->{$key}); } else { - $nativeData[$key] = $this->{$key}(); + $nativeData[$key] = $this->{$key}; } break; default: - if ($isNullable && $this->{$key}() === null) { + if ($isNullable && (! isset($this->{$key}))) { $nativeData[$key] = null; continue 2; } - $nativeData[$key] = $this->voTypeToNative($this->{$key}(), $key, $type); + $nativeData[$key] = $this->voTypeToNative($this->{$key}, $key, $type); } } @@ -126,7 +126,7 @@ public function toArray(): array public function equals(ImmutableRecord $other): bool { - if(get_class($this) !== get_class($other)) { + if (get_class($this) !== get_class($other)) { return false; } @@ -191,7 +191,7 @@ private function setNativeData(array $nativeData): void private function assertAllNotNull(): void { foreach (self::$__propTypeMap as $key => [$type, $isNative, $isNullable]) { - if (null === $this->{$key} && ! $isNullable) { + if (! isset($this->{$key}) && ! $isNullable) { throw new \InvalidArgumentException(\sprintf( 'Missing record data for key %s of record %s.', $key, diff --git a/tests/ImmutableRecordLogicTest.php b/tests/ImmutableRecordLogicTest.php index 19a7515..210ef1f 100644 --- a/tests/ImmutableRecordLogicTest.php +++ b/tests/ImmutableRecordLogicTest.php @@ -1,12 +1,26 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ declare(strict_types=1); namespace EventEngineTest\Data; +use EventEngineTest\Data\Stub\ImmutableItem; use EventEngineTest\Data\Stub\ImmutableRecordWithNoTypes; use EventEngineTest\Data\Stub\ImmutableRecordWithTypedGetters; use EventEngineTest\Data\Stub\TypeHintedImmutableRecord; +use EventEngineTest\Data\Stub\TypeHintedImmutableRecordWithValueObjects; +use EventEngineTest\Data\Stub\ValueObject\Access; +use EventEngineTest\Data\Stub\ValueObject\ItemList; +use EventEngineTest\Data\Stub\ValueObject\Name; +use EventEngineTest\Data\Stub\ValueObject\Percentage; +use EventEngineTest\Data\Stub\ValueObject\Type; +use EventEngineTest\Data\Stub\ValueObject\Version; use PHPUnit\Framework\TestCase; final class ImmutableRecordLogicTest extends TestCase @@ -20,6 +34,8 @@ protected function setUp() $this->data = [ 'name' => 'test', 'version' => 1, + 'itemList' => [['name' => 'one']], + 'access' => true, ]; } @@ -31,6 +47,7 @@ public function it_detects_type_hinted_properties() $typeHinted = TypeHintedImmutableRecord::fromArray($this->data); $this->data['type'] = null; + $this->data['percentage'] = 0.5; $this->assertEquals( $this->data, @@ -46,6 +63,7 @@ public function it_detects_coupled_getters_for_properties() $typedGetters = ImmutableRecordWithTypedGetters::fromArray($this->data); $this->data['type'] = null; + $this->data['percentage'] = 0.5; $this->assertEquals( $this->data, @@ -53,6 +71,45 @@ public function it_detects_coupled_getters_for_properties() ); } + /** + * @test + */ + public function it_can_handle_value_objects() + { + $valueObjects = TypeHintedImmutableRecordWithValueObjects::fromArray($this->data); + + $this->data['type'] = null; + $this->data['percentage'] = 0.5; + + $this->assertEquals( + $this->data, + $valueObjects->toArray() + ); + } + + /** + * @test + */ + public function it_takes_value_object_as_initialization_params() + { + $valueObjects = TypeHintedImmutableRecordWithValueObjects::fromRecordData([ + 'name' => Name::fromString($this->data['name']), + 'type' => Type::fromString('value_object'), + 'version' => Version::fromInt($this->data['version']), + 'access' => Access::fromBool($this->data['access']), + 'percentage' => Percentage::fromFloat(0.9), + 'itemList' => ItemList::fromItems(ImmutableItem::fromRecordData(['name' => 'one'])), + ]); + + $this->data['type'] = 'value_object'; + $this->data['percentage'] = 0.9; + + $this->assertEquals( + $this->data, + $valueObjects->toArray() + ); + } + /** * @test */ diff --git a/tests/Stub/ImmutableItem.php b/tests/Stub/ImmutableItem.php new file mode 100644 index 0000000..32cdbfb --- /dev/null +++ b/tests/Stub/ImmutableItem.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +declare(strict_types=1); + +namespace EventEngineTest\Data\Stub; + +use EventEngine\Data\ImmutableRecord; +use EventEngine\Data\ImmutableRecordLogic; + +final class ImmutableItem implements ImmutableRecord +{ + use ImmutableRecordLogic; + + private $name; + + public function name(): string + { + return $this->name; + } +} diff --git a/tests/Stub/ImmutableRecordWithNoTypes.php b/tests/Stub/ImmutableRecordWithNoTypes.php index 202355c..3a30282 100644 --- a/tests/Stub/ImmutableRecordWithNoTypes.php +++ b/tests/Stub/ImmutableRecordWithNoTypes.php @@ -1,5 +1,11 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ declare(strict_types=1); namespace EventEngineTest\Data\Stub; @@ -14,6 +20,9 @@ final class ImmutableRecordWithNoTypes implements ImmutableRecord private $name; private $type; private $version; + private $itemList; + private $access; + private $percentage; public function name() { @@ -29,4 +38,19 @@ public function version() { return $this->version; } + + public function itemList() + { + return $this->itemList; + } + + public function access() + { + return $this->access; + } + + public function percentage() + { + return $this->percentage; + } } diff --git a/tests/Stub/ImmutableRecordWithTypedGetters.php b/tests/Stub/ImmutableRecordWithTypedGetters.php index 20553bc..d7260f4 100644 --- a/tests/Stub/ImmutableRecordWithTypedGetters.php +++ b/tests/Stub/ImmutableRecordWithTypedGetters.php @@ -1,5 +1,11 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ declare(strict_types=1); namespace EventEngineTest\Data\Stub; @@ -14,6 +20,21 @@ final class ImmutableRecordWithTypedGetters implements ImmutableRecord private $name; private $type; private $version; + private $itemList; + private $access; + private $percentage; + + private static function arrayPropItemTypeMap(): array + { + return ['itemList' => ImmutableItem::class]; + } + + private function init(): void + { + if(null === $this->percentage) { + $this->percentage = 0.5; + } + } public function name() : string { @@ -29,4 +50,19 @@ public function version() : int { return $this->version; } + + public function itemList(): array + { + return $this->itemList; + } + + public function access(): bool + { + return $this->access; + } + + public function percentage(): float + { + return $this->percentage; + } } diff --git a/tests/Stub/TypeHintedImmutableRecord.php b/tests/Stub/TypeHintedImmutableRecord.php index df58fef..5496e80 100644 --- a/tests/Stub/TypeHintedImmutableRecord.php +++ b/tests/Stub/TypeHintedImmutableRecord.php @@ -1,5 +1,11 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ declare(strict_types=1); namespace EventEngineTest\Data\Stub; @@ -14,19 +20,67 @@ final class TypeHintedImmutableRecord implements ImmutableRecord private string $name; private ?string $type = null; private int $version; + private array $itemList; + private bool $access; + private float $percentage; + + private static function arrayPropItemTypeMap(): array + { + return ['itemList' => ImmutableItem::class]; + } - public function name() + private function init(): void + { + if(!isset($this->percentage)) { + $this->percentage = 0.5; + } + } + + /** + * @return string + */ + public function name(): string { return $this->name; } - public function type() + /** + * @return string|null + */ + public function type(): ?string { return $this->type; } - public function version() + /** + * @return int + */ + public function version(): int { return $this->version; } + + /** + * @return array + */ + public function itemList(): array + { + return $this->itemList; + } + + /** + * @return bool + */ + public function access(): bool + { + return $this->access; + } + + /** + * @return float + */ + public function percentage(): float + { + return $this->percentage; + } } diff --git a/tests/Stub/TypeHintedImmutableRecordWithValueObjects.php b/tests/Stub/TypeHintedImmutableRecordWithValueObjects.php new file mode 100644 index 0000000..7385553 --- /dev/null +++ b/tests/Stub/TypeHintedImmutableRecordWithValueObjects.php @@ -0,0 +1,92 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +declare(strict_types=1); + +namespace EventEngineTest\Data\Stub; + +use EventEngine\Data\ImmutableRecord; +use EventEngine\Data\ImmutableRecordLogic; +use EventEngineTest\Data\Stub\ValueObject\Access; +use EventEngineTest\Data\Stub\ValueObject\ItemList; +use EventEngineTest\Data\Stub\ValueObject\Name; +use EventEngineTest\Data\Stub\ValueObject\Percentage; +use EventEngineTest\Data\Stub\ValueObject\Type; +use EventEngineTest\Data\Stub\ValueObject\Version; + +final class TypeHintedImmutableRecordWithValueObjects implements ImmutableRecord +{ + use ImmutableRecordLogic; + + private Name $name; + + private ?Type $type; + + private Version $version; + + private ItemList $itemList; + + private Access $access; + + private Percentage $percentage; + + private function init(): void + { + if(!isset($this->percentage)) { + $this->percentage = Percentage::fromFloat(0.5); + } + } + + /** + * @return Name + */ + public function name(): Name + { + return $this->name; + } + + /** + * @return Type|null + */ + public function type(): ?Type + { + return $this->type; + } + + /** + * @return Version + */ + public function version(): Version + { + return $this->version; + } + + /** + * @return ItemList + */ + public function itemList(): ItemList + { + return $this->itemList; + } + + /** + * @return Access + */ + public function access(): Access + { + return $this->access; + } + + /** + * @return Percentage + */ + public function percentage(): Percentage + { + return $this->percentage; + } +} diff --git a/tests/Stub/ValueObject/Access.php b/tests/Stub/ValueObject/Access.php new file mode 100644 index 0000000..7e09d31 --- /dev/null +++ b/tests/Stub/ValueObject/Access.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +declare(strict_types=1); + +namespace EventEngineTest\Data\Stub\ValueObject; + +final class Access +{ + private $access; + + public static function fromBool(bool $access): self + { + return new self($access); + } + + private function __construct(bool $access) + { + $this->access = $access; + } + + public function toBool(): bool + { + return $this->access; + } + + /** + * @param mixed $other + */ + public function equals($other): bool + { + if(!$other instanceof self) { + return false; + } + + return $this->access === $other->access; + } + + public function __toString(): string + { + return $this->access ? 'TRUE' : 'FALSE'; + } +} diff --git a/tests/Stub/ValueObject/ItemList.php b/tests/Stub/ValueObject/ItemList.php new file mode 100644 index 0000000..70f3d97 --- /dev/null +++ b/tests/Stub/ValueObject/ItemList.php @@ -0,0 +1,129 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +declare(strict_types=1); + +namespace EventEngineTest\Data\Stub\ValueObject; + +use EventEngineTest\Data\Stub\ImmutableItem; + +final class ItemList +{ + /** + * @var ImmutableItem[] + */ + private $items; + + public static function fromArray(array $items): self + { + return new self(...array_map(function (array $item) { + return ImmutableItem::fromArray($item); + }, $items)); + } + + public static function fromItems(ImmutableItem ...$items): self + { + return new self(...$items); + } + + public static function emptyList(): self + { + return new self(); + } + + private function __construct(ImmutableItem ...$items) + { + $this->items = $items; + } + + public function push(ImmutableItem $item): self + { + $copy = clone $this; + $copy->items[] = $item; + return $copy; + } + + public function pop(): self + { + $copy = clone $this; + \array_pop($copy->items); + return $copy; + } + + public function first(): ?ImmutableItem + { + return $this->items[0] ?? null; + } + + public function last(): ?ImmutableItem + { + if (count($this->items) === 0) { + return null; + } + + return $this->items[count($this->items) - 1]; + } + + public function contains(ImmutableItem $item): bool + { + foreach ($this->items as $existingItem) { + if ($existingItem->equals($item)) { + return true; + } + } + + return false; + } + + public function filter(callable $filter): self + { + $filteredItems = []; + + foreach ($this->items as $item) { + if ($filter($item)) { + $filteredItems[] = $item; + } + } + + $copy = clone $this; + $copy->items = $filteredItems; + return $copy; + } + + /** + * @return ImmutableItem[] + */ + public function items(): array + { + return $this->items; + } + + public function toArray(): array + { + return \array_map(function (ImmutableItem $item) { + return $item->toArray(); + }, $this->items); + } + + /** + * @param mixed $other + */ + public function equals($other): bool + { + if (!$other instanceof self) { + return false; + } + + return $this->toArray() === $other->toArray(); + } + + public function __toString(): string + { + return \json_encode($this->toArray()); + } +} diff --git a/tests/Stub/ValueObject/Name.php b/tests/Stub/ValueObject/Name.php new file mode 100644 index 0000000..fffa4b7 --- /dev/null +++ b/tests/Stub/ValueObject/Name.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +declare(strict_types=1); + +namespace EventEngineTest\Data\Stub\ValueObject; + +final class Name +{ + private $name; + + public static function fromString(string $name): self + { + return new self($name); + } + + private function __construct(string $name) + { + $this->name = $name; + } + + public function toString(): string + { + return $this->name; + } + + /** + * @param mixed $other + */ + public function equals($other): bool + { + if(!$other instanceof self) { + return false; + } + + return $this->name === $other->name; + } + + public function __toString(): string + { + return $this->name; + } +} diff --git a/tests/Stub/ValueObject/Percentage.php b/tests/Stub/ValueObject/Percentage.php new file mode 100644 index 0000000..b5283dd --- /dev/null +++ b/tests/Stub/ValueObject/Percentage.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +declare(strict_types=1); + +namespace EventEngineTest\Data\Stub\ValueObject; + +final class Percentage +{ + private $percentage; + + public static function fromFloat(float $percentage): self + { + return new self($percentage); + } + + private function __construct(float $percentage) + { + $this->percentage = $percentage; + } + + public function toFloat(): float + { + return $this->percentage; + } + + /** + * @param mixed $other + */ + public function equals($other): bool + { + if(!$other instanceof self) { + return false; + } + + return $this->percentage === $other->percentage; + } + + public function __toString(): string + { + return (string)$this->percentage; + } +} diff --git a/tests/Stub/ValueObject/Type.php b/tests/Stub/ValueObject/Type.php new file mode 100644 index 0000000..bfe3e32 --- /dev/null +++ b/tests/Stub/ValueObject/Type.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +declare(strict_types=1); + +namespace EventEngineTest\Data\Stub\ValueObject; + +final class Type +{ + private $type; + + public static function fromString(string $type): self + { + return new self($type); + } + + private function __construct(string $type) + { + $this->type = $type; + } + + public function toString(): string + { + return $this->type; + } + + /** + * @param mixed $other + */ + public function equals($other): bool + { + if(!$other instanceof self) { + return false; + } + + return $this->type === $other->type; + } + + public function __toString(): string + { + return $this->type; + } +} diff --git a/tests/Stub/ValueObject/Version.php b/tests/Stub/ValueObject/Version.php new file mode 100644 index 0000000..70c2fc1 --- /dev/null +++ b/tests/Stub/ValueObject/Version.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +declare(strict_types=1); + +namespace EventEngineTest\Data\Stub\ValueObject; + +final class Version +{ + private $version; + + public static function fromInt(int $version): self + { + return new self($version); + } + + private function __construct(int $version) + { + $this->version = $version; + } + + public function toInt(): int + { + return $this->version; + } + + /** + * @param mixed $other + */ + public function equals($other): bool + { + if(!$other instanceof self) { + return false; + } + + return $this->version === $other->version; + } + + public function __toString(): string + { + return (string)$this->version; + } +} From ecc388740d9363a15957bcef147f2ea6f74c30d5 Mon Sep 17 00:00:00 2001 From: codeliner Date: Sat, 7 Mar 2020 18:24:09 +0100 Subject: [PATCH 07/21] Add travis and coeralls badges --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 3bc38af..6d4efe0 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,8 @@ # EventEngine\Data +[![Build Status](https://travis-ci.com/event-engine/php-data.svg?branch=master)](https://travis-ci.com/event-engine/php-data) +[![Coverage Status](https://coveralls.io/repos/github/event-engine/php-data/badge.svg?branch=master)](https://coveralls.io/github/event-engine/php-data?branch=master) + Generate Immutable Objects with ease! ![Value Object Template vo_string](https://event-engine.io/api/img/vo_string.gif) From d34cab958c39d5987e783d83d25cc435e15badc2 Mon Sep 17 00:00:00 2001 From: codeliner Date: Sat, 7 Mar 2020 18:25:58 +0100 Subject: [PATCH 08/21] Rm php_cs.cache --- .gitignore | 3 ++- .php_cs.cache | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) delete mode 100644 .php_cs.cache diff --git a/.gitignore b/.gitignore index 688d850..c7c729e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .idea +.php_cs.cache composer.lock -vendor \ No newline at end of file +vendor diff --git a/.php_cs.cache b/.php_cs.cache deleted file mode 100644 index 36f8ed5..0000000 --- a/.php_cs.cache +++ /dev/null @@ -1 +0,0 @@ -{"php":"7.4.1","version":"2.16.1:v2.16.1#c8afb599858876e95e8ebfcd97812d383fa23f02","indent":" ","lineEnding":"\n","rules":{"blank_line_after_namespace":true,"braces":true,"class_definition":true,"constant_case":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline"},"no_break_comment":true,"no_closing_tag":true,"no_spaces_after_function_name":true,"no_spaces_inside_parenthesis":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_import_per_statement":true,"single_line_after_imports":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"visibility_required":true,"encoding":true,"full_opening_tag":true},"hashes":{"src\/DataConverter.php":3585776980,"src\/ImmutableRecordDataConverter.php":2264693083,"src\/ImmutableRecord.php":2694122341,"src\/ImmutableRecordLogic.php":3915115104}} \ No newline at end of file From a2d9a0dd87f0961c0706c8cd3ce5e1a7be834ccb Mon Sep 17 00:00:00 2001 From: codeliner Date: Sat, 7 Mar 2020 18:31:59 +0100 Subject: [PATCH 09/21] Bump coveralls version to 2.2 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 82f6145..ee729b4 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,7 @@ "require-dev": { "phpunit/phpunit": "^7.0", "prooph/php-cs-fixer-config": "^0.3", - "satooshi/php-coveralls": "^1.0", + "satooshi/php-coveralls": "^2.2", "malukenho/docheader": "^0.1.4" }, "autoload": { From a6e62d90add6f9aa46c5d827db88b8bcff84bd6c Mon Sep 17 00:00:00 2001 From: codeliner Date: Sat, 7 Mar 2020 18:40:01 +0100 Subject: [PATCH 10/21] Add coeralls.yml --- .coveralls.yml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .coveralls.yml diff --git a/.coveralls.yml b/.coveralls.yml new file mode 100644 index 0000000..75aefc8 --- /dev/null +++ b/.coveralls.yml @@ -0,0 +1,3 @@ +# for php-coveralls +service_name: travis-ci +coverage_clover: build/logs/clover.xml From d5893e02c3f1f4fe9ca74ae2728bc491e6ce3461 Mon Sep 17 00:00:00 2001 From: codeliner Date: Sat, 7 Mar 2020 18:50:03 +0100 Subject: [PATCH 11/21] Enable cs checks on travis --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index bbaf530..003ea9d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,8 +23,8 @@ before_script: script: - if [[ $TEST_COVERAGE == 'true' ]]; then php -dzend_extension=xdebug.so ./vendor/bin/phpunit --coverage-text --coverage-clover ./build/logs/clover.xml; else ./vendor/bin/phpunit; fi -# - if [[ $EXECUTE_CS_CHECK == 'true' ]]; then ./vendor/bin/php-cs-fixer fix -v --diff --dry-run; fi -# - if [[ $EXECUTE_CS_CHECK == 'true' ]]; then ./vendor/bin/docheader check examples/ src/ tests/; fi + - if [[ $EXECUTE_CS_CHECK == 'true' ]]; then ./vendor/bin/php-cs-fixer fix -v --diff --dry-run; fi + - if [[ $EXECUTE_CS_CHECK == 'true' ]]; then ./vendor/bin/docheader check examples/ src/ tests/; fi after_success: - if [[ $TEST_COVERAGE == 'true' ]]; then php vendor/bin/coveralls -v; fi From cdfdf124ab28a6afa5b953f585b977594f00a4a4 Mon Sep 17 00:00:00 2001 From: codeliner Date: Sat, 7 Mar 2020 19:15:26 +0100 Subject: [PATCH 12/21] Fix cs check command in travis config --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 003ea9d..baaf2fb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,7 +23,7 @@ before_script: script: - if [[ $TEST_COVERAGE == 'true' ]]; then php -dzend_extension=xdebug.so ./vendor/bin/phpunit --coverage-text --coverage-clover ./build/logs/clover.xml; else ./vendor/bin/phpunit; fi - - if [[ $EXECUTE_CS_CHECK == 'true' ]]; then ./vendor/bin/php-cs-fixer fix -v --diff --dry-run; fi + - if [[ $EXECUTE_CS_CHECK == 'true' ]]; then ./vendor/bin/php-cs-fixer fix src -v --diff --dry-run; fi - if [[ $EXECUTE_CS_CHECK == 'true' ]]; then ./vendor/bin/docheader check examples/ src/ tests/; fi after_success: From 1e3ca706920cd7389b55da4affce7d30133d14b2 Mon Sep 17 00:00:00 2001 From: codeliner Date: Sat, 7 Mar 2020 20:32:29 +0100 Subject: [PATCH 13/21] Add tests for ImmutableRecordDataConverter --- tests/ImmutableRecordDataConverterTest.php | 96 ++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 tests/ImmutableRecordDataConverterTest.php diff --git a/tests/ImmutableRecordDataConverterTest.php b/tests/ImmutableRecordDataConverterTest.php new file mode 100644 index 0000000..2756d26 --- /dev/null +++ b/tests/ImmutableRecordDataConverterTest.php @@ -0,0 +1,96 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +declare(strict_types=1); + +namespace EventEngineTest\Data; + +use EventEngine\Data\ImmutableRecordDataConverter; +use EventEngineTest\Data\Stub\TypeHintedImmutableRecordWithValueObjects; +use PHPUnit\Framework\TestCase; +use stdClass; + +final class ImmutableRecordDataConverterTest extends TestCase +{ + private $data = []; + + protected function setUp() + { + parent::setUp(); + + $this->data = [ + 'name' => 'test', + 'version' => 1, + 'itemList' => [['name' => 'one']], + 'access' => true, + ]; + } + + /** + * @test + */ + public function it_converts_immutable_record_to_array() + { + $valueObjects = TypeHintedImmutableRecordWithValueObjects::fromArray($this->data); + + $dataConverter = new ImmutableRecordDataConverter(); + + $this->data['type'] = null; + $this->data['percentage'] = 0.5; + + + $this->assertEquals( + $this->data, + $dataConverter->convertDataToArray( + TypeHintedImmutableRecordWithValueObjects::class, + $valueObjects + ) + ); + } + + /** + * @test + */ + public function it_converts_stdClass_to_array() + { + $obj = new stdClass(); + + $obj->test = "This is a test"; + $obj->msg = "With a message"; + + $this->assertEquals([ + 'test' => "This is a test", + 'msg' => "With a message", + ], + (new ImmutableRecordDataConverter())->convertDataToArray(stdClass::class, $obj) + ); + } + + /** + * @test + */ + public function it_converts_array_to_immutable_record() + { + $dataConverter = new ImmutableRecordDataConverter(); + + $this->assertTrue($dataConverter->canConvertTypeToData(TypeHintedImmutableRecordWithValueObjects::class)); + + $valueObjects = $dataConverter->convertArrayToData( + TypeHintedImmutableRecordWithValueObjects::class, + $this->data + ); + + $this->data['type'] = null; + $this->data['percentage'] = 0.5; + + $this->assertEquals( + $this->data, + $valueObjects->toArray() + ); + } +} From 1ecda749b232fae6a4442eeabe738061a076fbfa Mon Sep 17 00:00:00 2001 From: codeliner Date: Sat, 7 Mar 2020 21:31:27 +0100 Subject: [PATCH 14/21] Fix coveralls path --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index baaf2fb..9dd7095 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,7 +27,7 @@ script: - if [[ $EXECUTE_CS_CHECK == 'true' ]]; then ./vendor/bin/docheader check examples/ src/ tests/; fi after_success: - - if [[ $TEST_COVERAGE == 'true' ]]; then php vendor/bin/coveralls -v; fi + - if [[ $TEST_COVERAGE == 'true' ]]; then php ./vendor/bin/coveralls -v; fi notifications: webhooks: From ad3710ad79acd6784897bd3481a6cc335df90e4c Mon Sep 17 00:00:00 2001 From: codeliner Date: Sat, 7 Mar 2020 23:31:43 +0100 Subject: [PATCH 15/21] Install correct php-coveralls package --- .travis.yml | 2 +- composer.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9dd7095..0adef60 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,7 +27,7 @@ script: - if [[ $EXECUTE_CS_CHECK == 'true' ]]; then ./vendor/bin/docheader check examples/ src/ tests/; fi after_success: - - if [[ $TEST_COVERAGE == 'true' ]]; then php ./vendor/bin/coveralls -v; fi + - if [[ $TEST_COVERAGE == 'true' ]]; then php ./vendor/bin/php-coveralls -v; fi notifications: webhooks: diff --git a/composer.json b/composer.json index ee729b4..97adaf2 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,7 @@ "require-dev": { "phpunit/phpunit": "^7.0", "prooph/php-cs-fixer-config": "^0.3", - "satooshi/php-coveralls": "^2.2", + "php-coveralls/php-coveralls": "^2.2", "malukenho/docheader": "^0.1.4" }, "autoload": { From c48b0ecb5505aa154ae484d10b24a56350b5d0ec Mon Sep 17 00:00:00 2001 From: codeliner Date: Sun, 8 Mar 2020 15:51:40 +0100 Subject: [PATCH 16/21] More tests --- tests/ImmutableRecordDataConverterTest.php | 25 +++++ tests/ImmutableRecordLogicTest.php | 108 +++++++++++++++++++++ tests/Stub/RecordWithStringList.php | 34 +++++++ 3 files changed, 167 insertions(+) create mode 100644 tests/Stub/RecordWithStringList.php diff --git a/tests/ImmutableRecordDataConverterTest.php b/tests/ImmutableRecordDataConverterTest.php index 2756d26..cf0f5bb 100644 --- a/tests/ImmutableRecordDataConverterTest.php +++ b/tests/ImmutableRecordDataConverterTest.php @@ -11,6 +11,7 @@ namespace EventEngineTest\Data; use EventEngine\Data\ImmutableRecordDataConverter; +use EventEngineTest\Data\Stub\ImmutableItem; use EventEngineTest\Data\Stub\TypeHintedImmutableRecordWithValueObjects; use PHPUnit\Framework\TestCase; use stdClass; @@ -53,6 +54,30 @@ public function it_converts_immutable_record_to_array() ); } + /** + * @test + */ + public function it_returns_array_if_passed_as_input() + { + $input = ["a" => "test"]; + + $dataConverter = new ImmutableRecordDataConverter(); + + $output = $dataConverter->convertDataToArray('data', $input); + + $this->assertEquals($input, $output); + } + + /** + * @test + */ + public function it_returns_false_if_unknown_class_is_passed() + { + $dataConverter = new ImmutableRecordDataConverter(); + + $this->assertFalse($dataConverter->canConvertTypeToData(ImmutableItem::class . 'Unknown')); + } + /** * @test */ diff --git a/tests/ImmutableRecordLogicTest.php b/tests/ImmutableRecordLogicTest.php index 210ef1f..aa62013 100644 --- a/tests/ImmutableRecordLogicTest.php +++ b/tests/ImmutableRecordLogicTest.php @@ -13,6 +13,7 @@ use EventEngineTest\Data\Stub\ImmutableItem; use EventEngineTest\Data\Stub\ImmutableRecordWithNoTypes; use EventEngineTest\Data\Stub\ImmutableRecordWithTypedGetters; +use EventEngineTest\Data\Stub\RecordWithStringList; use EventEngineTest\Data\Stub\TypeHintedImmutableRecord; use EventEngineTest\Data\Stub\TypeHintedImmutableRecordWithValueObjects; use EventEngineTest\Data\Stub\ValueObject\Access; @@ -22,6 +23,7 @@ use EventEngineTest\Data\Stub\ValueObject\Type; use EventEngineTest\Data\Stub\ValueObject\Version; use PHPUnit\Framework\TestCase; +use function sprintf; final class ImmutableRecordLogicTest extends TestCase { @@ -110,6 +112,112 @@ public function it_takes_value_object_as_initialization_params() ); } + /** + * @test + */ + public function it_returns_new_record_with_changed_properties() + { + $valueObjects = TypeHintedImmutableRecordWithValueObjects::fromArray($this->data); + + $changedValueObjects = $valueObjects->with([ + 'version' => Version::fromInt(2), + 'percentage' => Percentage::fromFloat(0.9) + ]); + + $this->data['type'] = null; + $this->data['percentage'] = 0.5; + + $this->assertEquals( + $this->data, + $valueObjects->toArray() + ); + + $this->data['percentage'] = 0.9; + $this->data['version'] = 2; + + $this->assertEquals( + $this->data, + $changedValueObjects->toArray() + ); + } + + /** + * @test + */ + public function it_equals_other_record_with_same_values() + { + $valueObjects = TypeHintedImmutableRecordWithValueObjects::fromArray($this->data); + $other = TypeHintedImmutableRecordWithValueObjects::fromArray($this->data); + + $this->assertTrue($valueObjects->equals($other)); + } + + /** + * @test + */ + public function it_throws_exception_if_unkown_property_provided() + { + $this->data['unknown'] = 'value'; + + $this->expectExceptionMessage('Invalid property passed to Record ' . TypeHintedImmutableRecordWithValueObjects::class . '. Got property with key unknown'); + + TypeHintedImmutableRecordWithValueObjects::fromArray($this->data); + } + + /** + * @test + */ + public function it_throws_exception_if_non_nullable_prop_is_missing() + { + unset($this->data['version']); + + $this->expectExceptionMessage('Missing record data for key version of record ' . TypeHintedImmutableRecord::class); + + TypeHintedImmutableRecord::fromArray($this->data); + } + + /** + * @test + */ + public function it_throws_exception_if_non_nullable_prop_should_be_set_to_null() + { + $this->data['version'] = null; + + $this->expectExceptionMessage("Got null for non nullable property version of Record " . TypeHintedImmutableRecord::class); + + TypeHintedImmutableRecord::fromArray($this->data); + } + + /** + * @test + */ + public function it_throws_exception_if_property_value_has_wrong_type() + { + $this->data['version'] = 'v1'; + + $this->expectExceptionMessage(sprintf( + "Record %s data contains invalid value for property version. Expected type is int. Got type string.", + TypeHintedImmutableRecord::class + )); + + TypeHintedImmutableRecord::fromArray($this->data); + } + + /** + * @test + */ + public function it_throws_exception_if_array_property_contains_invalid_value() + { + $stringList = ["abc", 123, "def"]; + + $this->expectExceptionMessage(sprintf( + "Record %s data contains invalid value for property stringList. Value should be an array of string, but at least one item of the array has the wrong type.", + RecordWithStringList::class + )); + + RecordWithStringList::fromArray(['stringList' => $stringList]); + } + /** * @test */ diff --git a/tests/Stub/RecordWithStringList.php b/tests/Stub/RecordWithStringList.php new file mode 100644 index 0000000..333371e --- /dev/null +++ b/tests/Stub/RecordWithStringList.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +declare(strict_types=1); + +namespace EventEngineTest\Data\Stub; + +use EventEngine\Data\ImmutableRecord; +use EventEngine\Data\ImmutableRecordLogic; + +final class RecordWithStringList implements ImmutableRecord +{ + use ImmutableRecordLogic; + + private array $stringList; + + private static function arrayPropItemTypeMap(): array + { + return ['stringList' => ImmutableRecord::PHP_TYPE_STRING]; + } + + /** + * @return array + */ + public function stringList(): array + { + return $this->stringList; + } +} From 97ed424cb797a71b14a5ee7e5cb42fde8733b9e1 Mon Sep 17 00:00:00 2001 From: Sandro Keil Date: Wed, 1 Jul 2020 23:14:17 +0200 Subject: [PATCH 17/21] Add special key support e. g. snake_case --- src/ImmutableRecordLogic.php | 42 ++++++++++---- src/SpecialKeySupport.php | 36 ++++++++++++ tests/ImmutableRecordLogicTest.php | 39 ++++++++++--- tests/Stub/RecordWithSpecialKey.php | 90 +++++++++++++++++++++++++++++ 4 files changed, 187 insertions(+), 20 deletions(-) create mode 100644 src/SpecialKeySupport.php create mode 100644 tests/Stub/RecordWithSpecialKey.php diff --git a/src/ImmutableRecordLogic.php b/src/ImmutableRecordLogic.php index 717647b..854573f 100644 --- a/src/ImmutableRecordLogic.php +++ b/src/ImmutableRecordLogic.php @@ -93,6 +93,12 @@ public function toArray(): array $arrayPropItemTypeMap = self::getArrayPropItemTypeMapFromMethodOrCache(); foreach (self::$__propTypeMap as $key => [$type, $isNative, $isNullable]) { + $specialKey = $key; + + if ($this instanceof SpecialKeySupport) { + $specialKey = $this->convertKeyForArray($key); + } + switch ($type) { case ImmutableRecord::PHP_TYPE_STRING: case ImmutableRecord::PHP_TYPE_INT: @@ -101,23 +107,23 @@ public function toArray(): array case ImmutableRecord::PHP_TYPE_ARRAY: if (\array_key_exists($key, $arrayPropItemTypeMap) && ! self::isScalarType($arrayPropItemTypeMap[$key])) { if ($isNullable && $this->{$key} === null) { - $nativeData[$key] = null; + $nativeData[$specialKey] = null; continue 2; } - $nativeData[$key] = \array_map(function ($item) use ($key, &$arrayPropItemTypeMap) { + $nativeData[$specialKey] = \array_map(function ($item) use ($key, &$arrayPropItemTypeMap) { return $this->voTypeToNative($item, $key, $arrayPropItemTypeMap[$key]); }, $this->{$key}); } else { - $nativeData[$key] = $this->{$key}; + $nativeData[$specialKey] = $this->{$key}; } break; default: if ($isNullable && (! isset($this->{$key}))) { - $nativeData[$key] = null; + $nativeData[$specialKey] = null; continue 2; } - $nativeData[$key] = $this->voTypeToNative($this->{$key}, $key, $type); + $nativeData[$specialKey] = $this->voTypeToNative($this->{$key}, $key, $type); } } @@ -126,7 +132,7 @@ public function toArray(): array public function equals(ImmutableRecord $other): bool { - if (get_class($this) !== get_class($other)) { + if (\get_class($this) !== \get_class($other)) { return false; } @@ -136,8 +142,14 @@ public function equals(ImmutableRecord $other): bool private function setRecordData(array $recordData): void { foreach ($recordData as $key => $value) { - $this->assertType($key, $value); - $this->{$key} = $value; + $specialKey = $key; + + if ($this instanceof SpecialKeySupport) { + $specialKey = $this->convertKeyForRecord($key); + } + + $this->assertType($specialKey, $value); + $this->{$specialKey} = $value; } } @@ -147,17 +159,23 @@ private function setNativeData(array $nativeData): void $arrayPropItemTypeMap = self::getArrayPropItemTypeMapFromMethodOrCache(); foreach ($nativeData as $key => $val) { - if (! isset(self::$__propTypeMap[$key])) { + $specialKey = $key; + + if ($this instanceof SpecialKeySupport) { + $specialKey = $this->convertKeyForRecord($key); + } + + if (! isset(self::$__propTypeMap[$specialKey])) { throw new \InvalidArgumentException(\sprintf( - 'Invalid property passed to Record %s. Got property with key ' . $key, + 'Invalid property passed to Record %s. Got property with key ' . $specialKey, \get_called_class() )); } - [$type, $isNative, $isNullable] = self::$__propTypeMap[$key]; + [$type, $isNative, $isNullable] = self::$__propTypeMap[$specialKey]; if ($val === null) { if (! $isNullable) { - throw new \RuntimeException("Got null for non nullable property $key of Record " . \get_called_class()); + throw new \RuntimeException("Got null for non nullable property $specialKey of Record " . \get_called_class()); } $recordData[$key] = null; diff --git a/src/SpecialKeySupport.php b/src/SpecialKeySupport.php new file mode 100644 index 0000000..b527515 --- /dev/null +++ b/src/SpecialKeySupport.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace EventEngine\Data; + +/** + * Implement this interface in your ImmutableRecord class if you have special keys like snake_case. + */ +interface SpecialKeySupport +{ + /** + * Converts the given key to key name for the record. For example if the key is first_name and you have a class + * property firstName then you have to convert it to camel case. + * + * @param string $key + * @return string + */ + public function convertKeyForRecord(string $key): string; + + /** + * Converts the given key to key name for the array data. For example if you have a class property firstName + * and want to have snake case array keys then you have to convert it to first_name. + * + * @param string $key + * @return string + */ + public function convertKeyForArray(string $key): string; +} diff --git a/tests/ImmutableRecordLogicTest.php b/tests/ImmutableRecordLogicTest.php index aa62013..a69b3aa 100644 --- a/tests/ImmutableRecordLogicTest.php +++ b/tests/ImmutableRecordLogicTest.php @@ -6,6 +6,7 @@ * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ + declare(strict_types=1); namespace EventEngineTest\Data; @@ -13,6 +14,7 @@ use EventEngineTest\Data\Stub\ImmutableItem; use EventEngineTest\Data\Stub\ImmutableRecordWithNoTypes; use EventEngineTest\Data\Stub\ImmutableRecordWithTypedGetters; +use EventEngineTest\Data\Stub\RecordWithSpecialKey; use EventEngineTest\Data\Stub\RecordWithStringList; use EventEngineTest\Data\Stub\TypeHintedImmutableRecord; use EventEngineTest\Data\Stub\TypeHintedImmutableRecordWithValueObjects; @@ -23,7 +25,6 @@ use EventEngineTest\Data\Stub\ValueObject\Type; use EventEngineTest\Data\Stub\ValueObject\Version; use PHPUnit\Framework\TestCase; -use function sprintf; final class ImmutableRecordLogicTest extends TestCase { @@ -121,7 +122,7 @@ public function it_returns_new_record_with_changed_properties() $changedValueObjects = $valueObjects->with([ 'version' => Version::fromInt(2), - 'percentage' => Percentage::fromFloat(0.9) + 'percentage' => Percentage::fromFloat(0.9), ]); $this->data['type'] = null; @@ -152,6 +153,28 @@ public function it_equals_other_record_with_same_values() $this->assertTrue($valueObjects->equals($other)); } + /** + * @test + */ + public function it_supports_special_keys(): void + { + // emulates snake_case + $recordArray = [ + RecordWithSpecialKey::BANK_ACCOUNT => '12324434', + RecordWithSpecialKey::SUCCESS_RATE => 33.33, + RecordWithSpecialKey::ITEM_LIST => [['name' => 'Awesome tester'], ['name' => 'John Smith']], + ]; + $specialKey = RecordWithSpecialKey::fromArray($recordArray); + $this->assertSame($recordArray, $specialKey->toArray()); + + $specialKey = RecordWithSpecialKey::fromRecordData([ + RecordWithSpecialKey::BANK_ACCOUNT => $recordArray[RecordWithSpecialKey::BANK_ACCOUNT], + RecordWithSpecialKey::SUCCESS_RATE => Percentage::fromFloat($recordArray[RecordWithSpecialKey::SUCCESS_RATE]), + RecordWithSpecialKey::ITEM_LIST => ItemList::fromArray($recordArray[RecordWithSpecialKey::ITEM_LIST]), + ]); + $this->assertSame($recordArray, $specialKey->toArray()); + } + /** * @test */ @@ -183,7 +206,7 @@ public function it_throws_exception_if_non_nullable_prop_should_be_set_to_null() { $this->data['version'] = null; - $this->expectExceptionMessage("Got null for non nullable property version of Record " . TypeHintedImmutableRecord::class); + $this->expectExceptionMessage('Got null for non nullable property version of Record ' . TypeHintedImmutableRecord::class); TypeHintedImmutableRecord::fromArray($this->data); } @@ -195,8 +218,8 @@ public function it_throws_exception_if_property_value_has_wrong_type() { $this->data['version'] = 'v1'; - $this->expectExceptionMessage(sprintf( - "Record %s data contains invalid value for property version. Expected type is int. Got type string.", + $this->expectExceptionMessage(\sprintf( + 'Record %s data contains invalid value for property version. Expected type is int. Got type string.', TypeHintedImmutableRecord::class )); @@ -208,10 +231,10 @@ public function it_throws_exception_if_property_value_has_wrong_type() */ public function it_throws_exception_if_array_property_contains_invalid_value() { - $stringList = ["abc", 123, "def"]; + $stringList = ['abc', 123, 'def']; - $this->expectExceptionMessage(sprintf( - "Record %s data contains invalid value for property stringList. Value should be an array of string, but at least one item of the array has the wrong type.", + $this->expectExceptionMessage(\sprintf( + 'Record %s data contains invalid value for property stringList. Value should be an array of string, but at least one item of the array has the wrong type.', RecordWithStringList::class )); diff --git a/tests/Stub/RecordWithSpecialKey.php b/tests/Stub/RecordWithSpecialKey.php new file mode 100644 index 0000000..3bd81b3 --- /dev/null +++ b/tests/Stub/RecordWithSpecialKey.php @@ -0,0 +1,90 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace EventEngineTest\Data\Stub; + +use EventEngine\Data\ImmutableRecord; +use EventEngine\Data\ImmutableRecordLogic; +use EventEngine\Data\SpecialKeySupport; +use EventEngineTest\Data\Stub\ValueObject\ItemList; +use EventEngineTest\Data\Stub\ValueObject\Percentage; + +final class RecordWithSpecialKey implements ImmutableRecord, SpecialKeySupport +{ + use ImmutableRecordLogic; + + public const BANK_ACCOUNT = 'bank_account'; + public const SUCCESS_RATE = 'success_rate'; + public const ITEM_LIST = 'item_list'; + + /** + * @var string + */ + private $bankAccount; + + /** + * @var Percentage + */ + private $successRate; + + /** + * @var ItemList + */ + private $itemList; + + /** + * @return mixed + */ + public function bankAccount(): string + { + return $this->bankAccount; + } + + /** + * @return Percentage + */ + public function successRate(): Percentage + { + return $this->successRate; + } + + /** + * @return ItemList + */ + public function itemList(): ItemList + { + return $this->itemList; + } + + public function convertKeyForRecord(string $key): string + { + switch ($key) { + case self::SUCCESS_RATE: + return 'successRate'; + case self::ITEM_LIST: + return 'itemList'; + default: + return 'bankAccount'; + } + } + + public function convertKeyForArray(string $key): string + { + switch ($key) { + case 'successRate': + return self::SUCCESS_RATE; + case 'itemList': + return self::ITEM_LIST; + default: + return self::BANK_ACCOUNT; + } + } +} From 1c41211bfd501557ab63ae0d94393f4f06aa8c37 Mon Sep 17 00:00:00 2001 From: Zacharias Luiten Date: Sat, 30 Jan 2021 14:04:48 +0100 Subject: [PATCH 18/21] - Allow PHP 8 - Updated (dev) dependencies - Updated doc header with copyright year --- .travis.yml | 5 +++++ composer.json | 6 +++--- src/DataConverter.php | 2 +- src/ImmutableRecord.php | 2 +- src/ImmutableRecordDataConverter.php | 2 +- src/ImmutableRecordLogic.php | 2 +- src/SpecialKeySupport.php | 2 +- tests/ImmutableRecordDataConverterTest.php | 4 ++-- tests/ImmutableRecordLogicTest.php | 4 ++-- tests/Stub/ImmutableItem.php | 2 +- tests/Stub/ImmutableRecordWithNoTypes.php | 2 +- tests/Stub/ImmutableRecordWithTypedGetters.php | 2 +- tests/Stub/RecordWithSpecialKey.php | 2 +- tests/Stub/RecordWithStringList.php | 2 +- tests/Stub/TypeHintedImmutableRecord.php | 2 +- tests/Stub/TypeHintedImmutableRecordWithValueObjects.php | 2 +- tests/Stub/ValueObject/Access.php | 2 +- tests/Stub/ValueObject/ItemList.php | 2 +- tests/Stub/ValueObject/Name.php | 2 +- tests/Stub/ValueObject/Percentage.php | 2 +- tests/Stub/ValueObject/Type.php | 2 +- tests/Stub/ValueObject/Version.php | 2 +- 22 files changed, 30 insertions(+), 25 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0adef60..209b70c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,6 +8,11 @@ matrix: - DEPENDENCIES="" - EXECUTE_CS_CHECK=true - TEST_COVERAGE=true + - php: 8.0 + env: + - DEPENDENCIES="" + - EXECUTE_CS_CHECK=true + - TEST_COVERAGE=true cache: directories: diff --git a/composer.json b/composer.json index 97adaf2..6376d90 100644 --- a/composer.json +++ b/composer.json @@ -16,12 +16,12 @@ } ], "require": { - "php": "^7.4", + "php": "^7.4 || ^8.0", "roave/security-advisories": "dev-master" }, "require-dev": { - "phpunit/phpunit": "^7.0", - "prooph/php-cs-fixer-config": "^0.3", + "phpunit/phpunit": "^8.0 || ^9.0", + "prooph/php-cs-fixer-config": "^0.4", "php-coveralls/php-coveralls": "^2.2", "malukenho/docheader": "^0.1.4" }, diff --git a/src/DataConverter.php b/src/DataConverter.php index 6b5c229..7ef87ba 100644 --- a/src/DataConverter.php +++ b/src/DataConverter.php @@ -1,7 +1,7 @@ + * (c) 2018-2021 prooph software GmbH * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. diff --git a/src/ImmutableRecord.php b/src/ImmutableRecord.php index 0abe53e..a705147 100644 --- a/src/ImmutableRecord.php +++ b/src/ImmutableRecord.php @@ -1,7 +1,7 @@ + * (c) 2018-2021 prooph software GmbH * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. diff --git a/src/ImmutableRecordDataConverter.php b/src/ImmutableRecordDataConverter.php index 73526aa..5029cde 100644 --- a/src/ImmutableRecordDataConverter.php +++ b/src/ImmutableRecordDataConverter.php @@ -1,7 +1,7 @@ + * (c) 2018-2021 prooph software GmbH * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. diff --git a/src/ImmutableRecordLogic.php b/src/ImmutableRecordLogic.php index 854573f..19c6478 100644 --- a/src/ImmutableRecordLogic.php +++ b/src/ImmutableRecordLogic.php @@ -1,7 +1,7 @@ + * (c) 2018-2021 prooph software GmbH * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. diff --git a/src/SpecialKeySupport.php b/src/SpecialKeySupport.php index b527515..158c0d0 100644 --- a/src/SpecialKeySupport.php +++ b/src/SpecialKeySupport.php @@ -1,7 +1,7 @@ + * (c) 2018-2021 prooph software GmbH * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. diff --git a/tests/ImmutableRecordDataConverterTest.php b/tests/ImmutableRecordDataConverterTest.php index cf0f5bb..77c6e5c 100644 --- a/tests/ImmutableRecordDataConverterTest.php +++ b/tests/ImmutableRecordDataConverterTest.php @@ -1,7 +1,7 @@ + * (c) 2018-2021 prooph software GmbH * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -20,7 +20,7 @@ final class ImmutableRecordDataConverterTest extends TestCase { private $data = []; - protected function setUp() + protected function setUp(): void { parent::setUp(); diff --git a/tests/ImmutableRecordLogicTest.php b/tests/ImmutableRecordLogicTest.php index a69b3aa..c3343ff 100644 --- a/tests/ImmutableRecordLogicTest.php +++ b/tests/ImmutableRecordLogicTest.php @@ -1,7 +1,7 @@ + * (c) 2018-2021 prooph software GmbH * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -30,7 +30,7 @@ final class ImmutableRecordLogicTest extends TestCase { private $data = []; - protected function setUp() + protected function setUp(): void { parent::setUp(); diff --git a/tests/Stub/ImmutableItem.php b/tests/Stub/ImmutableItem.php index 32cdbfb..92df681 100644 --- a/tests/Stub/ImmutableItem.php +++ b/tests/Stub/ImmutableItem.php @@ -1,7 +1,7 @@ + * (c) 2018-2021 prooph software GmbH * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. diff --git a/tests/Stub/ImmutableRecordWithNoTypes.php b/tests/Stub/ImmutableRecordWithNoTypes.php index 3a30282..8ac85ef 100644 --- a/tests/Stub/ImmutableRecordWithNoTypes.php +++ b/tests/Stub/ImmutableRecordWithNoTypes.php @@ -1,7 +1,7 @@ + * (c) 2018-2021 prooph software GmbH * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. diff --git a/tests/Stub/ImmutableRecordWithTypedGetters.php b/tests/Stub/ImmutableRecordWithTypedGetters.php index d7260f4..85ea424 100644 --- a/tests/Stub/ImmutableRecordWithTypedGetters.php +++ b/tests/Stub/ImmutableRecordWithTypedGetters.php @@ -1,7 +1,7 @@ + * (c) 2018-2021 prooph software GmbH * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. diff --git a/tests/Stub/RecordWithSpecialKey.php b/tests/Stub/RecordWithSpecialKey.php index 3bd81b3..8ab80cb 100644 --- a/tests/Stub/RecordWithSpecialKey.php +++ b/tests/Stub/RecordWithSpecialKey.php @@ -1,7 +1,7 @@ + * (c) 2018-2021 prooph software GmbH * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. diff --git a/tests/Stub/RecordWithStringList.php b/tests/Stub/RecordWithStringList.php index 333371e..059d359 100644 --- a/tests/Stub/RecordWithStringList.php +++ b/tests/Stub/RecordWithStringList.php @@ -1,7 +1,7 @@ + * (c) 2018-2021 prooph software GmbH * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. diff --git a/tests/Stub/TypeHintedImmutableRecord.php b/tests/Stub/TypeHintedImmutableRecord.php index 5496e80..5ae44d5 100644 --- a/tests/Stub/TypeHintedImmutableRecord.php +++ b/tests/Stub/TypeHintedImmutableRecord.php @@ -1,7 +1,7 @@ + * (c) 2018-2021 prooph software GmbH * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. diff --git a/tests/Stub/TypeHintedImmutableRecordWithValueObjects.php b/tests/Stub/TypeHintedImmutableRecordWithValueObjects.php index 7385553..07909ce 100644 --- a/tests/Stub/TypeHintedImmutableRecordWithValueObjects.php +++ b/tests/Stub/TypeHintedImmutableRecordWithValueObjects.php @@ -1,7 +1,7 @@ + * (c) 2018-2021 prooph software GmbH * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. diff --git a/tests/Stub/ValueObject/Access.php b/tests/Stub/ValueObject/Access.php index 7e09d31..cf7382b 100644 --- a/tests/Stub/ValueObject/Access.php +++ b/tests/Stub/ValueObject/Access.php @@ -1,7 +1,7 @@ + * (c) 2018-2021 prooph software GmbH * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. diff --git a/tests/Stub/ValueObject/ItemList.php b/tests/Stub/ValueObject/ItemList.php index 70f3d97..0bfc0a1 100644 --- a/tests/Stub/ValueObject/ItemList.php +++ b/tests/Stub/ValueObject/ItemList.php @@ -1,7 +1,7 @@ + * (c) 2018-2021 prooph software GmbH * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. diff --git a/tests/Stub/ValueObject/Name.php b/tests/Stub/ValueObject/Name.php index fffa4b7..342b3c0 100644 --- a/tests/Stub/ValueObject/Name.php +++ b/tests/Stub/ValueObject/Name.php @@ -1,7 +1,7 @@ + * (c) 2018-2021 prooph software GmbH * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. diff --git a/tests/Stub/ValueObject/Percentage.php b/tests/Stub/ValueObject/Percentage.php index b5283dd..145b3ac 100644 --- a/tests/Stub/ValueObject/Percentage.php +++ b/tests/Stub/ValueObject/Percentage.php @@ -1,7 +1,7 @@ + * (c) 2018-2021 prooph software GmbH * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. diff --git a/tests/Stub/ValueObject/Type.php b/tests/Stub/ValueObject/Type.php index bfe3e32..ff8c2ae 100644 --- a/tests/Stub/ValueObject/Type.php +++ b/tests/Stub/ValueObject/Type.php @@ -1,7 +1,7 @@ + * (c) 2018-2021 prooph software GmbH * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. diff --git a/tests/Stub/ValueObject/Version.php b/tests/Stub/ValueObject/Version.php index 70c2fc1..971f970 100644 --- a/tests/Stub/ValueObject/Version.php +++ b/tests/Stub/ValueObject/Version.php @@ -1,7 +1,7 @@ + * (c) 2018-2021 prooph software GmbH * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. From 522667ded5793ce4c7c64aabcbba0875c50dd0e3 Mon Sep 17 00:00:00 2001 From: Ahmed Ibrahim Date: Wed, 17 Nov 2021 18:52:49 +0100 Subject: [PATCH 19/21] move roave/security-advisories to require-dev --- composer.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index 6376d90..be3fa40 100644 --- a/composer.json +++ b/composer.json @@ -16,14 +16,14 @@ } ], "require": { - "php": "^7.4 || ^8.0", - "roave/security-advisories": "dev-master" + "php": "^7.4 || ^8.0" }, "require-dev": { "phpunit/phpunit": "^8.0 || ^9.0", "prooph/php-cs-fixer-config": "^0.4", "php-coveralls/php-coveralls": "^2.2", - "malukenho/docheader": "^0.1.4" + "malukenho/docheader": "^0.1.4", + "roave/security-advisories": "dev-latest" }, "autoload": { "psr-4": { From 2ec9e9199c3a4ab71dd2c7758d3cbd2920a0cea3 Mon Sep 17 00:00:00 2001 From: Arne De Smedt Date: Wed, 27 Apr 2022 10:57:09 +0200 Subject: [PATCH 20/21] arrayPropItemTypeMap should be indexed with a specialKey --- src/ImmutableRecordLogic.php | 6 +++--- tests/ImmutableRecordLogicTest.php | 8 ++++++++ tests/Stub/RecordWithSpecialKey.php | 25 +++++++++++++++++++++++++ 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/src/ImmutableRecordLogic.php b/src/ImmutableRecordLogic.php index 19c6478..32d5581 100644 --- a/src/ImmutableRecordLogic.php +++ b/src/ImmutableRecordLogic.php @@ -190,9 +190,9 @@ private function setNativeData(array $nativeData): void $recordData[$key] = $val; break; case ImmutableRecord::PHP_TYPE_ARRAY: - if (\array_key_exists($key, $arrayPropItemTypeMap) && ! self::isScalarType($arrayPropItemTypeMap[$key])) { - $recordData[$key] = \array_map(function ($item) use ($key, &$arrayPropItemTypeMap) { - return $this->fromType($item, $arrayPropItemTypeMap[$key]); + if (\array_key_exists($specialKey, $arrayPropItemTypeMap) && ! self::isScalarType($arrayPropItemTypeMap[$specialKey])) { + $recordData[$key] = \array_map(function ($item) use ($specialKey, &$arrayPropItemTypeMap) { + return $this->fromType($item, $arrayPropItemTypeMap[$specialKey]); }, $val); } else { $recordData[$key] = $val; diff --git a/tests/ImmutableRecordLogicTest.php b/tests/ImmutableRecordLogicTest.php index c3343ff..e8f9ab5 100644 --- a/tests/ImmutableRecordLogicTest.php +++ b/tests/ImmutableRecordLogicTest.php @@ -163,6 +163,8 @@ public function it_supports_special_keys(): void RecordWithSpecialKey::BANK_ACCOUNT => '12324434', RecordWithSpecialKey::SUCCESS_RATE => 33.33, RecordWithSpecialKey::ITEM_LIST => [['name' => 'Awesome tester'], ['name' => 'John Smith']], + RecordWithSpecialKey::ITEM_ARRAY => [['name' => 'Awesome tester array'], ['name' => 'John Smith array']], + ]; $specialKey = RecordWithSpecialKey::fromArray($recordArray); $this->assertSame($recordArray, $specialKey->toArray()); @@ -171,6 +173,12 @@ public function it_supports_special_keys(): void RecordWithSpecialKey::BANK_ACCOUNT => $recordArray[RecordWithSpecialKey::BANK_ACCOUNT], RecordWithSpecialKey::SUCCESS_RATE => Percentage::fromFloat($recordArray[RecordWithSpecialKey::SUCCESS_RATE]), RecordWithSpecialKey::ITEM_LIST => ItemList::fromArray($recordArray[RecordWithSpecialKey::ITEM_LIST]), + RecordWithSpecialKey::ITEM_ARRAY => array_map( + static function (array $item) { + return ImmutableItem::fromArray($item); + }, + $recordArray[RecordWithSpecialKey::ITEM_ARRAY] + ), ]); $this->assertSame($recordArray, $specialKey->toArray()); } diff --git a/tests/Stub/RecordWithSpecialKey.php b/tests/Stub/RecordWithSpecialKey.php index 8ab80cb..a7b7249 100644 --- a/tests/Stub/RecordWithSpecialKey.php +++ b/tests/Stub/RecordWithSpecialKey.php @@ -24,6 +24,7 @@ final class RecordWithSpecialKey implements ImmutableRecord, SpecialKeySupport public const BANK_ACCOUNT = 'bank_account'; public const SUCCESS_RATE = 'success_rate'; public const ITEM_LIST = 'item_list'; + public const ITEM_ARRAY = 'item_array'; /** * @var string @@ -40,6 +41,11 @@ final class RecordWithSpecialKey implements ImmutableRecord, SpecialKeySupport */ private $itemList; + /** + * @var array + */ + private $itemArray; + /** * @return mixed */ @@ -64,6 +70,14 @@ public function itemList(): ItemList return $this->itemList; } + /** + * @return array + */ + public function itemArray(): array + { + return $this->itemArray; + } + public function convertKeyForRecord(string $key): string { switch ($key) { @@ -71,6 +85,8 @@ public function convertKeyForRecord(string $key): string return 'successRate'; case self::ITEM_LIST: return 'itemList'; + case self::ITEM_ARRAY: + return 'itemArray'; default: return 'bankAccount'; } @@ -83,8 +99,17 @@ public function convertKeyForArray(string $key): string return self::SUCCESS_RATE; case 'itemList': return self::ITEM_LIST; + case 'itemArray': + return self::ITEM_ARRAY; default: return self::BANK_ACCOUNT; } } + + private static function arrayPropItemTypeMap(): array + { + return [ + 'itemArray' => ImmutableItem::class, + ]; + } } From 5285ec033241de39faaed8975ed931e6e0e72626 Mon Sep 17 00:00:00 2001 From: Arne De Smedt Date: Thu, 16 Jan 2025 13:09:03 +0100 Subject: [PATCH 21/21] Explicit nullable types --- src/ImmutableRecordLogic.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImmutableRecordLogic.php b/src/ImmutableRecordLogic.php index 32d5581..5516f05 100644 --- a/src/ImmutableRecordLogic.php +++ b/src/ImmutableRecordLogic.php @@ -56,7 +56,7 @@ public static function fromArray(array $nativeData): self return new self(null, $nativeData); } - private function __construct(array $recordData = null, array $nativeData = null) + private function __construct(array|null $recordData = null, array|null $nativeData = null) { if (null === self::$__propTypeMap) { self::$__propTypeMap = self::buildPropTypeMap();