diff --git a/src/Phpro/SoapClient/Soap/Metadata/Manipulators/DuplicateTypes/IntersectDuplicateTypesStrategy.php b/src/Phpro/SoapClient/Soap/Metadata/Manipulators/DuplicateTypes/IntersectDuplicateTypesStrategy.php index 07e82a7c..441c5742 100644 --- a/src/Phpro/SoapClient/Soap/Metadata/Manipulators/DuplicateTypes/IntersectDuplicateTypesStrategy.php +++ b/src/Phpro/SoapClient/Soap/Metadata/Manipulators/DuplicateTypes/IntersectDuplicateTypesStrategy.php @@ -10,7 +10,15 @@ use Soap\Engine\Metadata\Collection\TypeCollection; use Soap\Engine\Metadata\Model\Property; use Soap\Engine\Metadata\Model\Type; +use Soap\Engine\Metadata\Model\TypeMeta; +use function Psl\Iter\contains; +use function Psl\Iter\first; +use function Psl\Iter\reduce; +use function Psl\Type\instance_of; use function Psl\Type\non_empty_string; +use function Psl\Vec\flat_map; +use function Psl\Vec\map; +use function Psl\Vec\values; final class IntersectDuplicateTypesStrategy implements TypesManipulatorInterface { @@ -36,14 +44,12 @@ function (array $result, Type $type) use ($allTypes): array { private function intersectTypes(TypeCollection $duplicateTypes): Type { + $type = instance_of(Type::class)->assert(first($duplicateTypes)); + return new Type( - current(iterator_to_array($duplicateTypes))->getXsdType(), + $type->getXsdType(), $this->uniqueProperties( - new PropertyCollection(...array_merge( - ...$duplicateTypes->map( - static fn (Type $type): array => iterator_to_array($type->getProperties()) - ) - )) + ...map($duplicateTypes, static fn (Type $type) => $type->getProperties()) ) ); } @@ -55,13 +61,43 @@ private function fetchAllTypesNormalizedByName(TypeCollection $types, string $na }); } - private function uniqueProperties(PropertyCollection $props): PropertyCollection + private function uniqueProperties(PropertyCollection ...$types): PropertyCollection { - return new PropertyCollection(...array_values( - array_combine( - $props->map(static fn (Property $prop) => $prop->getName()), - iterator_to_array($props) + $allProps = flat_map($types, static fn (PropertyCollection $props) => $props); + $typePropNames = map( + $types, + static fn (PropertyCollection $props): array => $props->map(static fn (Property $prop) => $prop->getName()) + ); + $intersectedPropNames = array_intersect(...$typePropNames); + + return new PropertyCollection( + ...values( + reduce( + $allProps, + /** + * @param array $result + * + * @return array + */ + static function (array $result, Property $prop) use ($intersectedPropNames): array { + $result[$prop->getName()] = new Property( + $prop->getName(), + $prop->getType()->withMeta( + static function (TypeMeta $meta) use ($prop, $intersectedPropNames): TypeMeta { + if (contains($intersectedPropNames, $prop->getName())) { + return $meta; + } + + return $meta->withIsNullable(true); + } + ) + ); + + return $result; + }, + [] + ) ) - )); + ); } } diff --git a/test/PhproTest/SoapClient/Unit/Soap/Metadata/Manipulators/DuplicateTypes/IntersectDuplicateTypesStrategyTest.php b/test/PhproTest/SoapClient/Unit/Soap/Metadata/Manipulators/DuplicateTypes/IntersectDuplicateTypesStrategyTest.php index 94045cca..9457ca31 100644 --- a/test/PhproTest/SoapClient/Unit/Soap/Metadata/Manipulators/DuplicateTypes/IntersectDuplicateTypesStrategyTest.php +++ b/test/PhproTest/SoapClient/Unit/Soap/Metadata/Manipulators/DuplicateTypes/IntersectDuplicateTypesStrategyTest.php @@ -11,6 +11,7 @@ use Soap\Engine\Metadata\Collection\TypeCollection; use Soap\Engine\Metadata\Model\Property; use Soap\Engine\Metadata\Model\Type; +use Soap\Engine\Metadata\Model\TypeMeta; use Soap\Engine\Metadata\Model\XsdType; class IntersectDuplicateTypesStrategyTest extends TestCase @@ -29,9 +30,10 @@ public function it_can_intersect_duplicate_types(): void new Type(XsdType::create('file'), new PropertyCollection( new Property('prop1', XsdType::create('string')), new Property('prop3', XsdType::create('string')), + new Property('prop4', XsdType::create('string')), )), new Type(XsdType::create('file'), new PropertyCollection( - new Property('prop1', XsdType::create('string')), + new Property('prop1', XsdType::create('int')), new Property('prop2', XsdType::create('string')), )), new Type(XsdType::create('uppercased'), new PropertyCollection()), @@ -44,14 +46,16 @@ public function it_can_intersect_duplicate_types(): void ); $manipulated = $strategy($types); + $nullable = static fn(TypeMeta $meta) => $meta->withIsNullable(true); self::assertInstanceOf(TypeCollection::class, $manipulated); self::assertEquals( [ new Type(XsdType::create('file'), new PropertyCollection( - new Property('prop1', XsdType::create('string')), - new Property('prop3', XsdType::create('string')), - new Property('prop2', XsdType::create('string')), + new Property('prop1', XsdType::create('int')), + new Property('prop3', XsdType::create('string')->withMeta($nullable)), + new Property('prop4', XsdType::create('string')->withMeta($nullable)), + new Property('prop2', XsdType::create('string')->withMeta($nullable)), )), new Type(XsdType::create('uppercased'), new PropertyCollection()), new Type(XsdType::create('with-specialchar'), new PropertyCollection()),