Skip to content

Commit 4556560

Browse files
committed
Enhance property meta with container meta
1 parent f4ebc7b commit 4556560

6 files changed

Lines changed: 208 additions & 1 deletion

File tree

src/Metadata/Converter/SchemaToTypesConverter.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
use GoetasWebservices\XML\XSDReader\Schema\Element\ElementDef;
77
use GoetasWebservices\XML\XSDReader\Schema\Schema;
8+
use GoetasWebservices\XML\XSDReader\Schema\Type\ComplexType;
89
use GoetasWebservices\XML\XSDReader\Schema\Type\Type;
910
use Soap\Engine\Metadata\Collection\TypeCollection;
1011
use Soap\WsdlReader\Metadata\Converter\Types\ParentContext;
@@ -18,6 +19,12 @@ final class SchemaToTypesConverter
1819
{
1920
public function __invoke(Schema $schema, TypesConverterContext $context): TypeCollection
2021
{
22+
/** @var ComplexType $type */
23+
//$type = $schema->findType('nullableGroupRef', 'http://test-uri/');
24+
//dd($type->getElements());
25+
//dd($schema->findGroup('mygroup', 'http://test-uri/'));
26+
27+
2128
return $context->visit($schema, function () use ($schema, $context): TypeCollection {
2229
return new TypeCollection(
2330
...filter_nulls([
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace Soap\WsdlReader\Metadata\Converter\Types\Configurator;
4+
5+
use Closure;
6+
use GoetasWebservices\XML\XSDReader\Schema\Element\ElementContainer;
7+
use GoetasWebservices\XML\XSDReader\Schema\Element\InterfaceSetMinMax;
8+
use Soap\Engine\Metadata\Collection\PropertyCollection;
9+
use Soap\Engine\Metadata\Model\Property;
10+
use Soap\Engine\Metadata\Model\TypeMeta;
11+
use Soap\Engine\Metadata\Model\XsdType;
12+
use Soap\WsdlReader\Metadata\Converter\Types\TypesConverterContext;
13+
use function Psl\Fun\pipe;
14+
use function Psl\Vec\map;
15+
16+
final readonly class ElementContainerConfigurator
17+
{
18+
public function __invoke(PropertyCollection $properties, mixed $xsdType, TypesConverterContext $context): PropertyCollection
19+
{
20+
return $properties;
21+
if (!$xsdType instanceof ElementContainer) {
22+
return $properties;
23+
}
24+
25+
$configure = pipe(
26+
fn (PropertyCollection $properties): PropertyCollection => $this->enhanceMinMaxSettings($properties, $xsdType),
27+
);
28+
29+
return $configure($properties);
30+
}
31+
32+
/**
33+
* @param Closure(XsdType): XsdType $mapper
34+
*/
35+
private function mapPropertyTypes(PropertyCollection $properties, Closure $mapper): PropertyCollection
36+
{
37+
return new PropertyCollection(
38+
...map(
39+
$properties,
40+
static fn (Property $property): Property => new Property(
41+
$property->getName(),
42+
$mapper($property->getType())
43+
)
44+
)
45+
);
46+
}
47+
48+
private function enhanceMinMaxSettings(PropertyCollection $properties, mixed $xsdType): PropertyCollection
49+
{
50+
if (!$xsdType instanceof InterfaceSetMinMax) {
51+
return $properties;
52+
}
53+
54+
//dd($xsdType);
55+
56+
$containerMin = $xsdType->getMin();
57+
$containerMax = $xsdType->getMax();
58+
$forceNullable = $containerMin === 0 && $containerMax === 1;
59+
$forceList = $containerMax > 1 || $containerMax === -1;
60+
61+
return $this->mapPropertyTypes($properties, static function (XsdType $type) use (
62+
$containerMin, $containerMax, $forceList, $forceNullable
63+
): XsdType {
64+
$meta = $type->getMeta();
65+
$min = ($forceList || $forceNullable) ? 0 : $meta->minOccurs()->unwrapOr($containerMax);
66+
$max = $forceList ? -1 : $meta->maxOccurs()->unwrapOr($containerMax);
67+
$isNullable = $forceNullable || $meta->isNullable()->unwrapOr(false) || ($min === 0 && $max === 1);
68+
$isList = $forceList || $meta->isList()->unwrapOr(false) || ($max > 1 || $max === -1);
69+
70+
//dump($type->getXmlTargetNodeName(), $meta->maxOccurs());
71+
72+
return $type
73+
->withBaseType($isList ? 'array' : $type->getBaseType())
74+
->withMeta(
75+
static fn (TypeMeta $meta): TypeMeta => $meta
76+
->withMinOccurs($containerMin)
77+
->withMaxOccurs($containerMax)
78+
->withIsNullable($isNullable)
79+
->withIsList($isList)
80+
->withIsRepeatingElement($isList)
81+
);
82+
});
83+
}
84+
}

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,22 @@ public function __invoke(EngineType $engineType, mixed $xsdType, TypesConverterC
1616
return $engineType;
1717
}
1818

19+
$xsdname = $xsdType->getName();
1920
$min = $xsdType->getMin();
2021
$max = $xsdType->getMax();
22+
23+
xdebug_break();
24+
2125
$isNullable = $engineType->getMeta()->isNullable()->unwrapOr(false) || ($min === 0 && $max === 1);
2226
$isList = $engineType->getMeta()->isList()->unwrapOr(false) || ($max > 1 || $max === -1);
2327

28+
// TODO : To investigate : xsd-reader is returning max of "1" for list and scoped. It is invalidly inherited from sequence.
29+
30+
//dump(['xsdname' => $xsdType->getName(), 'min' => $min, 'max' => $max]);
31+
if ($xsdType->getName() === 'list') {
32+
//dd($xsdType);
33+
}
34+
2435
return $engineType
2536
->withBaseType($isList ? 'array' : $engineType->getBaseType())
2637
->withMeta(

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,15 @@ public function __invoke(ElementContainer $container, TypesConverterContext $con
3232
private function parseElementItem(ElementItem $element, TypesConverterContext $context): PropertyCollection
3333
{
3434
if ($element instanceof Group || $element instanceof Choice || $element instanceof Sequence) {
35-
return $this->__invoke($element, $context);
35+
$configure = pipe(
36+
static fn (PropertyCollection $properties) => (new Configurator\ElementContainerConfigurator())($properties, $element, $context),
37+
);
38+
39+
return $configure($this->__invoke($element, $context));
3640
}
3741

42+
xdebug_break();
43+
3844
$typeName = (new ElementTypeNameDetector())($element, $context->parent()->unwrap());
3945
$configure = pipe(
4046
static fn (EngineType $engineType): EngineType => (new Configurator\ElementConfigurator())($engineType, $element, $context),
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
--TEST--
2+
SOAP XML Schema 1015: Group ref with minOccurs / MaxOccurs
3+
--FILE--
4+
<?php
5+
include __DIR__."/test_schema.inc";
6+
$schema = <<<EOF
7+
<complexType name="nullableGroupRef">
8+
<sequence>
9+
<group minOccurs="0" ref="tns:mygroup"/>
10+
</sequence>
11+
</complexType>
12+
<complexType name="listGroupRef">
13+
<sequence>
14+
<group minOccurs="0" maxOccurs="unbounded" ref="tns:mygroup"/>
15+
</sequence>
16+
</complexType>
17+
<complexType name="scopedGroupRef">
18+
<sequence>
19+
<group minOccurs="2" maxOccurs="6" ref="tns:mygroup"/>
20+
</sequence>
21+
</complexType>
22+
<complexType name="singleGroupRef">
23+
<sequence>
24+
<group minOccurs="1" maxOccurs="1" ref="tns:mygroup"/>
25+
</sequence>
26+
</complexType>
27+
<group name="mygroup">
28+
<sequence>
29+
<element name="nullable" type="string" minOccurs="0" />
30+
<element name="list" type="string" minOccurs="0" maxOccurs="unbounded" />
31+
<element name="scoped" type="string" minOccurs="3" maxOccurs="5" />
32+
<element name="single" type="string" minOccurs="1" maxOccurs="1"/>
33+
</sequence>
34+
</group>
35+
EOF;
36+
test_schema($schema,'type="tns:Element"');
37+
?>
38+
--EXPECT--
39+
Methods:
40+
> test(Element $testParam): void
41+
42+
Types:
43+
> http://test-uri/:nullableGroupRef {
44+
?string $nullable
45+
array<int<0, max>, string> $list
46+
array<int<0, 5>, string> $scoped
47+
?string $single
48+
}
49+
> http://test-uri/:listGroupRef {
50+
array<int<0, max>, string> $nullable
51+
array<int<0, max>, string> $list
52+
array<int<0, max>, string> $scoped
53+
array<int<0, max>, string> $single
54+
}
55+
> http://test-uri/:scopedGroupRef {
56+
array<int<0, 6>, string> $nullable
57+
array<int<0, max>, string> $list
58+
array<int<6, 30>, string> $scoped
59+
array<int<2, 6>, string> $single
60+
}
61+
> http://test-uri/:singleGroupRef {
62+
?string $nullable
63+
array<int<0, max>, string> $list
64+
array<int<3, 5>, string> $scoped
65+
string $single
66+
}

tests/PhpCompatibility/test.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
include __DIR__."/test_schema.inc";
3+
$schema = <<<EOF
4+
<complexType name="nullableGroupRef">
5+
<sequence>
6+
<group minOccurs="0" ref="tns:mygroup"/>
7+
</sequence>
8+
</complexType>
9+
<complexType name="listGroupRef">
10+
<sequence>
11+
<group minOccurs="0" maxOccurs="unbounded" ref="tns:mygroup"/>
12+
</sequence>
13+
</complexType>
14+
<complexType name="scopedGroupRef">
15+
<sequence>
16+
<group minOccurs="2" maxOccurs="6" ref="tns:mygroup"/>
17+
</sequence>
18+
</complexType>
19+
<complexType name="singleGroupRef">
20+
<sequence>
21+
<group minOccurs="1" maxOccurs="1" ref="tns:mygroup"/>
22+
</sequence>
23+
</complexType>
24+
<group name="mygroup">
25+
<sequence>
26+
<element name="nullable" type="string" minOccurs="0" />
27+
<element name="list" type="string" minOccurs="0" maxOccurs="unbounded" />
28+
<element name="scoped" type="string" minOccurs="3" maxOccurs="5" />
29+
<element name="single" type="string" minOccurs="1" maxOccurs="1"/>
30+
</sequence>
31+
</group>
32+
EOF;
33+
test_schema($schema,'type="tns:Element"');

0 commit comments

Comments
 (0)