diff --git a/src/Metadata/Converter/SchemaToTypesConverter.php b/src/Metadata/Converter/SchemaToTypesConverter.php
index 089df38..e1f3583 100644
--- a/src/Metadata/Converter/SchemaToTypesConverter.php
+++ b/src/Metadata/Converter/SchemaToTypesConverter.php
@@ -22,7 +22,10 @@ public function __invoke(Schema $schema, TypesConverterContext $context): TypeCo
return $context->visit($schema, function () use ($schema, $context): TypeCollection {
return new TypeCollection(
...filter_nulls([
- ...map($schema->getTypes(), static fn (Type $type): SoapType => (new TypeVisitor())($type, $context)),
+ ...flat_map(
+ $schema->getTypes(),
+ static fn (Type $type): TypeCollection => (new TypeVisitor())($type, $context)
+ ),
...map($schema->getElements(), static fn (ElementDef $element): ?SoapType => (new ElementVisitor())($element, $context)),
...flat_map(
$schema->getSchemas(),
diff --git a/src/Metadata/Converter/Types/Visitor/ElementVisitor.php b/src/Metadata/Converter/Types/Visitor/ElementVisitor.php
index ce1eaa3..04e6f4c 100644
--- a/src/Metadata/Converter/Types/Visitor/ElementVisitor.php
+++ b/src/Metadata/Converter/Types/Visitor/ElementVisitor.php
@@ -3,7 +3,7 @@
namespace Soap\WsdlReader\Metadata\Converter\Types\Visitor;
-use GoetasWebservices\XML\XSDReader\Schema\Element\ElementDef;
+use GoetasWebservices\XML\XSDReader\Schema\Element\AbstractElementSingle;
use Soap\Engine\Metadata\Model\Type as EngineType;
use Soap\Engine\Metadata\Model\XsdType as MetaType;
use Soap\WsdlReader\Metadata\Converter\Types\Configurator;
@@ -12,7 +12,7 @@
final class ElementVisitor
{
- public function __invoke(ElementDef $element, TypesConverterContext $context): ?EngineType
+ public function __invoke(AbstractElementSingle $element, TypesConverterContext $context): ?EngineType
{
// When there is no type set on the element, we cannot do anything with it here. There should always be one somehow.
$xsdType = $element->getType();
diff --git a/src/Metadata/Converter/Types/Visitor/TypeVisitor.php b/src/Metadata/Converter/Types/Visitor/TypeVisitor.php
index 052aaa7..f4abdb8 100644
--- a/src/Metadata/Converter/Types/Visitor/TypeVisitor.php
+++ b/src/Metadata/Converter/Types/Visitor/TypeVisitor.php
@@ -3,24 +3,72 @@
namespace Soap\WsdlReader\Metadata\Converter\Types\Visitor;
+use GoetasWebservices\XML\XSDReader\Schema\Element\AbstractElementSingle;
+use GoetasWebservices\XML\XSDReader\Schema\Element\ElementItem;
+use GoetasWebservices\XML\XSDReader\Schema\Element\ElementRef;
+use GoetasWebservices\XML\XSDReader\Schema\Type\ComplexType;
use GoetasWebservices\XML\XSDReader\Schema\Type\Type;
+use Soap\Engine\Metadata\Collection\TypeCollection;
use Soap\Engine\Metadata\Model\Type as EngineType;
use Soap\Engine\Metadata\Model\XsdType as MetaType;
use Soap\WsdlReader\Metadata\Converter\Types\Configurator;
use Soap\WsdlReader\Metadata\Converter\Types\TypesConverterContext;
use function Psl\Fun\pipe;
+use function Psl\Vec\filter_nulls;
+use function Psl\Vec\map;
final class TypeVisitor
{
- public function __invoke(Type $xsdType, TypesConverterContext $context): EngineType
+ public function __invoke(Type $xsdType, TypesConverterContext $context): TypeCollection
{
$configure = pipe(
static fn (MetaType $metaType): MetaType => (new Configurator\TypeConfigurator())($metaType, $xsdType, $context),
);
- return new EngineType(
- $configure(MetaType::guess($xsdType->getName() ?? '')),
- (new PropertiesVisitor())($xsdType, $context)
+ return new TypeCollection(
+ new EngineType(
+ $configure(MetaType::guess($xsdType->getName() ?? '')),
+ (new PropertiesVisitor())($xsdType, $context)
+ ),
+ ...$this->parseNestedInlineElements($xsdType, $context),
+ );
+ }
+
+ /**
+ * Complex types may contain nested inline types.
+ * Let's unwrap them:
+ */
+ private function parseNestedInlineElements(Type $xsdType, TypesConverterContext $context): TypeCollection
+ {
+ if (!$xsdType instanceof ComplexType) {
+ return new TypeCollection();
+ }
+
+ $elementVisitor = new ElementVisitor();
+
+ return new TypeCollection(
+ ...filter_nulls(
+ map(
+ $xsdType->getElements(),
+ static function (ElementItem $element) use ($elementVisitor, $context): ?EngineType {
+ if (!$element instanceof AbstractElementSingle || $element instanceof ElementRef) {
+ return null;
+ }
+
+ // There is no need to create types for simple elements like strings.
+ if (!$element->getType() instanceof ComplexType) {
+ return null;
+ }
+
+ // If the element links to a named type, we already know about it.
+ if ($element->getType()?->getName()) {
+ return null;
+ }
+
+ return $elementVisitor($element, $context);
+ }
+ )
+ )
);
}
}
diff --git a/tests/PhpCompatibility/schema036.phpt b/tests/PhpCompatibility/schema036.phpt
index eb07c55..1105014 100644
--- a/tests/PhpCompatibility/schema036.phpt
+++ b/tests/PhpCompatibility/schema036.phpt
@@ -27,4 +27,7 @@ Types:
> http://test-uri/:testType {
int $int
testType2 $testType2
- }
\ No newline at end of file
+ }
+ > http://test-uri/:testType2 {
+ int $int
+ }
diff --git a/tests/PhpCompatibility/schema1002.phpt b/tests/PhpCompatibility/schema1002.phpt
new file mode 100644
index 0000000..e27b1c3
--- /dev/null
+++ b/tests/PhpCompatibility/schema1002.phpt
@@ -0,0 +1,51 @@
+--TEST--
+SOAP XML Schema 18: union with list
+--FILE--
+
+
+ Specifies charges and/or penalties associated with making ticket changes after purchase.
+
+
+
+
+ Specifies penalty charges as either a currency amount or a percentage of the fare
+
+
+
+
+ Indicates the type of penalty involved in the search or response.
+
+
+
+
+ Identifier used to indicate whether the change occurs before or after departure from the origin city.
+
+
+
+
+
+
+
+ Indicator used to specify whether voluntary change and other penalties are involved in the search or response.
+
+
+
+EOF;
+test_schema($schema,'type="tns:VoluntaryChangesType"');
+?>
+--EXPECT--
+Methods:
+ > test(VoluntaryChangesType $testParam): void
+
+Types:
+ > http://test-uri/:VoluntaryChangesType {
+ ?Penalty $Penalty
+ @boolean $VolChangeInd
+ }
+ > http://test-uri/:Penalty {
+ @string $PenaltyType
+ @string $DepartureStatus
+ }