Skip to content

Commit

Permalink
Merge pull request #16 from veewee/import-root-type-error
Browse files Browse the repository at this point in the history
Fix import of removed (optimized) root xmlns during flattening schemas
  • Loading branch information
veewee committed Jan 14, 2024
2 parents 22a8fce + a8dd63c commit ca6bc7b
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 11 deletions.
27 changes: 26 additions & 1 deletion src/Xml/Configurator/FlattenXsdImports.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use VeeWee\Xml\Exception\RuntimeException;
use function Psl\Type\instance_of;
use function Psl\Type\nullable;
use function VeeWee\Xml\Dom\Assert\assert_element;
use function VeeWee\Xml\Dom\Locator\Node\children;
use function VeeWee\Xml\Dom\Manipulator\Element\copy_named_xmlns_attributes;
use function VeeWee\Xml\Dom\Manipulator\Node\append_external_node;
Expand Down Expand Up @@ -188,16 +189,40 @@ private function registerSchemaInTypes(DOMElement $schema): void

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

// When an existing schema exists, all xmlns attributes need to be copied.
// This is to make sure that possible QNames (strings) get resolved in XSD.
// Finally - all children of the newly loaded schema can be appended to the existing schema.
copy_named_xmlns_attributes($existingSchema, $schema);
$this->fixRemovedDefaultXmlnsDeclarationsDuringImport($existingSchema, $schema);
children($schema)->forEach(
static fn (DOMNode $node) => append_external_node($existingSchema, $node)
);
}

/**
* @see https://gist.github.com/veewee/32c3aa94adcf878700a9d5baa4b2a2de
*
* PHP does an optimization of namespaces during `importNode()`.
* In some cases, this causes the root xmlns to be removed from the imported node which could lead to xsd qname errors.
*
* This function tries to re-add the root xmlns if it's available on the source but not on the target.
*
* It will most likely be solved in PHP 8.4's new spec compliant DOM\XMLDocument implementation.
* @see https://github.com/php/php-src/pull/13031
*
* For now, this will do the trick.
*/
private function fixRemovedDefaultXmlnsDeclarationsDuringImport(DOMElement $target, DOMElement $source): void
{
if (!$source->getAttribute('xmlns') || $target->hasAttribute('xmlns')) {
return;
}

$target->setAttribute('xmlns', $source->getAttribute('xmlns'));
}
}
33 changes: 23 additions & 10 deletions tests/Unit/Xml/Configurator/FlattenXsdImportsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Soap\Wsdl\Loader\StreamWrapperLoader;
use Soap\Wsdl\Xml\Configurator\FlattenXsdImports;
use VeeWee\Xml\Dom\Document;
use function VeeWee\Xml\Dom\Configurator\canonicalize;
use function VeeWee\Xml\Dom\Configurator\comparable;

final class FlattenXsdImportsTest extends TestCase
Expand All @@ -16,47 +17,59 @@ final class FlattenXsdImportsTest extends TestCase
*
* @dataProvider provideTestCases
*/
public function test_it_can_flatten_xsd_imports(string $wsdlUri, Document $expected): void
public function test_it_can_flatten_xsd_imports(string $wsdlUri, Document $expected, callable $xmlConfigurator): void
{
$wsdl = Document::fromXmlFile($wsdlUri);
$configurator = new FlattenXsdImports(
$wsdlUri,
FlatteningContext::forWsdl($wsdlUri, $wsdl, new StreamWrapperLoader())
);
$flattened = Document::fromUnsafeDocument($wsdl->toUnsafeDocument(), $configurator, comparable());
$flattened = Document::fromUnsafeDocument($wsdl->toUnsafeDocument(), $configurator, $xmlConfigurator);

static::assertSame($expected->toXmlString(), $flattened->toXmlString());
static::assertSame($expected->reconfigure($xmlConfigurator)->toXmlString(), $flattened->toXmlString());
}

public function provideTestCases()
{
yield 'single-xsd' => [
'wsdl' => FIXTURE_DIR.'/flattening/single-xsd.wsdl',
'expected' => Document::fromXmlFile(FIXTURE_DIR.'/flattening/result/single-xsd-result.wsdl', comparable()),
'expected' => Document::fromXmlFile(FIXTURE_DIR.'/flattening/result/single-xsd-result.wsdl'),
comparable(),
];
yield 'once-xsd' => [
'wsdl' => FIXTURE_DIR.'/flattening/once-xsd.wsdl',
'expected' => Document::fromXmlFile(FIXTURE_DIR.'/flattening/result/once-xsd-result.wsdl', comparable()),
'expected' => Document::fromXmlFile(FIXTURE_DIR.'/flattening/result/once-xsd-result.wsdl'),
comparable(),
];
yield 'multi-xsd' => [
'wsdl' => FIXTURE_DIR.'/flattening/multi-xsd.wsdl',
'expected' => Document::fromXmlFile(FIXTURE_DIR.'/flattening/result/multi-xsd-result.wsdl', comparable()),
'expected' => Document::fromXmlFile(FIXTURE_DIR.'/flattening/result/multi-xsd-result.wsdl'),
comparable(),
];
yield 'circular-xsd' => [
'wsdl' => FIXTURE_DIR.'/flattening/circular-xsd.wsdl',
'expected' => Document::fromXmlFile(FIXTURE_DIR.'/flattening/result/circular-xsd-result.wsdl', comparable()),
'expected' => Document::fromXmlFile(FIXTURE_DIR.'/flattening/result/circular-xsd-result.wsdl'),
comparable(),
];
yield 'redefine-xsd' => [
'wsdl' => FIXTURE_DIR.'/flattening/redefine-xsd.wsdl',
'expected' => Document::fromXmlFile(FIXTURE_DIR.'/flattening/result/redefine-xsd-result.wsdl', comparable()),
'expected' => Document::fromXmlFile(FIXTURE_DIR.'/flattening/result/redefine-xsd-result.wsdl'),
comparable(),
];
yield 'tnsless-xsd' => [
'wsdl' => FIXTURE_DIR.'/flattening/tnsless-xsd.wsdl',
'expected' => Document::fromXmlFile(FIXTURE_DIR.'/flattening/result/tnsless-xsd-result.wsdl', comparable()),
'expected' => Document::fromXmlFile(FIXTURE_DIR.'/flattening/result/tnsless-xsd-result.wsdl'),
comparable(),
];
yield 'grouped-xsd' => [
'wsdl' => FIXTURE_DIR.'/flattening/grouped-xsd.wsdl',
'expected' => Document::fromXmlFile(FIXTURE_DIR.'/flattening/result/grouped-xsd-result.wsdl', comparable()),
'expected' => Document::fromXmlFile(FIXTURE_DIR.'/flattening/result/grouped-xsd-result.wsdl'),
comparable(),
];
yield 'root-xmlns-import-issue' => [
'wsdl' => FIXTURE_DIR.'/flattening/root-xmlns-import-issue.wsdl',
'expected' => Document::fromXmlFile(FIXTURE_DIR.'/flattening/result/root-xmlns-import-issue-result.wsdl'),
canonicalize(),
];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<wsdl:definitions
name="agenzia"
targetNamespace="http://www.immobinet.it/wsdl/WebserviceModAgenziaBundle"
xmlns:imw="http://www.immobinet.it/wsdl/WebserviceModAgenziaBundle"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:core="http://www.immobinet.it/schema/WebserviceCoreBundle"
xmlns:imm2="http://www.immobinet.it/schema/WebserviceImmobileBundle"
>
<wsdl:types>
<xsd:schema xmlns:imsw="http://www.immobinet.it/schema/WebserviceModAgenziaBundle" elementFormDefault="qualified" targetNamespace="http://www.immobinet.it/wsdl/WebserviceModAgenziaBundle">
<xsd:import namespace="http://www.immobinet.it/schema/WebserviceCoreBundle"/>
</xsd:schema>
<xsd:schema
xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:imm="http://www.immobinet.it/schema/WebserviceCoreBundle"
targetNamespace="http://www.immobinet.it/schema/WebserviceCoreBundle"
elementFormDefault="qualified"
>
<xsd:complexType name="Auth">
<xsd:sequence>
<xsd:element name="username" type="string" maxOccurs="1" minOccurs="1"/>
<xsd:element name="password" type="string" maxOccurs="1" minOccurs="1"/>
</xsd:sequence>
</xsd:complexType>
</xsd:schema>
</wsdl:types>
</wsdl:definitions>
19 changes: 19 additions & 0 deletions tests/fixtures/flattening/root-xmlns-import-issue.wsdl
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<wsdl:definitions name="agenzia" targetNamespace="http://www.immobinet.it/wsdl/WebserviceModAgenziaBundle"
xmlns:imw="http://www.immobinet.it/wsdl/WebserviceModAgenziaBundle"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:core="http://www.immobinet.it/schema/WebserviceCoreBundle"
xmlns:imm2="http://www.immobinet.it/schema/WebserviceImmobileBundle">
<wsdl:types>
<xsd:schema elementFormDefault="qualified"
targetNamespace="http://www.immobinet.it/wsdl/WebserviceModAgenziaBundle"
xmlns:imsw="http://www.immobinet.it/schema/WebserviceModAgenziaBundle"
>
<xsd:import schemaLocation="xsd/root-xmlns-import-issue-core.xsd"
namespace="http://www.immobinet.it/schema/WebserviceCoreBundle"></xsd:import>

</xsd:schema>
</wsdl:types>
</wsdl:definitions>
10 changes: 10 additions & 0 deletions tests/fixtures/flattening/xsd/root-xmlns-import-issue-core.xsd
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.immobinet.it/schema/WebserviceCoreBundle"
xmlns:imm="http://www.immobinet.it/schema/WebserviceCoreBundle" elementFormDefault="qualified">
<complexType name="Auth">
<sequence>
<element name="username" type="string" maxOccurs="1" minOccurs="1"></element>
<element name="password" type="string" maxOccurs="1" minOccurs="1"></element>
</sequence>
</complexType>
</schema>

0 comments on commit ca6bc7b

Please sign in to comment.