Skip to content

Commit ffb4e82

Browse files
authored
Merge pull request #41 from veewee/attribute-type-names
Prefix local types with parent type-name
2 parents 1128124 + b25d608 commit ffb4e82

12 files changed

Lines changed: 274 additions & 12 deletions

src/Metadata/Converter/SchemaToTypesConverter.php

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use GoetasWebservices\XML\XSDReader\Schema\Schema;
88
use GoetasWebservices\XML\XSDReader\Schema\Type\Type;
99
use Soap\Engine\Metadata\Collection\TypeCollection;
10+
use Soap\WsdlReader\Metadata\Converter\Types\ParentContext;
1011
use Soap\WsdlReader\Metadata\Converter\Types\TypesConverterContext;
1112
use Soap\WsdlReader\Metadata\Converter\Types\Visitor\ElementVisitor;
1213
use Soap\WsdlReader\Metadata\Converter\Types\Visitor\TypeVisitor;
@@ -22,15 +23,24 @@ public function __invoke(Schema $schema, TypesConverterContext $context): TypeCo
2223
...filter_nulls([
2324
...flat_map(
2425
$schema->getTypes(),
25-
static fn (Type $type): TypeCollection => (new TypeVisitor())($type, $context)
26+
static fn (Type $type): TypeCollection => (new TypeVisitor())(
27+
$type,
28+
$context->onParent(ParentContext::create($type))
29+
)
2630
),
2731
...flat_map(
2832
$schema->getElements(),
29-
static fn (ElementDef $element): TypeCollection => (new ElementVisitor())($element, $context)
33+
static fn (ElementDef $element): TypeCollection => (new ElementVisitor())(
34+
$element,
35+
$context->onParent(ParentContext::create($element))
36+
)
3037
),
3138
...flat_map(
3239
$schema->getSchemas(),
33-
fn (Schema $childSchema): TypeCollection => $this->__invoke($childSchema, $context)
40+
fn (Schema $childSchema): TypeCollection => $this->__invoke(
41+
$childSchema,
42+
$context->onParent(null)
43+
)
3444
)
3545
])
3646
);

src/Metadata/Converter/Types/Configurator/XmlTypeInfoConfigurator.php

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,14 @@
33

44
namespace Soap\WsdlReader\Metadata\Converter\Types\Configurator;
55

6+
use GoetasWebservices\XML\XSDReader\Schema\Attribute\AttributeItem;
7+
use GoetasWebservices\XML\XSDReader\Schema\Element\ElementItem;
68
use GoetasWebservices\XML\XSDReader\Schema\Item;
79
use GoetasWebservices\XML\XSDReader\Schema\SchemaItem;
810
use GoetasWebservices\XML\XSDReader\Schema\Type\Type;
911
use Soap\Engine\Metadata\Model\XsdType as EngineType;
12+
use Soap\WsdlReader\Metadata\Converter\Types\Detector\AttributeTypeNameDetector;
13+
use Soap\WsdlReader\Metadata\Converter\Types\Detector\ElementTypeNameDetector;
1014
use Soap\WsdlReader\Metadata\Converter\Types\TypesConverterContext;
1115

1216
final class XmlTypeInfoConfigurator
@@ -25,9 +29,16 @@ public function __invoke(EngineType $engineType, mixed $xsdType, TypesConverterC
2529
$targetNamespace = $xsdType->getSchema()->getTargetNamespace() ?? '';
2630
$typeNamespace = $type?->getSchema()->getTargetNamespace() ?: $targetNamespace;
2731

32+
$parentContext = $context->parent()->unwrapOr(null);
33+
$xmlTypeName = match(true) {
34+
$parentContext && $item instanceof ElementItem => (new ElementTypeNameDetector())($item, $parentContext),
35+
$parentContext && $item instanceof AttributeItem => (new AttributeTypeNameDetector())($item, $parentContext),
36+
default => $typeName,
37+
};
38+
2839
return $engineType
2940
->withXmlTargetNodeName($itemName ?: $typeName)
30-
->withXmlTypeName($typeName ?: $itemName ?: '')
41+
->withXmlTypeName($xmlTypeName)
3142
->withXmlNamespace($typeNamespace)
3243
->withXmlNamespaceName(
3344
$context->knownNamespaces->lookupNameFromNamespace($typeNamespace)->unwrapOr(
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace Soap\WsdlReader\Metadata\Converter\Types\Detector;
4+
5+
use GoetasWebservices\XML\XSDReader\Schema\Attribute\AttributeContainer;
6+
use GoetasWebservices\XML\XSDReader\Schema\Attribute\AttributeItem;
7+
use GoetasWebservices\XML\XSDReader\Schema\Item;
8+
use GoetasWebservices\XML\XSDReader\Schema\SchemaItem;
9+
use GoetasWebservices\XML\XSDReader\Schema\Type\Type;
10+
use Psl\Option\Option;
11+
use function Psl\Option\none;
12+
use function Psl\Option\some;
13+
14+
final class AttributeDeclaringParentTypeDetector
15+
{
16+
/**
17+
* This class detects the declaring parent type of an attribute.
18+
* It can be used together with the ParentContext and works as followed
19+
*
20+
* - If the parent is an AttributeContainer, it will check if the parent has the attribute
21+
* - If the parent is not declaring the attribute, it will check if the parent is extending another type and test this extended type.
22+
*
23+
* @return Option<Type>
24+
*/
25+
public function __invoke(AttributeItem $item, ?SchemaItem $parent): Option
26+
{
27+
$parent = match(true) {
28+
$parent instanceof Item => $parent->getType(),
29+
default => $parent,
30+
};
31+
32+
if (!$parent instanceof Type) {
33+
return none();
34+
}
35+
36+
if ($parent instanceof AttributeContainer) {
37+
foreach ($parent->getAttributes() as $parentAttribute) {
38+
if ($parentAttribute->getName() === $item->getName()) {
39+
/** @var Option<Type> */
40+
return some($parent);
41+
}
42+
}
43+
}
44+
45+
$extensionBase = $parent->getExtension()?->getBase();
46+
if ($extensionBase) {
47+
return $this->__invoke($item, $extensionBase);
48+
}
49+
50+
return none();
51+
}
52+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace Soap\WsdlReader\Metadata\Converter\Types\Detector;
4+
5+
use GoetasWebservices\XML\XSDReader\Schema\Attribute\AttributeItem;
6+
use GoetasWebservices\XML\XSDReader\Schema\Attribute\AttributeSingle;
7+
use GoetasWebservices\XML\XSDReader\Schema\Type\Type;
8+
use Psl\Option\Option;
9+
use Soap\WsdlReader\Metadata\Converter\Types\ParentContext;
10+
use function Psl\Option\from_nullable;
11+
12+
final class AttributeTypeNameDetector
13+
{
14+
public function __invoke(AttributeItem $attribute, ParentContext $parentContext): string
15+
{
16+
$attributeType = $attribute instanceof AttributeSingle ? $attribute->getType() : null;
17+
$attributeRestriction = $attributeType?->getRestriction();
18+
$attributeTypeName = $attributeType?->getName();
19+
$attributeRestrictionName = ($attributeRestriction && !$attributeRestriction->getChecks()) ? $attributeRestriction->getBase()?->getName() : null;
20+
21+
$typeName = $attributeTypeName ?: ($attributeRestrictionName ?: $attribute->getName());
22+
23+
// If a name cannot be determined from the type, we fallback to the attribute name:
24+
// Prefix the attribute name with the parent element name resulting in a more unique type-name.
25+
if (!$attributeTypeName && !$attributeRestrictionName) {
26+
$typeName = (new AttributeDeclaringParentTypeDetector())($attribute, $parentContext->currentParent())
27+
->andThen(static fn (Type $parent): Option => from_nullable($parent->getName()))
28+
->map(static fn (string $parentName): string => $parentName . ucfirst($typeName))
29+
->unwrapOr($typeName);
30+
}
31+
32+
return $typeName;
33+
}
34+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace Soap\WsdlReader\Metadata\Converter\Types\Detector;
4+
5+
use GoetasWebservices\XML\XSDReader\Schema\Element\ElementItem;
6+
use GoetasWebservices\XML\XSDReader\Schema\Element\ElementSingle;
7+
use GoetasWebservices\XML\XSDReader\Schema\Type\SimpleType;
8+
use GoetasWebservices\XML\XSDReader\Schema\Type\Type;
9+
use Soap\WsdlReader\Metadata\Converter\Types\ParentContext;
10+
11+
final class ElementTypeNameDetector
12+
{
13+
public function __invoke(ElementItem $element, ParentContext $parentContext, ?string $calculatedTypeName = null): string
14+
{
15+
$type = $element instanceof ElementSingle ? $element->getType() : null;
16+
$typeName = $calculatedTypeName ?? ($type?->getName() ?: $element->getName());
17+
18+
// For inline simple types, we prefix the name of the element with the name of the parent type.
19+
if ($type instanceof SimpleType && !$type->getName()) {
20+
$parent = $parentContext->currentParent();
21+
22+
if ($parent instanceof Type || $parent instanceof ElementItem) {
23+
if ($parentName = $parent->getName()) {
24+
$typeName = $parentName . ucfirst($typeName);
25+
}
26+
}
27+
}
28+
29+
return $typeName;
30+
}
31+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace Soap\WsdlReader\Metadata\Converter\Types;
4+
5+
use GoetasWebservices\XML\XSDReader\Schema\SchemaItem;
6+
use function Psl\Iter\first;
7+
use function Psl\Iter\last;
8+
9+
final class ParentContext
10+
{
11+
/**
12+
* @param non-empty-list<SchemaItem> $items
13+
*/
14+
private function __construct(
15+
public readonly array $items,
16+
) {
17+
}
18+
19+
public static function create(SchemaItem $item): self
20+
{
21+
return new self([$item]);
22+
}
23+
24+
public function withNextParent(SchemaItem $item): self
25+
{
26+
return new self([...$this->items, $item]);
27+
}
28+
29+
public function rootParent(): SchemaItem
30+
{
31+
return first($this->items);
32+
}
33+
34+
public function currentParent(): SchemaItem
35+
{
36+
return last($this->items);
37+
}
38+
}

src/Metadata/Converter/Types/TypesConverterContext.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@
44
namespace Soap\WsdlReader\Metadata\Converter\Types;
55

66
use GoetasWebservices\XML\XSDReader\Schema\Schema;
7+
use Psl\Option\Option;
78
use Soap\Engine\Metadata\Collection\TypeCollection;
89
use Soap\WsdlReader\Model\Definitions\Namespaces;
910
use Soap\WsdlReader\Parser\Definitions\SchemaParser;
11+
use function Psl\Option\from_nullable;
12+
use function Psl\Option\none;
1013

1114
final class TypesConverterContext
1215
{
@@ -20,9 +23,15 @@ final class TypesConverterContext
2023
*/
2124
private array $visited = [];
2225

26+
/**
27+
* @var Option<ParentContext>
28+
*/
29+
private Option $parentContext;
30+
2331
private function __construct(
2432
public readonly Namespaces $knownNamespaces
2533
) {
34+
$this->parentContext = none();
2635
}
2736

2837
public static function default(Namespaces $knownNamespaces): self
@@ -57,4 +66,19 @@ public function visit(Schema $schema, callable $visitor): TypeCollection
5766

5867
return $visitor($schema);
5968
}
69+
70+
public function onParent(?ParentContext $parentContext): self
71+
{
72+
$this->parentContext = from_nullable($parentContext);
73+
74+
return $this;
75+
}
76+
77+
/**
78+
* @return Option<ParentContext>
79+
*/
80+
public function parent(): Option
81+
{
82+
return $this->parentContext;
83+
}
6084
}

src/Metadata/Converter/Types/Visitor/AttributeContainerVisitor.php

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@
55

66
use GoetasWebservices\XML\XSDReader\Schema\Attribute\AttributeContainer;
77
use GoetasWebservices\XML\XSDReader\Schema\Attribute\AttributeItem;
8-
use GoetasWebservices\XML\XSDReader\Schema\Attribute\AttributeSingle;
98
use GoetasWebservices\XML\XSDReader\Schema\Attribute\Group;
109
use GoetasWebservices\XML\XSDReader\Schema\Type\Type;
1110
use Soap\Engine\Metadata\Collection\PropertyCollection;
1211
use Soap\Engine\Metadata\Model\Property;
1312
use Soap\Engine\Metadata\Model\TypeMeta;
1413
use Soap\Engine\Metadata\Model\XsdType as EngineType;
1514
use Soap\WsdlReader\Metadata\Converter\Types\Configurator;
15+
use Soap\WsdlReader\Metadata\Converter\Types\Detector\AttributeTypeNameDetector;
1616
use Soap\WsdlReader\Metadata\Converter\Types\TypesConverterContext;
1717
use function Psl\Fun\pipe;
1818
use function Psl\Result\wrap;
@@ -88,9 +88,7 @@ private function parseAttribute(AttributeItem $attribute, TypesConverterContext
8888
return $this->parseAttributes($attribute, $context);
8989
}
9090

91-
$attributeType = $attribute instanceof AttributeSingle ? $attribute->getType() : null;
92-
$typeName = $attributeType?->getName() ?: $attribute->getName();
93-
91+
$typeName = (new AttributeTypeNameDetector())($attribute, $context->parent()->unwrap());
9492
$configure = pipe(
9593
static fn (EngineType $engineType): EngineType => (new Configurator\AttributeConfigurator())($engineType, $attribute, $context),
9694
);

src/Metadata/Converter/Types/Visitor/ElementContainerVisitor.php

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@
66
use GoetasWebservices\XML\XSDReader\Schema\Element\Choice;
77
use GoetasWebservices\XML\XSDReader\Schema\Element\ElementContainer;
88
use GoetasWebservices\XML\XSDReader\Schema\Element\ElementItem;
9-
use GoetasWebservices\XML\XSDReader\Schema\Element\ElementSingle;
109
use GoetasWebservices\XML\XSDReader\Schema\Element\Group;
1110
use GoetasWebservices\XML\XSDReader\Schema\Element\Sequence;
1211
use Soap\Engine\Metadata\Collection\PropertyCollection;
1312
use Soap\Engine\Metadata\Model\Property;
1413
use Soap\Engine\Metadata\Model\XsdType as EngineType;
1514
use Soap\WsdlReader\Metadata\Converter\Types\Configurator;
15+
use Soap\WsdlReader\Metadata\Converter\Types\Detector\ElementTypeNameDetector;
1616
use Soap\WsdlReader\Metadata\Converter\Types\TypesConverterContext;
1717
use function Psl\Fun\pipe;
1818
use function Psl\Vec\flat_map;
@@ -35,8 +35,7 @@ private function parseElementItem(ElementItem $element, TypesConverterContext $c
3535
return $this->__invoke($element, $context);
3636
}
3737

38-
$type = $element instanceof ElementSingle ? $element->getType() : null;
39-
$typeName = $type?->getName() ?: $element->getName();
38+
$typeName = (new ElementTypeNameDetector())($element, $context->parent()->unwrap());
4039
$configure = pipe(
4140
static fn (EngineType $engineType): EngineType => (new Configurator\ElementConfigurator())($engineType, $element, $context),
4241
static fn (EngineType $engineType): EngineType => (new Configurator\AnyElementConfigurator())($engineType, $element, $context),

src/Metadata/Converter/Types/Visitor/InlineElementTypeVisitor.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ private function detectInlineTypes(ElementItem $element, TypesConverterContext $
6262
return new TypeCollection();
6363
}
6464

65-
return $elementVisitor($element, $context);
65+
return $elementVisitor($element, $context->onParent(
66+
$context->parent()->unwrap()->withNextParent($element)
67+
));
6668
}
6769
}

0 commit comments

Comments
 (0)