diff --git a/CHANGELOG.md b/CHANGELOG.md index 562d94f..cb2d33d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,8 @@ type. * feat(`TraversableToArrayAccessTransformer`): Now supports `ArrayCollection` & `ArrayIterator`. * test: Assorted tests. +* fix: Remove iterating object key because not supported in PHP. +* test: Assorted tests. ## 0.5.10 diff --git a/src/Transformer/TraversableToArrayAccessTransformer.php b/src/Transformer/TraversableToArrayAccessTransformer.php index 3595f8f..af92683 100644 --- a/src/Transformer/TraversableToArrayAccessTransformer.php +++ b/src/Transformer/TraversableToArrayAccessTransformer.php @@ -24,7 +24,6 @@ use Rekalogika\Mapper\Transformer\Contracts\TypeMapping; use Rekalogika\Mapper\Transformer\Exception\ClassNotInstantiableException; use Rekalogika\Mapper\Transformer\Exception\InvalidTypeInArgumentException; -use Rekalogika\Mapper\Transformer\Exception\MissingMemberKeyTypeException; use Rekalogika\Mapper\Util\TypeCheck; use Rekalogika\Mapper\Util\TypeFactory; use Symfony\Component\PropertyInfo\Type; @@ -68,7 +67,6 @@ public function transform( // Prepare variables for the output loop $targetMemberKeyType = $targetType->getCollectionKeyTypes(); - $targetMemberKeyTypeIsMissing = count($targetMemberKeyType) === 0; $targetMemberKeyTypeIsInt = count($targetMemberKeyType) === 1 && TypeCheck::isInt($targetMemberKeyType[0]); $targetMemberValueType = $targetType->getCollectionValueTypes(); @@ -77,47 +75,15 @@ public function transform( /** @var mixed $sourceMemberValue */ foreach ($source as $sourceMemberKey => $sourceMemberValue) { - /** @var mixed $sourceMemberKey */ + // if target has int key type but the source has string key type, + // we discard the source key & use null (i.e. $target[] = $value) - if (is_string($sourceMemberKey) || is_int($sourceMemberKey)) { - // if the key is a simple type: int|string - - if ($targetMemberKeyTypeIsInt && is_string($sourceMemberKey)) { - // if target has int key type but the source has string key type, - // we discard the source key & use null (i.e. $target[] = $value) - - $targetMemberKey = null; - $path = sprintf('[%d]', $i); - } else { - $targetMemberKey = $sourceMemberKey; - $path = sprintf('[%s]', $sourceMemberKey); - } + if ($targetMemberKeyTypeIsInt && is_string($sourceMemberKey)) { + $targetMemberKey = null; + $path = sprintf('[%d]', $i); } else { - // If the type of the key is a complex type (not int or string). - // i.e. an ArrayObject can have an object as its key. - - // Refuse to continue if the target key type is not provided - - if ($targetMemberKeyTypeIsMissing) { - throw new MissingMemberKeyTypeException($sourceType, $targetType, context: $context); - } - - // If provided, we transform the source key to the key type of - // the target - - /** @var mixed */ - $targetMemberKey = $this->getMainTransformer()->transform( - source: $sourceMemberKey, - target: null, - targetTypes: $targetMemberKeyType, - context: $context, - ); - - if ($targetMemberKey instanceof \Stringable) { - $path = sprintf('[%s]', $targetMemberKey); - } else { - $path = sprintf('[%s]', get_debug_type($targetMemberKey)); - } + $targetMemberKey = $sourceMemberKey; + $path = sprintf('[%s]', $sourceMemberKey); } // Get the existing member value from the target diff --git a/src/Transformer/TraversableToTraversableTransformer.php b/src/Transformer/TraversableToTraversableTransformer.php index d9bbb12..a83ff96 100644 --- a/src/Transformer/TraversableToTraversableTransformer.php +++ b/src/Transformer/TraversableToTraversableTransformer.php @@ -20,8 +20,6 @@ use Rekalogika\Mapper\Transformer\Contracts\MainTransformerAwareTrait; use Rekalogika\Mapper\Transformer\Contracts\TransformerInterface; use Rekalogika\Mapper\Transformer\Contracts\TypeMapping; -use Rekalogika\Mapper\Transformer\Exception\MissingMemberKeyTypeException; -use Rekalogika\Mapper\Transformer\Exception\MissingMemberValueTypeException; use Rekalogika\Mapper\Transformer\Model\TraversableCountableWrapper; use Rekalogika\Mapper\Util\TypeCheck; use Rekalogika\Mapper\Util\TypeFactory; @@ -57,28 +55,16 @@ public function transform( // We can't work if the target type doesn't contain the information // about the type of its member objects - $targetMemberValueType = $targetType->getCollectionValueTypes(); - - if (count($targetMemberValueType) === 0) { - throw new MissingMemberValueTypeException($sourceType, $targetType, context: $context); - } - - // Prepare variables for the output loop - $targetMemberKeyType = $targetType->getCollectionKeyTypes(); - $targetMemberKeyTypeIsMissing = count($targetMemberKeyType) === 0; $targetMemberKeyTypeIsInt = count($targetMemberKeyType) === 1 && TypeCheck::isInt($targetMemberKeyType[0]); + $targetMemberValueType = $targetType->getCollectionValueTypes(); // create generator $target = (function () use ( $source, $targetMemberKeyTypeIsInt, - $targetMemberKeyTypeIsMissing, - $sourceType, - $targetType, - $targetMemberKeyType, $targetMemberValueType, $context ): \Traversable { @@ -86,47 +72,15 @@ public function transform( /** @var mixed $sourcePropertyValue */ foreach ($source as $sourcePropertyKey => $sourcePropertyValue) { - /** @var mixed $sourcePropertyKey */ - - if (is_string($sourcePropertyKey) || is_int($sourcePropertyKey)) { - // if the key is a simple type: int|string - - if ($targetMemberKeyTypeIsInt && is_string($sourcePropertyKey)) { - // if target has int key type but the source has string key type, - // we discard the source key & use null (i.e. $target[] = $value) + // if target has int key type but the source has string key type, + // we discard the source key & use null (i.e. $target[] = $value) - $targetMemberKey = null; - $path = sprintf('[%d]', $i); - } else { - $targetMemberKey = $sourcePropertyKey; - $path = sprintf('[%s]', $sourcePropertyKey); - } + if ($targetMemberKeyTypeIsInt && is_string($sourcePropertyKey)) { + $targetMemberKey = null; + $path = sprintf('[%d]', $i); } else { - // If the type of the key is a complex type (not int or string). - // i.e. an ArrayObject can have an object as its key. - - // Refuse to continue if the target key type is not provided - - if ($targetMemberKeyTypeIsMissing) { - throw new MissingMemberKeyTypeException($sourceType, $targetType, context: $context); - } - - // If provided, we transform the source key to the key type of - // the target - - /** @var mixed */ - $targetMemberKey = $this->getMainTransformer()->transform( - source: $sourcePropertyKey, - target: null, - targetTypes: $targetMemberKeyType, - context: $context, - ); - - if ($targetMemberKey instanceof \Stringable) { - $path = sprintf('[%s]', $targetMemberKey); - } else { - $path = sprintf('[%s]', get_debug_type($targetMemberKey)); - } + $targetMemberKey = $sourcePropertyKey; + $path = sprintf('[%s]', $sourcePropertyKey); } // now transform the source member value to the type of the target diff --git a/tests/Fixtures/ArrayLikeDto/ObjectWithArrayAccessPropertyWithoutTypeHintDto.php b/tests/Fixtures/ArrayLikeDto/ObjectWithArrayAccessPropertyWithoutTypeHintDto.php index 1d2a05a..7204131 100644 --- a/tests/Fixtures/ArrayLikeDto/ObjectWithArrayAccessPropertyWithoutTypeHintDto.php +++ b/tests/Fixtures/ArrayLikeDto/ObjectWithArrayAccessPropertyWithoutTypeHintDto.php @@ -15,9 +15,7 @@ class ObjectWithArrayAccessPropertyWithoutTypeHintDto { - /** - * @var \ArrayAccess - */ + // @phpstan-ignore-next-line public \ArrayAccess $property; public function __construct() diff --git a/tests/Fixtures/ArrayLikeDto/ObjectWithArrayPropertyWithoutTypeHintDto.php b/tests/Fixtures/ArrayLikeDto/ObjectWithArrayPropertyWithoutTypeHintDto.php index 8ee5a9d..9008493 100644 --- a/tests/Fixtures/ArrayLikeDto/ObjectWithArrayPropertyWithoutTypeHintDto.php +++ b/tests/Fixtures/ArrayLikeDto/ObjectWithArrayPropertyWithoutTypeHintDto.php @@ -15,8 +15,6 @@ class ObjectWithArrayPropertyWithoutTypeHintDto { - /** - * @var array - */ + // @phpstan-ignore-next-line public array $property = []; } diff --git a/tests/Fixtures/ArrayLikeDto/ObjectWithTraversablePropertyWithoutTypeHintDto.php b/tests/Fixtures/ArrayLikeDto/ObjectWithTraversablePropertyWithoutTypeHintDto.php new file mode 100644 index 0000000..d368516 --- /dev/null +++ b/tests/Fixtures/ArrayLikeDto/ObjectWithTraversablePropertyWithoutTypeHintDto.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE file + * that was distributed with this source code. + */ + +namespace Rekalogika\Mapper\Tests\Fixtures\ArrayLikeDto; + +class ObjectWithTraversablePropertyWithoutTypeHintDto +{ + // @phpstan-ignore-next-line + public ?\Traversable $property = null; +} diff --git a/tests/IntegrationTest/TraversableToArrayAccessMappingTest.php b/tests/IntegrationTest/TraversableToArrayAccessMappingTest.php index d6e0c6f..67a23ba 100644 --- a/tests/IntegrationTest/TraversableToArrayAccessMappingTest.php +++ b/tests/IntegrationTest/TraversableToArrayAccessMappingTest.php @@ -29,7 +29,11 @@ class TraversableToArrayAccessMappingTest extends AbstractIntegrationTest { - public function testFailedArrayToArray(): void + // + // without typehint + // + + public function testArrayToArrayWithoutTypehint(): void { $source = new ObjectWithArrayProperty(); $result = $this->mapper->map($source, ObjectWithArrayPropertyWithoutTypeHintDto::class); @@ -44,7 +48,7 @@ public function testFailedArrayToArray(): void $this->assertInstanceOf(ObjectWithScalarProperties::class, $result->property[2]); } - public function testFailedTraversableToArrayAccess(): void + public function testTraversableToArrayAccessWithoutTypehint(): void { $source = new ObjectWithTraversableProperties(); $result = $this->mapper->map($source, ObjectWithArrayPropertyWithoutTypeHintDto::class); diff --git a/tests/IntegrationTest/TraversableToTraversableMappingTest.php b/tests/IntegrationTest/TraversableToTraversableMappingTest.php index 1d522f1..67bbd53 100644 --- a/tests/IntegrationTest/TraversableToTraversableMappingTest.php +++ b/tests/IntegrationTest/TraversableToTraversableMappingTest.php @@ -19,6 +19,8 @@ use Rekalogika\Mapper\Tests\Fixtures\ArrayLike\ObjectWithLazyDoctrineCollectionWithPresetCountableProperty; use Rekalogika\Mapper\Tests\Fixtures\ArrayLike\ObjectWithTraversableProperties; use Rekalogika\Mapper\Tests\Fixtures\ArrayLikeDto\ObjectWithTraversablePropertyDto; +use Rekalogika\Mapper\Tests\Fixtures\ArrayLikeDto\ObjectWithTraversablePropertyWithoutTypeHintDto; +use Rekalogika\Mapper\Tests\Fixtures\Scalar\ObjectWithScalarProperties; use Rekalogika\Mapper\Tests\Fixtures\ScalarDto\ObjectWithScalarPropertiesDto; use Rekalogika\Mapper\Transformer\Model\TraversableCountableWrapper; @@ -97,4 +99,26 @@ public function testExtraLazy(): void $this->expectException(\LogicException::class); foreach ($result->property as $item); } + + // + // without type hint + // + + public function testArrayToTraversableWithoutTypehint(): void + { + $source = new ObjectWithArrayProperty(); + $result = $this->mapper->map($source, ObjectWithTraversablePropertyWithoutTypeHintDto::class); + + $this->assertInstanceOf(\Traversable::class, $result->property); + $this->assertInstanceOf(\Countable::class, $result->property); + + foreach ($result->property as $item) { + $this->assertInstanceOf(ObjectWithScalarProperties::class, $item); + + $this->assertEquals(1, $item->a); + $this->assertEquals("string", $item->b); + $this->assertEquals(true, $item->c); + $this->assertEquals(1.1, $item->d); + } + } }