Skip to content

Commit ca6bc7b

Browse files
authored
Merge pull request #16 from veewee/import-root-type-error
Fix import of removed (optimized) root xmlns during flattening schemas
2 parents 22a8fce + a8dd63c commit ca6bc7b

File tree

5 files changed

+108
-11
lines changed

5 files changed

+108
-11
lines changed

src/Xml/Configurator/FlattenXsdImports.php

+26-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use VeeWee\Xml\Exception\RuntimeException;
1919
use function Psl\Type\instance_of;
2020
use function Psl\Type\nullable;
21+
use function VeeWee\Xml\Dom\Assert\assert_element;
2122
use function VeeWee\Xml\Dom\Locator\Node\children;
2223
use function VeeWee\Xml\Dom\Manipulator\Element\copy_named_xmlns_attributes;
2324
use function VeeWee\Xml\Dom\Manipulator\Node\append_external_node;
@@ -188,16 +189,40 @@ private function registerSchemaInTypes(DOMElement $schema): void
188189

189190
// If no schema exists yet: Add the newly loaded schema as a completely new schema in the WSDL types.
190191
if (!$existingSchema) {
191-
append_external_node($types, $schema);
192+
$imported = assert_element(append_external_node($types, $schema));
193+
$this->fixRemovedDefaultXmlnsDeclarationsDuringImport($imported, $schema);
192194
return;
193195
}
194196

195197
// When an existing schema exists, all xmlns attributes need to be copied.
196198
// This is to make sure that possible QNames (strings) get resolved in XSD.
197199
// Finally - all children of the newly loaded schema can be appended to the existing schema.
198200
copy_named_xmlns_attributes($existingSchema, $schema);
201+
$this->fixRemovedDefaultXmlnsDeclarationsDuringImport($existingSchema, $schema);
199202
children($schema)->forEach(
200203
static fn (DOMNode $node) => append_external_node($existingSchema, $node)
201204
);
202205
}
206+
207+
/**
208+
* @see https://gist.github.com/veewee/32c3aa94adcf878700a9d5baa4b2a2de
209+
*
210+
* PHP does an optimization of namespaces during `importNode()`.
211+
* In some cases, this causes the root xmlns to be removed from the imported node which could lead to xsd qname errors.
212+
*
213+
* This function tries to re-add the root xmlns if it's available on the source but not on the target.
214+
*
215+
* It will most likely be solved in PHP 8.4's new spec compliant DOM\XMLDocument implementation.
216+
* @see https://github.com/php/php-src/pull/13031
217+
*
218+
* For now, this will do the trick.
219+
*/
220+
private function fixRemovedDefaultXmlnsDeclarationsDuringImport(DOMElement $target, DOMElement $source): void
221+
{
222+
if (!$source->getAttribute('xmlns') || $target->hasAttribute('xmlns')) {
223+
return;
224+
}
225+
226+
$target->setAttribute('xmlns', $source->getAttribute('xmlns'));
227+
}
203228
}

tests/Unit/Xml/Configurator/FlattenXsdImportsTest.php

+23-10
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Soap\Wsdl\Loader\StreamWrapperLoader;
99
use Soap\Wsdl\Xml\Configurator\FlattenXsdImports;
1010
use VeeWee\Xml\Dom\Document;
11+
use function VeeWee\Xml\Dom\Configurator\canonicalize;
1112
use function VeeWee\Xml\Dom\Configurator\comparable;
1213

1314
final class FlattenXsdImportsTest extends TestCase
@@ -16,47 +17,59 @@ final class FlattenXsdImportsTest extends TestCase
1617
*
1718
* @dataProvider provideTestCases
1819
*/
19-
public function test_it_can_flatten_xsd_imports(string $wsdlUri, Document $expected): void
20+
public function test_it_can_flatten_xsd_imports(string $wsdlUri, Document $expected, callable $xmlConfigurator): void
2021
{
2122
$wsdl = Document::fromXmlFile($wsdlUri);
2223
$configurator = new FlattenXsdImports(
2324
$wsdlUri,
2425
FlatteningContext::forWsdl($wsdlUri, $wsdl, new StreamWrapperLoader())
2526
);
26-
$flattened = Document::fromUnsafeDocument($wsdl->toUnsafeDocument(), $configurator, comparable());
27+
$flattened = Document::fromUnsafeDocument($wsdl->toUnsafeDocument(), $configurator, $xmlConfigurator);
2728

28-
static::assertSame($expected->toXmlString(), $flattened->toXmlString());
29+
static::assertSame($expected->reconfigure($xmlConfigurator)->toXmlString(), $flattened->toXmlString());
2930
}
3031

3132
public function provideTestCases()
3233
{
3334
yield 'single-xsd' => [
3435
'wsdl' => FIXTURE_DIR.'/flattening/single-xsd.wsdl',
35-
'expected' => Document::fromXmlFile(FIXTURE_DIR.'/flattening/result/single-xsd-result.wsdl', comparable()),
36+
'expected' => Document::fromXmlFile(FIXTURE_DIR.'/flattening/result/single-xsd-result.wsdl'),
37+
comparable(),
3638
];
3739
yield 'once-xsd' => [
3840
'wsdl' => FIXTURE_DIR.'/flattening/once-xsd.wsdl',
39-
'expected' => Document::fromXmlFile(FIXTURE_DIR.'/flattening/result/once-xsd-result.wsdl', comparable()),
41+
'expected' => Document::fromXmlFile(FIXTURE_DIR.'/flattening/result/once-xsd-result.wsdl'),
42+
comparable(),
4043
];
4144
yield 'multi-xsd' => [
4245
'wsdl' => FIXTURE_DIR.'/flattening/multi-xsd.wsdl',
43-
'expected' => Document::fromXmlFile(FIXTURE_DIR.'/flattening/result/multi-xsd-result.wsdl', comparable()),
46+
'expected' => Document::fromXmlFile(FIXTURE_DIR.'/flattening/result/multi-xsd-result.wsdl'),
47+
comparable(),
4448
];
4549
yield 'circular-xsd' => [
4650
'wsdl' => FIXTURE_DIR.'/flattening/circular-xsd.wsdl',
47-
'expected' => Document::fromXmlFile(FIXTURE_DIR.'/flattening/result/circular-xsd-result.wsdl', comparable()),
51+
'expected' => Document::fromXmlFile(FIXTURE_DIR.'/flattening/result/circular-xsd-result.wsdl'),
52+
comparable(),
4853
];
4954
yield 'redefine-xsd' => [
5055
'wsdl' => FIXTURE_DIR.'/flattening/redefine-xsd.wsdl',
51-
'expected' => Document::fromXmlFile(FIXTURE_DIR.'/flattening/result/redefine-xsd-result.wsdl', comparable()),
56+
'expected' => Document::fromXmlFile(FIXTURE_DIR.'/flattening/result/redefine-xsd-result.wsdl'),
57+
comparable(),
5258
];
5359
yield 'tnsless-xsd' => [
5460
'wsdl' => FIXTURE_DIR.'/flattening/tnsless-xsd.wsdl',
55-
'expected' => Document::fromXmlFile(FIXTURE_DIR.'/flattening/result/tnsless-xsd-result.wsdl', comparable()),
61+
'expected' => Document::fromXmlFile(FIXTURE_DIR.'/flattening/result/tnsless-xsd-result.wsdl'),
62+
comparable(),
5663
];
5764
yield 'grouped-xsd' => [
5865
'wsdl' => FIXTURE_DIR.'/flattening/grouped-xsd.wsdl',
59-
'expected' => Document::fromXmlFile(FIXTURE_DIR.'/flattening/result/grouped-xsd-result.wsdl', comparable()),
66+
'expected' => Document::fromXmlFile(FIXTURE_DIR.'/flattening/result/grouped-xsd-result.wsdl'),
67+
comparable(),
68+
];
69+
yield 'root-xmlns-import-issue' => [
70+
'wsdl' => FIXTURE_DIR.'/flattening/root-xmlns-import-issue.wsdl',
71+
'expected' => Document::fromXmlFile(FIXTURE_DIR.'/flattening/result/root-xmlns-import-issue-result.wsdl'),
72+
canonicalize(),
6073
];
6174
}
6275
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2+
<wsdl:definitions
3+
name="agenzia"
4+
targetNamespace="http://www.immobinet.it/wsdl/WebserviceModAgenziaBundle"
5+
xmlns:imw="http://www.immobinet.it/wsdl/WebserviceModAgenziaBundle"
6+
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
7+
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
8+
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
9+
xmlns:core="http://www.immobinet.it/schema/WebserviceCoreBundle"
10+
xmlns:imm2="http://www.immobinet.it/schema/WebserviceImmobileBundle"
11+
>
12+
<wsdl:types>
13+
<xsd:schema xmlns:imsw="http://www.immobinet.it/schema/WebserviceModAgenziaBundle" elementFormDefault="qualified" targetNamespace="http://www.immobinet.it/wsdl/WebserviceModAgenziaBundle">
14+
<xsd:import namespace="http://www.immobinet.it/schema/WebserviceCoreBundle"/>
15+
</xsd:schema>
16+
<xsd:schema
17+
xmlns="http://www.w3.org/2001/XMLSchema"
18+
xmlns:imm="http://www.immobinet.it/schema/WebserviceCoreBundle"
19+
targetNamespace="http://www.immobinet.it/schema/WebserviceCoreBundle"
20+
elementFormDefault="qualified"
21+
>
22+
<xsd:complexType name="Auth">
23+
<xsd:sequence>
24+
<xsd:element name="username" type="string" maxOccurs="1" minOccurs="1"/>
25+
<xsd:element name="password" type="string" maxOccurs="1" minOccurs="1"/>
26+
</xsd:sequence>
27+
</xsd:complexType>
28+
</xsd:schema>
29+
</wsdl:types>
30+
</wsdl:definitions>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2+
<wsdl:definitions name="agenzia" targetNamespace="http://www.immobinet.it/wsdl/WebserviceModAgenziaBundle"
3+
xmlns:imw="http://www.immobinet.it/wsdl/WebserviceModAgenziaBundle"
4+
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
5+
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
6+
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
7+
xmlns:core="http://www.immobinet.it/schema/WebserviceCoreBundle"
8+
xmlns:imm2="http://www.immobinet.it/schema/WebserviceImmobileBundle">
9+
<wsdl:types>
10+
<xsd:schema elementFormDefault="qualified"
11+
targetNamespace="http://www.immobinet.it/wsdl/WebserviceModAgenziaBundle"
12+
xmlns:imsw="http://www.immobinet.it/schema/WebserviceModAgenziaBundle"
13+
>
14+
<xsd:import schemaLocation="xsd/root-xmlns-import-issue-core.xsd"
15+
namespace="http://www.immobinet.it/schema/WebserviceCoreBundle"></xsd:import>
16+
17+
</xsd:schema>
18+
</wsdl:types>
19+
</wsdl:definitions>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.immobinet.it/schema/WebserviceCoreBundle"
3+
xmlns:imm="http://www.immobinet.it/schema/WebserviceCoreBundle" elementFormDefault="qualified">
4+
<complexType name="Auth">
5+
<sequence>
6+
<element name="username" type="string" maxOccurs="1" minOccurs="1"></element>
7+
<element name="password" type="string" maxOccurs="1" minOccurs="1"></element>
8+
</sequence>
9+
</complexType>
10+
</schema>

0 commit comments

Comments
 (0)