Skip to content

Commit 0c5a1af

Browse files
authored
Merge pull request #27 from veewee/fix-xsd-includes-without-target-namespace
Fix XSD includes without targetNamespace
2 parents 6140d29 + 0a2a19e commit 0c5a1af

File tree

4 files changed

+50
-22
lines changed

4 files changed

+50
-22
lines changed

src/Xml/Configurator/FlattenXsdImports.php

+43-17
Original file line numberDiff line numberDiff line change
@@ -85,22 +85,39 @@ private function includeSchema(DOMElement $include): ?DOMElement
8585
}
8686

8787
/*
88-
* Currently we do not validate the namespace of includes - we assume the provided imports are valid!
88+
* Target namespace rules:
8989
*
9090
* @see https://docs.microsoft.com/en-us/previous-versions/dotnet/netframework-4.0/ms256198(v=vs.100)
9191
* The included schema document must meet one of the following conditions.
92-
- It must have the same target namespace as the containing schema document.
93-
- It must not have a target namespace specified (no targetNamespace attribute).
92+
- It must have the same target namespace as the containing schema document.
93+
- It must not have a target namespace specified (no targetNamespace attribute).
94+
In that case, the including schema document provides the target namespace for the new one.
9495
*/
9596

97+
// Detect namespaces from both the current schema and the import
98+
$parentSchema = $this->detectParentSchemaNode($include);
99+
$parentTns = $parentSchema->getAttribute('targetNamespace');
100+
96101
$schema = $this->loadSchema($location);
102+
if ($schema) {
103+
// If the included schema has no targetNamespace, it will inherit the parent schema's namespace.
104+
if (!$schema->hasAttribute('targetNamespace')) {
105+
$schema->setAttribute('targetNamespace', $parentTns);
106+
}
107+
108+
// Validate if the namespaces of the included document match the parent schema.
109+
$schemaTns = $schema->getAttribute('targetNamespace');
110+
if ($schemaTns !== $parentTns) {
111+
throw FlattenException::invalidIncludeTargetNamespace($parentTns, $schemaTns);
112+
}
97113

98-
// Redefines overwrite tags from includes.
99-
// The children of redefine elements are appended to the newly loaded schema.
100-
if ($schema && $include->localName === 'redefine') {
101-
children($include)->map(
102-
static fn (DOMNode $node) => append_external_node($schema, $node)
103-
);
114+
// Redefines overwrite tags from includes.
115+
// The children of redefine elements are appended to the newly loaded schema.
116+
if ($include->localName === 'redefine') {
117+
children($include)->map(
118+
static fn (DOMNode $node) => append_external_node($schema, $node)
119+
);
120+
}
104121
}
105122

106123
// Include tags can be removed, since the schema will be made available in the types
@@ -123,19 +140,14 @@ private function importSchema(DOMElement $import): ?DOMElement
123140
return null;
124141
}
125142

126-
// Find the schema that wants to import the new schema:
127-
$doc = Document::fromUnsafeDocument($import->ownerDocument);
128-
$xpath = $doc->xpath(new WsdlPreset($doc));
129-
/** @var DOMElement $schema */
130-
$schema = $xpath->querySingle('ancestor::schema:schema', $import);
131-
132143
// Detect namespaces from both the current schema and the import
133144
$namespace = $import->getAttribute('namespace');
134-
$tns = $schema->getAttribute('targetNamespace');
145+
$parentSchema = $this->detectParentSchemaNode($import);
146+
$parentTns = $parentSchema->getAttribute('targetNamespace');
135147

136148
// Imports can only deal with different namespaces.
137149
// You'll need to use "include" if you want to inject something in the same namespace.
138-
if ($tns && $namespace && $tns === $namespace) {
150+
if ($parentTns && $namespace && $parentTns === $namespace) {
139151
throw FlattenException::unableToImportXsd($location);
140152
}
141153

@@ -147,6 +159,20 @@ private function importSchema(DOMElement $import): ?DOMElement
147159
return $schema;
148160
}
149161

162+
/**
163+
* This function will return the parent declaring <schema /> tag for elements like import | include, ...
164+
*
165+
* @throws RuntimeException
166+
*/
167+
private function detectParentSchemaNode(DOMElement $element): DOMElement
168+
{
169+
$parent = Document::fromUnsafeDocument($element->ownerDocument);
170+
$xpath = $parent->xpath(new WsdlPreset($parent));
171+
172+
/** @var DOMElement */
173+
return $xpath->querySingle('ancestor::schema:schema', $element);
174+
}
175+
150176
/**
151177
* @throws RuntimeException
152178
* @throws UnloadableWsdlException

src/Xml/Exception/FlattenException.php

+5
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ public static function noLocation(string $elementName): self
1313
return new self("Parsing Schema: {$elementName} has no 'schemaLocation' attribute");
1414
}
1515

16+
public static function invalidIncludeTargetNamespace(string $parentTns, string $currentTns): self
17+
{
18+
return new self("Parsing Schema: include has an invalid targetNamespace of '$currentTns'. Expected '$parentTns'");
19+
}
20+
1621
public static function unableToImportXsd(string $location): self
1722
{
1823
$target = $location ? ' from '.$location : '';

tests/fixtures/flattening/result/tnsless-xsd-result.wsdl

+1-4
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,9 @@
66
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
77
<types>
88
<xsd:schema targetNamespace="http://soapinterop.org/store1">
9-
10-
</xsd:schema>
11-
<xsd:schema xmlns="http://www.w3.org/2001/XMLSchema" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
129
<xsd:complexType name="Store">
1310
<xsd:sequence>
14-
<element minOccurs="1" maxOccurs="1" name="Attribute1" type="string"/>
11+
<xsd:element minOccurs="1" maxOccurs="1" name="Attribute1" type="xsd:string"/>
1512
</xsd:sequence>
1613
</xsd:complexType>
1714
</xsd:schema>
+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<schema xmlns="http://www.w3.org/2001/XMLSchema" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
22
<xsd:complexType name="Store">
33
<xsd:sequence>
4-
<element minOccurs="1" maxOccurs="1" name="Attribute1" type="string"/>
4+
<element minOccurs="1" maxOccurs="1" name="Attribute1" type="xsd:string"/>
55
</xsd:sequence>
66
</xsd:complexType>
77
</schema>

0 commit comments

Comments
 (0)