From b2b87a98ec933c314eb699cd57670385aa31ce51 Mon Sep 17 00:00:00 2001 From: Toon Verwerft Date: Wed, 30 Sep 2026 07:41:35 +0200 Subject: [PATCH] Resolve references between inline schemas without explicit imports A WSDL can hold several inline schemas in its types section where one schema refers to an element or type of another without importing that namespace. ext-soap treats all inline schemas as one pool and accepts this. xsd-reader only resolves references through imports, so reading such a WSDL failed with "Can't find element named {namespace}#Name". SchemaParser now adds the missing namespace-only imports between the inline schemas before handing them to xsd-reader. It leaves existing imports alone. In phpro/soap-client#614 the provider's own schema refers to ns1:Vehicle without importing its namespace. The FlatteningLoader copies that schema as is, which is why flattening did not help. Fixes phpro/soap-client#614 --- src/Parser/Definitions/SchemaParser.php | 36 +++++++++++++++++++++++++ tests/PhpCompatibility/schema1017.phpt | 34 +++++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 tests/PhpCompatibility/schema1017.phpt diff --git a/src/Parser/Definitions/SchemaParser.php b/src/Parser/Definitions/SchemaParser.php index a571813..39a6c7e 100644 --- a/src/Parser/Definitions/SchemaParser.php +++ b/src/Parser/Definitions/SchemaParser.php @@ -74,9 +74,45 @@ private static function legacySchemaNodes(Document $wsdl): array $result[] = $node; } + self::importSiblingSchemas($result); + return $result; } + /** + * WSDLs often contain multiple inline schemas that reference each other without an explicit import. + * ext-soap treats all inline schemas as one pool of types, whereas the XSD reader only resolves + * references through imports. Adding the missing imports makes the inline schemas visible to each other. + * + * @param list $schemaNodes + */ + private static function importSiblingSchemas(array $schemaNodes): void + { + $namespaces = array_unique(array_filter( + array_map( + static fn (DOMElement $schema): string => $schema->getAttribute('targetNamespace'), + $schemaNodes + ) + )); + + foreach ($schemaNodes as $schema) { + $imported = [$schema->getAttribute('targetNamespace')]; + /** @var DOMNode $child */ + foreach ($schema->childNodes as $child) { + if ($child instanceof DOMElement && $child->namespaceURI === SchemaReader::XSD_NS && $child->localName === 'import') { + $imported[] = $child->getAttribute('namespace'); + } + } + + foreach (array_diff($namespaces, $imported) as $namespace) { + $import = $schema->ownerDocument->createElementNS(SchemaReader::XSD_NS, 'import'); + assert($import instanceof DOMElement); + $import->setAttribute('namespace', $namespace); + $schema->insertBefore($import, $schema->firstChild); + } + } + } + /** * TODO: Can be removed once goetas-webservices/xsd-reader supports Dom\Element. */ diff --git a/tests/PhpCompatibility/schema1017.phpt b/tests/PhpCompatibility/schema1017.phpt new file mode 100644 index 0000000..091487e --- /dev/null +++ b/tests/PhpCompatibility/schema1017.phpt @@ -0,0 +1,34 @@ +--TEST-- +SOAP XML Schema 1017: Inline schemas referencing each other without explicit imports +--FILE-- + + + + + + + + + + + + + +EOF; +test_schema($schema,'type="tns:testType"'); +?> +--EXPECT-- +Methods: + > test(testType $testParam): void + +Types: + > http://test-uri/:testType { + Selection $selection + } + > http://test-uri/:Vehicle extends anySimpleType + > http://test-uri/other/:Selection { + string $Vehicle + }