From 2d84d9fd89e5b352561bd9d83ba4915b7f3333d5 Mon Sep 17 00:00:00 2001 From: Toon Verwerft Date: Thu, 2 Jan 2025 11:29:52 +0100 Subject: [PATCH] Prefix attribute type-names with parent type name. --- .../Converter/SchemaToTypesConverter.php | 16 ++++-- .../AttributeDeclaringParentTypeDetector.php | 52 +++++++++++++++++++ .../Converter/Types/ParentContext.php | 13 +++++ .../Converter/Types/TypesConverterContext.php | 14 +++++ .../Visitor/AttributeContainerVisitor.php | 25 ++++++++- .../Visitor/InlineElementTypeVisitor.php | 3 +- tests/PhpCompatibility/schema1013.phpt | 32 ++++++++++++ 7 files changed, 149 insertions(+), 6 deletions(-) create mode 100644 src/Metadata/Converter/Types/Detector/AttributeDeclaringParentTypeDetector.php create mode 100644 src/Metadata/Converter/Types/ParentContext.php create mode 100644 tests/PhpCompatibility/schema1013.phpt diff --git a/src/Metadata/Converter/SchemaToTypesConverter.php b/src/Metadata/Converter/SchemaToTypesConverter.php index 48db279..4d05e71 100644 --- a/src/Metadata/Converter/SchemaToTypesConverter.php +++ b/src/Metadata/Converter/SchemaToTypesConverter.php @@ -7,6 +7,7 @@ use GoetasWebservices\XML\XSDReader\Schema\Schema; use GoetasWebservices\XML\XSDReader\Schema\Type\Type; use Soap\Engine\Metadata\Collection\TypeCollection; +use Soap\WsdlReader\Metadata\Converter\Types\ParentContext; use Soap\WsdlReader\Metadata\Converter\Types\TypesConverterContext; use Soap\WsdlReader\Metadata\Converter\Types\Visitor\ElementVisitor; use Soap\WsdlReader\Metadata\Converter\Types\Visitor\TypeVisitor; @@ -22,15 +23,24 @@ public function __invoke(Schema $schema, TypesConverterContext $context): TypeCo ...filter_nulls([ ...flat_map( $schema->getTypes(), - static fn (Type $type): TypeCollection => (new TypeVisitor())($type, $context) + static fn (Type $type): TypeCollection => (new TypeVisitor())( + $type, + $context->onParent(new ParentContext($type)) + ) ), ...flat_map( $schema->getElements(), - static fn (ElementDef $element): TypeCollection => (new ElementVisitor())($element, $context) + static fn (ElementDef $element): TypeCollection => (new ElementVisitor())( + $element, + $context->onParent(new ParentContext($element)) + ) ), ...flat_map( $schema->getSchemas(), - fn (Schema $childSchema): TypeCollection => $this->__invoke($childSchema, $context) + fn (Schema $childSchema): TypeCollection => $this->__invoke( + $childSchema, + $context->onParent(null) + ) ) ]) ); diff --git a/src/Metadata/Converter/Types/Detector/AttributeDeclaringParentTypeDetector.php b/src/Metadata/Converter/Types/Detector/AttributeDeclaringParentTypeDetector.php new file mode 100644 index 0000000..c523078 --- /dev/null +++ b/src/Metadata/Converter/Types/Detector/AttributeDeclaringParentTypeDetector.php @@ -0,0 +1,52 @@ + + */ + public function __invoke(AttributeItem $item, ?SchemaItem $parent): Option + { + $parent = match(true) { + $parent instanceof Item => $parent->getType(), + default => $parent, + }; + + if (!$parent instanceof Type) { + return none(); + } + + if ($parent instanceof AttributeContainer) { + foreach ($parent->getAttributes() as $parentAttribute) { + if ($parentAttribute->getName() === $item->getName()) { + /** @var Option */ + return some($parent); + } + } + } + + $extensionBase = $parent->getExtension()?->getBase(); + if ($extensionBase) { + return $this->__invoke($item, $extensionBase); + } + + return none(); + } +} diff --git a/src/Metadata/Converter/Types/ParentContext.php b/src/Metadata/Converter/Types/ParentContext.php new file mode 100644 index 0000000..89a4fe9 --- /dev/null +++ b/src/Metadata/Converter/Types/ParentContext.php @@ -0,0 +1,13 @@ +parentContext = $parentContext; + + return $this; + } + + public function parent(): ?ParentContext + { + return $this->parentContext; + } } diff --git a/src/Metadata/Converter/Types/Visitor/AttributeContainerVisitor.php b/src/Metadata/Converter/Types/Visitor/AttributeContainerVisitor.php index c6a2a9d..a64e6cb 100644 --- a/src/Metadata/Converter/Types/Visitor/AttributeContainerVisitor.php +++ b/src/Metadata/Converter/Types/Visitor/AttributeContainerVisitor.php @@ -8,13 +8,16 @@ use GoetasWebservices\XML\XSDReader\Schema\Attribute\AttributeSingle; use GoetasWebservices\XML\XSDReader\Schema\Attribute\Group; use GoetasWebservices\XML\XSDReader\Schema\Type\Type; +use Psl\Option\Option; use Soap\Engine\Metadata\Collection\PropertyCollection; use Soap\Engine\Metadata\Model\Property; use Soap\Engine\Metadata\Model\TypeMeta; use Soap\Engine\Metadata\Model\XsdType as EngineType; use Soap\WsdlReader\Metadata\Converter\Types\Configurator; +use Soap\WsdlReader\Metadata\Converter\Types\Detector\AttributeDeclaringParentTypeDetector; use Soap\WsdlReader\Metadata\Converter\Types\TypesConverterContext; use function Psl\Fun\pipe; +use function Psl\Option\from_nullable; use function Psl\Result\wrap; use function Psl\Type\instance_of; use function Psl\Vec\flat_map; @@ -88,8 +91,26 @@ private function parseAttribute(AttributeItem $attribute, TypesConverterContext return $this->parseAttributes($attribute, $context); } + // Detecting the type-name for an attribute is complex. + // We first try to use the type name, + // Next up is the base type of the restriction if there aren't any restriction checks configured. + // Finally there is a fallback to the attribute name $attributeType = $attribute instanceof AttributeSingle ? $attribute->getType() : null; - $typeName = $attributeType?->getName() ?: $attribute->getName(); + $attributeRestriction = $attributeType?->getRestriction(); + $attributeTypeName = $attributeType?->getName(); + $attributeRestrictionName = ($attributeRestriction && !$attributeRestriction->getChecks()) ? $attributeRestriction->getBase()?->getName() : null; + + $typeName = $attributeTypeName ?: ($attributeRestrictionName ?: $attribute->getName()); + $engineType = EngineType::guess($typeName); + + // If a name cannot be determined from the type, we fallback to the attribute name: + // Prefix the attribute name with the parent element name resulting in a more unique type-name. + if (!$attributeTypeName && !$attributeRestrictionName) { + $engineType = (new AttributeDeclaringParentTypeDetector())($attribute, $context->parent()?->item) + ->andThen(static fn (Type $parent): Option => from_nullable($parent->getName())) + ->map(static fn (string $parentName): EngineType => $engineType->copy($parentName . ucfirst($typeName))) + ->unwrapOr($engineType); + } $configure = pipe( static fn (EngineType $engineType): EngineType => (new Configurator\AttributeConfigurator())($engineType, $attribute, $context), @@ -98,7 +119,7 @@ private function parseAttribute(AttributeItem $attribute, TypesConverterContext return new PropertyCollection( new Property( $attribute->getName(), - $configure(EngineType::guess($typeName)) + $configure($engineType) ) ); } diff --git a/src/Metadata/Converter/Types/Visitor/InlineElementTypeVisitor.php b/src/Metadata/Converter/Types/Visitor/InlineElementTypeVisitor.php index 68278f3..6269431 100644 --- a/src/Metadata/Converter/Types/Visitor/InlineElementTypeVisitor.php +++ b/src/Metadata/Converter/Types/Visitor/InlineElementTypeVisitor.php @@ -11,6 +11,7 @@ use GoetasWebservices\XML\XSDReader\Schema\Type\ComplexType; use GoetasWebservices\XML\XSDReader\Schema\Type\Type; use Soap\Engine\Metadata\Collection\TypeCollection; +use Soap\WsdlReader\Metadata\Converter\Types\ParentContext; use Soap\WsdlReader\Metadata\Converter\Types\TypesConverterContext; use function Psl\Vec\flat_map; @@ -62,6 +63,6 @@ private function detectInlineTypes(ElementItem $element, TypesConverterContext $ return new TypeCollection(); } - return $elementVisitor($element, $context); + return $elementVisitor($element, $context->onParent(new ParentContext($element))); } } diff --git a/tests/PhpCompatibility/schema1013.phpt b/tests/PhpCompatibility/schema1013.phpt new file mode 100644 index 0000000..cfd386b --- /dev/null +++ b/tests/PhpCompatibility/schema1013.phpt @@ -0,0 +1,32 @@ +--TEST-- +SOAP XML Schema 1001: Prepend element name before attribute type names for more unique type-names. +--FILE-- + + + + + + + + + + + + + + +EOF; +test_schema($schema,'type="tns:VehicleCoreType"'); +?> +--EXPECT-- +Methods: + > test(VehicleCoreType $testParam): void + +Types: + > http://test-uri/:VehicleCoreType { + ?string $VehType + @?VehicleCoreTypeDriveType in (AWD|4WD|Unspecified) $DriveType + }