diff --git a/.phive/phars.xml b/.phive/phars.xml
index 980defa..b2b9a1e 100644
--- a/.phive/phars.xml
+++ b/.phive/phars.xml
@@ -1,5 +1,5 @@
-
+
diff --git a/README.md b/README.md
index e07f52c..f718ffc 100644
--- a/README.md
+++ b/README.md
@@ -56,6 +56,21 @@ $loader = new StreamWrapperLoader(
$contents = $loader($wsdl);
```
+### FlatteningLoader
+
+This loader can be used if your WSDL file contains WSDL or XSD imports.
+It will any other loader internally to load all the parts.
+The result of this loader is a completely flattened WSDL file which you can e.g. cache on your local filesystem.
+
+```php
+use Soap\Wsdl\Loader\FlatteningLoader;
+use Soap\Wsdl\Loader\StreamWrapperLoader;
+
+$loader = new FlatteningLoader(new StreamWrapperLoader());
+
+$contents = $loader($wsdl);
+```
+
## WSDL Validators
@@ -92,4 +107,3 @@ $wsdl = Document::fromXmlString((new StreamWrapperLoader())($file));
echo "Validating WSDL:".PHP_EOL;
$issues = $wsdl->validate(new Validator\WsdlSyntaxValidator());
echo ($issues->count() ? $issues->toString() : '🟢 ALL GOOD').PHP_EOL;
-```
diff --git a/composer.json b/composer.json
index 4d39d27..54d3be5 100644
--- a/composer.json
+++ b/composer.json
@@ -21,8 +21,12 @@
],
"require": {
"php": "^8.0",
+ "ext-dom": "*",
+ "azjezz/psl": "^1.9",
+ "league/uri": "^6.5",
+ "league/uri-components": "^2.4",
"php-soap/xml": "^1.2",
- "veewee/xml": "^1.1"
+ "veewee/xml": "~1.2"
},
"require-dev": {
"phpunit/phpunit": "^9.5"
diff --git a/phpunit.xml b/phpunit.xml
index 07df353..c3059d4 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -1,4 +1,8 @@
-
+
./tests/Unit
diff --git a/psalm.xml b/psalm.xml
index 539e13c..8d4bba4 100644
--- a/psalm.xml
+++ b/psalm.xml
@@ -2,7 +2,6 @@
+
+
+
+
diff --git a/src/Loader/Context/FlatteningContext.php b/src/Loader/Context/FlatteningContext.php
new file mode 100644
index 0000000..545a902
--- /dev/null
+++ b/src/Loader/Context/FlatteningContext.php
@@ -0,0 +1,112 @@
+ raw (not flattened) xml
+ *
+ * @var array
+ */
+ private $catalog = [];
+
+ public static function forWsdl(
+ string $location,
+ Document $wsdl,
+ WsdlLoader $loader,
+ ): self {
+ $new = new self($wsdl, $loader);
+ $new->catalog[$location] = $wsdl->map(xml_string());
+
+ return $new;
+ }
+
+ private function __construct(
+ private Document $wsdl,
+ private WsdlLoader $loader
+ ) {
+ }
+
+ /**
+ * This function can be used to detect if the context knows about a part of the WSDL.
+ * It knows about a part from the moment that the raw XML version has been loaded once,
+ * even if the flattening process is still in an underlying import / include.
+ */
+ public function knowsAboutPart(string $location): bool
+ {
+ return array_key_exists($location, $this->catalog);
+ }
+
+ /**
+ * Imports and include only need to occur once.
+ * This function determines if an import should be done.
+ *
+ * It either returns null if the import already was done or the flattened XML if it still requires an import.
+ */
+ public function import(string $location): ?string
+ {
+ return $this->knowsAboutPart($location)
+ ? null
+ : $this->loadFlattenedXml($location);
+ }
+
+ /**
+ * Returns the base WSDL document that can be worked on by flattener configurators.
+ */
+ public function wsdl(): Document
+ {
+ return $this->wsdl;
+ }
+
+ /**
+ * This method searches for a single tag
+ * If no tag exists, it will create an empty one.
+ * If multiple tags exist, it will merge those tags into one.
+ *
+ * @throws RuntimeException
+ */
+ public function types(): DOMElement
+ {
+ $doc = Document::fromUnsafeDocument($this->wsdl->toUnsafeDocument(), new FlattenTypes());
+ $xpath = $doc->xpath(new WsdlPreset($doc));
+
+ /** @var DOMElement $types */
+ $types = $xpath->querySingle('//wsdl:types');
+
+ return $types;
+ }
+
+ /**
+ * This function will take care of the import catalog!
+ * It will first load the raw xml from the remote source and store that internally.
+ *
+ * Next it will apply the XML flattening on the loaded xml and return the flattened string.
+ * We keep track of all nested flattening locations that are in progress.
+ * This way we can prevent circular includes as well.
+ *
+ * @throws UnloadableWsdlException
+ * @throws RuntimeException
+ */
+ private function loadFlattenedXml(string $location): string
+ {
+ if (!array_key_exists($location, $this->catalog)) {
+ $this->catalog[$location] = ($this->loader)($location);
+ }
+
+ $document = Document::fromXmlString($this->catalog[$location]);
+
+ return (new Flattener())($location, $document, $this);
+ }
+}
diff --git a/src/Loader/FlatteningLoader.php b/src/Loader/FlatteningLoader.php
new file mode 100644
index 0000000..d6fa33b
--- /dev/null
+++ b/src/Loader/FlatteningLoader.php
@@ -0,0 +1,30 @@
+loader)($location));
+ $context = FlatteningContext::forWsdl($location, $currentDoc, $this->loader);
+
+ return (new Flattener())($location, $currentDoc, $context);
+ }
+}
diff --git a/src/Uri/IncludePathBuilder.php b/src/Uri/IncludePathBuilder.php
new file mode 100644
index 0000000..8b3db39
--- /dev/null
+++ b/src/Uri/IncludePathBuilder.php
@@ -0,0 +1,24 @@
+__toString();
+ }
+}
diff --git a/src/Xml/Configurator/FlattenTypes.php b/src/Xml/Configurator/FlattenTypes.php
new file mode 100644
index 0000000..21d3b7b
--- /dev/null
+++ b/src/Xml/Configurator/FlattenTypes.php
@@ -0,0 +1,61 @@
+xpath(new WsdlPreset($xml));
+ /** @var list $types */
+ $types = [...$xpath->query('wsdl:types')];
+
+ // Creates wsdl:types if no matching element exists yet
+ if (!count($types)) {
+ $document->documentElement->append(
+ namespaced_element(Xmlns::wsdl()->value(), 'types')($document)
+ );
+
+ return $document;
+ }
+
+ // Skip if only one exists
+ $first = array_shift($types);
+ if (!count($types)) {
+ return $document;
+ }
+
+ // Flattens multiple wsdl:types elements.
+ foreach ($types as $additionalTypes) {
+ $children = children($additionalTypes);
+ if (count($children)) {
+ append(...$children)($first);
+ }
+
+ remove($additionalTypes);
+ }
+
+ return $document;
+ }
+}
diff --git a/src/Xml/Configurator/FlattenWsdlImports.php b/src/Xml/Configurator/FlattenWsdlImports.php
new file mode 100644
index 0000000..531cbde
--- /dev/null
+++ b/src/Xml/Configurator/FlattenWsdlImports.php
@@ -0,0 +1,76 @@
+xpath(new WsdlPreset($xml));
+
+ $imports = $xpath->query('wsdl:import');
+ $imports->forEach(fn (DOMElement $import) => $this->importWsdlImportElement($import));
+
+ return $document;
+ }
+
+ /**
+ * @throws RuntimeException
+ * @throws UnloadableWsdlException
+ */
+ private function importWsdlImportElement(DOMElement $import): void
+ {
+ $location = IncludePathBuilder::build(
+ $import->getAttribute('location'),
+ $this->currentLocation
+ );
+
+ $result = $this->context->import($location);
+ if (!$result) {
+ remove($import);
+ return;
+ }
+
+ $imported = Document::fromXmlString($result);
+ $definitions = $imported->map(document_element());
+
+ replace_by_external_nodes(
+ $import,
+ children($definitions)
+ );
+ }
+}
diff --git a/src/Xml/Configurator/FlattenXsdImports.php b/src/Xml/Configurator/FlattenXsdImports.php
new file mode 100644
index 0000000..905d4ee
--- /dev/null
+++ b/src/Xml/Configurator/FlattenXsdImports.php
@@ -0,0 +1,203 @@
+xpath(new WsdlPreset($xml));
+
+ $imports = $xpath
+ ->query('//schema:import|//schema:include|//schema:redefine')
+ ->expectAllOfType(DOMElement::class);
+
+ if (!count($imports)) {
+ return $document;
+ }
+
+ foreach ($imports as $import) {
+ $schema = match ($import->localName) {
+ 'include', 'redefine' => $this->includeSchema($import),
+ 'import' => $this->importSchema($import),
+ };
+
+ if ($schema) {
+ $this->registerSchemaInTypes($schema);
+ }
+ }
+
+ return $document;
+ }
+
+ /**
+ * @throws RuntimeException
+ * @throws UnloadableWsdlException
+ * @throws FlattenException
+ */
+ private function includeSchema(DOMElement $include): ?DOMElement
+ {
+ // All includes and redefines require a schemLocation attribute
+ if (!$location = $include->getAttribute('schemaLocation')) {
+ throw FlattenException::noLocation($include->localName);
+ }
+
+ /*
+ * Currently we do not validate the namespace of includes - we assume the provided imports are valid!
+ *
+ * @see https://docs.microsoft.com/en-us/previous-versions/dotnet/netframework-4.0/ms256198(v=vs.100)
+ * The included schema document must meet one of the following conditions.
+ - It must have the same target namespace as the containing schema document.
+ - It must not have a target namespace specified (no targetNamespace attribute).
+ */
+
+ $schema = $this->loadSchema($location);
+
+ // Redefines overwrite tags from includes.
+ // The children of redefine elements are appended to the newly loaded schema.
+ if ($schema && $include->localName === 'redefine') {
+ children($include)->map(
+ static fn (DOMNode $node) => append_external_node($schema, $node)
+ );
+ }
+
+ // Include tags can be removed, since the schema will be made available in the types
+ // using the namespace it defines. The include/redefine tag will have no purpose anymore.
+ remove($include);
+
+ return $schema;
+ }
+
+ /**
+ * @throws RuntimeException
+ * @throws UnloadableWsdlException
+ * @throws FlattenException
+ */
+ private function importSchema(DOMElement $import): ?DOMElement
+ {
+ // xsd:import tags don't require a location!
+ $location = $import->getAttribute('schemaLocation');
+ if (!$location) {
+ return null;
+ }
+
+ // Normally an import has an owner document, since it is coming from xpath on an existing document
+ // However, static analysis does not know about this.
+ if (!$import->ownerDocument) {
+ return null;
+ }
+
+ // Find the schema that wants to import the new schema:
+ $doc = Document::fromUnsafeDocument($import->ownerDocument);
+ $xpath = $doc->xpath(new WsdlPreset($doc));
+ /** @var DOMElement $schema */
+ $schema = $xpath->querySingle('ancestor::schema:schema', $import);
+
+ // Detect namespaces from both the current schema and the import
+ $namespace = $import->getAttribute('namespace');
+ $tns = $schema->getAttribute('targetNamespace');
+
+ // Imports can only deal with different namespaces.
+ // You'll need to use "include" if you want to inject something in the same namespace.
+ if ($tns && $namespace && $tns === $namespace) {
+ throw FlattenException::unableToImportXsd($location);
+ }
+
+ // After loading the schema, the import element needs to remain in the XSD.
+ // The schema location is removed, since it will be embedded in the WSDL.
+ $schema = $this->loadSchema($location);
+ $import->removeAttribute('schemaLocation');
+
+ return $schema;
+ }
+
+ /**
+ * @throws RuntimeException
+ * @throws UnloadableWsdlException
+ */
+ private function loadSchema(string $location): ?DOMElement
+ {
+ $path = IncludePathBuilder::build($location, $this->currentLocation);
+ $result = $this->context->import($path);
+
+ if (!$result) {
+ return null;
+ }
+
+ $imported = Document::fromXmlString($result);
+ /** @var DOMElement $schema */
+ $schema = $imported->xpath(new WsdlPreset($imported))->querySingle('/schema:schema');
+
+ return $schema;
+ }
+
+ /**
+ * This function registers the newly provided schema in the WSDL types section.
+ * It groups all imports by targetNamespace.
+ *
+ * @throws RuntimeException
+ * @throws AssertException
+ */
+ private function registerSchemaInTypes(DOMElement $schema): void
+ {
+ $wsdl = $this->context->wsdl();
+ $xpath = $wsdl->xpath(new WsdlPreset($wsdl));
+ $types = $this->context->types();
+ $tns = $schema->getAttribute('targetNamespace');
+
+ $query = $tns ? './schema:schema[@targetNamespace=\''.$tns.'\']' : './schema:schema[not(@targetNamespace)]';
+ $existingSchema = nullable(object(DOMElement::class))
+ ->assert($xpath->query($query, $types)->first());
+
+ // 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);
+ 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);
+ children($schema)->forEach(
+ static fn (DOMNode $node) => append_external_node($existingSchema, $node)
+ );
+ }
+}
diff --git a/src/Xml/Exception/FlattenException.php b/src/Xml/Exception/FlattenException.php
new file mode 100644
index 0000000..dab9ec5
--- /dev/null
+++ b/src/Xml/Exception/FlattenException.php
@@ -0,0 +1,22 @@
+toUnsafeDocument(),
+ pipe(
+ utf8(),
+ when(
+ static fn (DOMDocument $document): bool => $document->documentElement->localName === 'definitions',
+ pipe(
+ Closure::fromCallable(new FlattenWsdlImports($location, $context)),
+ Closure::fromCallable(new FlattenTypes()),
+ Closure::fromCallable(new FlattenXsdImports($location, $context)),
+ ),
+ Closure::fromCallable(new FlattenXsdImports($location, $context))
+ )
+ )
+ )->toXmlString();
+ }
+}
diff --git a/tests/Unit/Loader/FlatteningLoaderTest.php b/tests/Unit/Loader/FlatteningLoaderTest.php
new file mode 100644
index 0000000..65d120c
--- /dev/null
+++ b/tests/Unit/Loader/FlatteningLoaderTest.php
@@ -0,0 +1,103 @@
+loader = new FlatteningLoader(new StreamWrapperLoader());
+ }
+
+ /**
+ *
+ * @dataProvider provideTestCases
+ */
+ public function test_it_can_load_flattened_imports(string $wsdlUri, Document $expected): void
+ {
+ $result = ($this->loader)($wsdlUri);
+ $flattened = Document::fromXmlString($result, comparable());
+
+ static::assertSame($expected->toXmlString(), $flattened->toXmlString());
+ }
+
+ public function provideTestCases()
+ {
+ //
+ // Basic test cases
+ //
+ yield 'no-imports' => [
+ 'wsdl' => FIXTURE_DIR.'/flattening/functional/float.wsdl',
+ 'expected' => Document::fromXmlFile(FIXTURE_DIR.'/flattening/functional/float.wsdl', comparable()),
+ ];
+ yield 'circular-xsd' => [
+ 'wsdl' => FIXTURE_DIR.'/flattening/circular-xsd.wsdl',
+ 'expected' => Document::fromXmlFile(FIXTURE_DIR.'/flattening/result/circular-xsd-result.wsdl', comparable()),
+ ];
+ yield 'importing-circular-xsd' => [
+ 'wsdl' => FIXTURE_DIR.'/flattening/importing-circular-xsd.wsdl',
+ 'expected' => Document::fromXmlFile(FIXTURE_DIR.'/flattening/result/importing-circular-xsd-result.wsdl', comparable()),
+ ];
+
+ //
+ // XSDs Imports
+ //
+ yield 'single-xsd' => [
+ 'wsdl' => FIXTURE_DIR.'/flattening/single-xsd.wsdl',
+ 'expected' => Document::fromXmlFile(FIXTURE_DIR.'/flattening/result/single-xsd-result.wsdl', comparable()),
+ ];
+ yield 'import-once-xsd' => [
+ 'wsdl' => FIXTURE_DIR.'/flattening/once-xsd.wsdl',
+ 'expected' => Document::fromXmlFile(FIXTURE_DIR.'/flattening/result/once-xsd-result.wsdl', comparable()),
+ ];
+ yield 'import-multi-xsd' => [
+ 'wsdl' => FIXTURE_DIR.'/flattening/multi-xsd.wsdl',
+ 'expected' => Document::fromXmlFile(FIXTURE_DIR.'/flattening/result/multi-xsd-result.wsdl', comparable()),
+ ];
+
+ //
+ // XSD Includes
+ //
+ yield 'include-single' => [
+ 'wsdl' => FIXTURE_DIR.'/flattening/include.wsdl',
+ 'expected' => Document::fromXmlFile(FIXTURE_DIR.'/flattening/result/include.wsdl', comparable()),
+ ];
+ yield 'include-multi' => [
+ 'wsdl' => FIXTURE_DIR.'/flattening/multi-include.wsdl',
+ 'expected' => Document::fromXmlFile(FIXTURE_DIR.'/flattening/result/multi-include.wsdl', comparable()),
+ ];
+ yield 'include-nested' => [
+ 'wsdl' => FIXTURE_DIR.'/flattening/include-nested.wsdl',
+ 'expected' => Document::fromXmlFile(FIXTURE_DIR.'/flattening/result/include.wsdl', comparable()),
+ ];
+ yield 'imported-include-nested' => [
+ 'wsdl' => FIXTURE_DIR.'/flattening/include-nested.wsdl',
+ 'expected' => Document::fromXmlFile(FIXTURE_DIR.'/flattening/result/include.wsdl', comparable()),
+ ];
+ yield 'include-additional-namespaces' => [
+ 'wsdl' => FIXTURE_DIR.'/flattening/include-namespaces.wsdl',
+ 'expected' => Document::fromXmlFile(FIXTURE_DIR.'/flattening/result/include-namespaces.wsdl', comparable()),
+ ];
+
+ //
+ // WSDLs
+ //
+ yield 'only-wsdl-import' => [
+ 'wsdl' => FIXTURE_DIR.'/flattening/import.wsdl',
+ 'expected' => Document::fromXmlFile(FIXTURE_DIR.'/flattening/functional/float.wsdl', comparable()),
+ ];
+ yield 'import-wsdl-once' => [
+ 'wsdl' => FIXTURE_DIR.'/flattening/import.wsdl',
+ 'expected' => Document::fromXmlFile(FIXTURE_DIR.'/flattening/functional/float.wsdl', comparable()),
+ ];
+ }
+}
diff --git a/tests/Unit/Uri/IncludePathBuilderTest.php b/tests/Unit/Uri/IncludePathBuilderTest.php
new file mode 100644
index 0000000..ea73708
--- /dev/null
+++ b/tests/Unit/Uri/IncludePathBuilderTest.php
@@ -0,0 +1,44 @@
+ [
+ 'relativePath' => 'otherfile.xml',
+ 'fromFile' => 'somedir/somefile.xml',
+ 'expected' => 'somedir/otherfile.xml',
+ ];
+ yield 'child-dir-file' => [
+ 'relativePath' => '../otherfile.xml',
+ 'fromFile' => 'somedir/child/somefile.xml',
+ 'expected' => 'somedir/otherfile.xml',
+ ];
+ yield 'http-file' => [
+ 'relativePath' => 'otherfile.xml',
+ 'fromFile' => 'http://localhost/somedir/somefile.xml',
+ 'expected' => 'http://localhost/somedir/otherfile.xml',
+ ];
+ yield 'http-dir-file' => [
+ 'relativePath' => '../otherfile.xml',
+ 'fromFile' => 'http://localhost/somedir/child/somefile.xml',
+ 'expected' => 'http://localhost/somedir/otherfile.xml',
+ ];
+ }
+}
diff --git a/tests/Unit/Xml/Configurator/FlattenTypesTest.php b/tests/Unit/Xml/Configurator/FlattenTypesTest.php
new file mode 100644
index 0000000..ad420a4
--- /dev/null
+++ b/tests/Unit/Xml/Configurator/FlattenTypesTest.php
@@ -0,0 +1,39 @@
+toXmlString(), $wsdl->toXmlString());
+ }
+
+ public function provideTestCases()
+ {
+ yield 'single-type' => [
+ 'wsdl' => FIXTURE_DIR . '/flattening/functional/empty-schema.wsdl',
+ 'expected' => Document::fromXmlFile(FIXTURE_DIR . '/flattening/functional/empty-schema.wsdl', comparable()),
+ ];
+ yield 'no-type' => [
+ 'wsdl' => FIXTURE_DIR . '/flattening/functional/empty.wsdl',
+ 'expected' => Document::fromXmlFile(FIXTURE_DIR . '/flattening/result/empty-typed.wsdl', comparable()),
+ ];
+ yield 'multiple-types' => [
+ 'wsdl' => FIXTURE_DIR . '/flattening/result/import-with-own-tags-result.wsdl',
+ 'expected' => Document::fromXmlFile(FIXTURE_DIR . '/flattening/result/import-with-own-tags-single-type-result.wsdl', comparable()),
+ ];
+ }
+}
diff --git a/tests/Unit/Xml/Configurator/FlattenWsdlImportsTest.php b/tests/Unit/Xml/Configurator/FlattenWsdlImportsTest.php
new file mode 100644
index 0000000..a482ddb
--- /dev/null
+++ b/tests/Unit/Xml/Configurator/FlattenWsdlImportsTest.php
@@ -0,0 +1,50 @@
+toUnsafeDocument(), $configurator, comparable());
+
+ static::assertSame($expected->toXmlString(), $flattened->toXmlString());
+ }
+
+ public function provideTestCases()
+ {
+ yield 'only-import' => [
+ 'wsdl' => FIXTURE_DIR.'/flattening/import.wsdl',
+ 'expected' => Document::fromXmlFile(FIXTURE_DIR.'/flattening/functional/float.wsdl', comparable()),
+ ];
+ yield 'import-once' => [
+ 'wsdl' => FIXTURE_DIR.'/flattening/import.wsdl',
+ 'expected' => Document::fromXmlFile(FIXTURE_DIR.'/flattening/functional/float.wsdl', comparable()),
+ ];
+ yield 'with-own-tags' => [
+ 'wsdl' => FIXTURE_DIR.'/flattening/import-with-own-tags.wsdl',
+ 'expected' => Document::fromXmlFile(FIXTURE_DIR.'/flattening/result/import-with-own-tags-result.wsdl', comparable()),
+ ];
+ yield 'multi-imports' => [
+ 'wsdl' => FIXTURE_DIR.'/flattening/multi-import.wsdl',
+ 'expected' => Document::fromXmlFile(FIXTURE_DIR.'/flattening/result/multi-import-result.wsdl', comparable()),
+ ];
+ }
+}
diff --git a/tests/Unit/Xml/Configurator/FlattenXsdImportsTest.php b/tests/Unit/Xml/Configurator/FlattenXsdImportsTest.php
new file mode 100644
index 0000000..9d9ae90
--- /dev/null
+++ b/tests/Unit/Xml/Configurator/FlattenXsdImportsTest.php
@@ -0,0 +1,62 @@
+toUnsafeDocument(), $configurator, comparable());
+
+ static::assertSame($expected->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()),
+ ];
+ yield 'once-xsd' => [
+ 'wsdl' => FIXTURE_DIR.'/flattening/once-xsd.wsdl',
+ '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()),
+ ];
+ yield 'circular-xsd' => [
+ 'wsdl' => FIXTURE_DIR.'/flattening/circular-xsd.wsdl',
+ '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()),
+ ];
+ yield 'tnsless-xsd' => [
+ 'wsdl' => FIXTURE_DIR.'/flattening/tnsless-xsd.wsdl',
+ '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()),
+ ];
+ }
+}
diff --git a/tests/fixtures/flattening/circular-xsd.wsdl b/tests/fixtures/flattening/circular-xsd.wsdl
new file mode 100644
index 0000000..843b47b
--- /dev/null
+++ b/tests/fixtures/flattening/circular-xsd.wsdl
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
diff --git a/tests/fixtures/flattening/duplicate-typenames.wsdl b/tests/fixtures/flattening/duplicate-typenames.wsdl
new file mode 100644
index 0000000..b8c503c
--- /dev/null
+++ b/tests/fixtures/flattening/duplicate-typenames.wsdl
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/fixtures/flattening/functional/empty-schema.wsdl b/tests/fixtures/flattening/functional/empty-schema.wsdl
new file mode 100644
index 0000000..2218d08
--- /dev/null
+++ b/tests/fixtures/flattening/functional/empty-schema.wsdl
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
diff --git a/tests/fixtures/flattening/functional/empty.wsdl b/tests/fixtures/flattening/functional/empty.wsdl
new file mode 100644
index 0000000..0ade329
--- /dev/null
+++ b/tests/fixtures/flattening/functional/empty.wsdl
@@ -0,0 +1,9 @@
+
+
+
+
diff --git a/tests/fixtures/flattening/functional/float.wsdl b/tests/fixtures/flattening/functional/float.wsdl
new file mode 100644
index 0000000..113f995
--- /dev/null
+++ b/tests/fixtures/flattening/functional/float.wsdl
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/fixtures/flattening/grouped-xsd.wsdl b/tests/fixtures/flattening/grouped-xsd.wsdl
new file mode 100644
index 0000000..658a712
--- /dev/null
+++ b/tests/fixtures/flattening/grouped-xsd.wsdl
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
diff --git a/tests/fixtures/flattening/import-once.wsdl b/tests/fixtures/flattening/import-once.wsdl
new file mode 100644
index 0000000..273a09b
--- /dev/null
+++ b/tests/fixtures/flattening/import-once.wsdl
@@ -0,0 +1,9 @@
+
+
+
+
+
+
diff --git a/tests/fixtures/flattening/import-with-own-tags.wsdl b/tests/fixtures/flattening/import-with-own-tags.wsdl
new file mode 100644
index 0000000..20105cc
--- /dev/null
+++ b/tests/fixtures/flattening/import-with-own-tags.wsdl
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
diff --git a/tests/fixtures/flattening/import.wsdl b/tests/fixtures/flattening/import.wsdl
new file mode 100644
index 0000000..4ab3e1a
--- /dev/null
+++ b/tests/fixtures/flattening/import.wsdl
@@ -0,0 +1,7 @@
+
+
+
+
diff --git a/tests/fixtures/flattening/imported-include-nested.wsdl b/tests/fixtures/flattening/imported-include-nested.wsdl
new file mode 100644
index 0000000..f4ab712
--- /dev/null
+++ b/tests/fixtures/flattening/imported-include-nested.wsdl
@@ -0,0 +1,10 @@
+
+
+
+
+
+
diff --git a/tests/fixtures/flattening/importing-circular-xsd.wsdl b/tests/fixtures/flattening/importing-circular-xsd.wsdl
new file mode 100644
index 0000000..72156f5
--- /dev/null
+++ b/tests/fixtures/flattening/importing-circular-xsd.wsdl
@@ -0,0 +1,7 @@
+
+
+
+
diff --git a/tests/fixtures/flattening/include-namespaces.wsdl b/tests/fixtures/flattening/include-namespaces.wsdl
new file mode 100644
index 0000000..29c8188
--- /dev/null
+++ b/tests/fixtures/flattening/include-namespaces.wsdl
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
diff --git a/tests/fixtures/flattening/include-nested.wsdl b/tests/fixtures/flattening/include-nested.wsdl
new file mode 100644
index 0000000..b0a9559
--- /dev/null
+++ b/tests/fixtures/flattening/include-nested.wsdl
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
diff --git a/tests/fixtures/flattening/include.wsdl b/tests/fixtures/flattening/include.wsdl
new file mode 100644
index 0000000..97fe602
--- /dev/null
+++ b/tests/fixtures/flattening/include.wsdl
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
diff --git a/tests/fixtures/flattening/multi-import.wsdl b/tests/fixtures/flattening/multi-import.wsdl
new file mode 100644
index 0000000..9fc1f79
--- /dev/null
+++ b/tests/fixtures/flattening/multi-import.wsdl
@@ -0,0 +1,8 @@
+
+
+
+
+
diff --git a/tests/fixtures/flattening/multi-include.wsdl b/tests/fixtures/flattening/multi-include.wsdl
new file mode 100644
index 0000000..f3e5bf2
--- /dev/null
+++ b/tests/fixtures/flattening/multi-include.wsdl
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
diff --git a/tests/fixtures/flattening/multi-xsd.wsdl b/tests/fixtures/flattening/multi-xsd.wsdl
new file mode 100644
index 0000000..b887bc8
--- /dev/null
+++ b/tests/fixtures/flattening/multi-xsd.wsdl
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
diff --git a/tests/fixtures/flattening/once-xsd.wsdl b/tests/fixtures/flattening/once-xsd.wsdl
new file mode 100644
index 0000000..6c2b132
--- /dev/null
+++ b/tests/fixtures/flattening/once-xsd.wsdl
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
diff --git a/tests/fixtures/flattening/redefine-xsd.wsdl b/tests/fixtures/flattening/redefine-xsd.wsdl
new file mode 100644
index 0000000..5b4b3ab
--- /dev/null
+++ b/tests/fixtures/flattening/redefine-xsd.wsdl
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/fixtures/flattening/result/circular-xsd-result.wsdl b/tests/fixtures/flattening/result/circular-xsd-result.wsdl
new file mode 100644
index 0000000..00fddab
--- /dev/null
+++ b/tests/fixtures/flattening/result/circular-xsd-result.wsdl
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/fixtures/flattening/result/empty-typed.wsdl b/tests/fixtures/flattening/result/empty-typed.wsdl
new file mode 100644
index 0000000..b4250c4
--- /dev/null
+++ b/tests/fixtures/flattening/result/empty-typed.wsdl
@@ -0,0 +1,9 @@
+
+
+
+
diff --git a/tests/fixtures/flattening/result/grouped-xsd-result.wsdl b/tests/fixtures/flattening/result/grouped-xsd-result.wsdl
new file mode 100644
index 0000000..64a6e54
--- /dev/null
+++ b/tests/fixtures/flattening/result/grouped-xsd-result.wsdl
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/fixtures/flattening/result/import-with-own-tags-result.wsdl b/tests/fixtures/flattening/result/import-with-own-tags-result.wsdl
new file mode 100644
index 0000000..4b38fe4
--- /dev/null
+++ b/tests/fixtures/flattening/result/import-with-own-tags-result.wsdl
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/fixtures/flattening/result/import-with-own-tags-single-type-result.wsdl b/tests/fixtures/flattening/result/import-with-own-tags-single-type-result.wsdl
new file mode 100644
index 0000000..401220d
--- /dev/null
+++ b/tests/fixtures/flattening/result/import-with-own-tags-single-type-result.wsdl
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/fixtures/flattening/result/importing-circular-xsd-result.wsdl b/tests/fixtures/flattening/result/importing-circular-xsd-result.wsdl
new file mode 100644
index 0000000..00fddab
--- /dev/null
+++ b/tests/fixtures/flattening/result/importing-circular-xsd-result.wsdl
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/fixtures/flattening/result/include-namespaces.wsdl b/tests/fixtures/flattening/result/include-namespaces.wsdl
new file mode 100644
index 0000000..7d3cc20
--- /dev/null
+++ b/tests/fixtures/flattening/result/include-namespaces.wsdl
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/fixtures/flattening/result/include.wsdl b/tests/fixtures/flattening/result/include.wsdl
new file mode 100644
index 0000000..df3c73f
--- /dev/null
+++ b/tests/fixtures/flattening/result/include.wsdl
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/fixtures/flattening/result/multi-import-result.wsdl b/tests/fixtures/flattening/result/multi-import-result.wsdl
new file mode 100644
index 0000000..4b38fe4
--- /dev/null
+++ b/tests/fixtures/flattening/result/multi-import-result.wsdl
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/fixtures/flattening/result/multi-include.wsdl b/tests/fixtures/flattening/result/multi-include.wsdl
new file mode 100644
index 0000000..df3c73f
--- /dev/null
+++ b/tests/fixtures/flattening/result/multi-include.wsdl
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/fixtures/flattening/result/multi-xsd-result.wsdl b/tests/fixtures/flattening/result/multi-xsd-result.wsdl
new file mode 100644
index 0000000..14322b6
--- /dev/null
+++ b/tests/fixtures/flattening/result/multi-xsd-result.wsdl
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/fixtures/flattening/result/once-xsd-result.wsdl b/tests/fixtures/flattening/result/once-xsd-result.wsdl
new file mode 100644
index 0000000..bc7860c
--- /dev/null
+++ b/tests/fixtures/flattening/result/once-xsd-result.wsdl
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/fixtures/flattening/result/redefine-xsd-result.wsdl b/tests/fixtures/flattening/result/redefine-xsd-result.wsdl
new file mode 100644
index 0000000..088279d
--- /dev/null
+++ b/tests/fixtures/flattening/result/redefine-xsd-result.wsdl
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/fixtures/flattening/result/single-xsd-result.wsdl b/tests/fixtures/flattening/result/single-xsd-result.wsdl
new file mode 100644
index 0000000..f037567
--- /dev/null
+++ b/tests/fixtures/flattening/result/single-xsd-result.wsdl
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/fixtures/flattening/result/tnsless-xsd-result.wsdl b/tests/fixtures/flattening/result/tnsless-xsd-result.wsdl
new file mode 100644
index 0000000..d607663
--- /dev/null
+++ b/tests/fixtures/flattening/result/tnsless-xsd-result.wsdl
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/fixtures/flattening/single-xsd.wsdl b/tests/fixtures/flattening/single-xsd.wsdl
new file mode 100644
index 0000000..83fa3ec
--- /dev/null
+++ b/tests/fixtures/flattening/single-xsd.wsdl
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
diff --git a/tests/fixtures/flattening/tnsless-xsd.wsdl b/tests/fixtures/flattening/tnsless-xsd.wsdl
new file mode 100644
index 0000000..855ac06
--- /dev/null
+++ b/tests/fixtures/flattening/tnsless-xsd.wsdl
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
diff --git a/tests/fixtures/flattening/xsd/additional-namespaces.xsd b/tests/fixtures/flattening/xsd/additional-namespaces.xsd
new file mode 100644
index 0000000..747f5b6
--- /dev/null
+++ b/tests/fixtures/flattening/xsd/additional-namespaces.xsd
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/fixtures/flattening/xsd/circular-common.xsd b/tests/fixtures/flattening/xsd/circular-common.xsd
new file mode 100644
index 0000000..46ecb22
--- /dev/null
+++ b/tests/fixtures/flattening/xsd/circular-common.xsd
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/tests/fixtures/flattening/xsd/circular.xsd b/tests/fixtures/flattening/xsd/circular.xsd
new file mode 100644
index 0000000..ff07211
--- /dev/null
+++ b/tests/fixtures/flattening/xsd/circular.xsd
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/tests/fixtures/flattening/xsd/include.xsd b/tests/fixtures/flattening/xsd/include.xsd
new file mode 100644
index 0000000..8cadb95
--- /dev/null
+++ b/tests/fixtures/flattening/xsd/include.xsd
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/tests/fixtures/flattening/xsd/store1-extras.xsd b/tests/fixtures/flattening/xsd/store1-extras.xsd
new file mode 100644
index 0000000..490be3c
--- /dev/null
+++ b/tests/fixtures/flattening/xsd/store1-extras.xsd
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/fixtures/flattening/xsd/store1.xsd b/tests/fixtures/flattening/xsd/store1.xsd
new file mode 100644
index 0000000..247bfe1
--- /dev/null
+++ b/tests/fixtures/flattening/xsd/store1.xsd
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/fixtures/flattening/xsd/store2.xsd b/tests/fixtures/flattening/xsd/store2.xsd
new file mode 100644
index 0000000..61213e7
--- /dev/null
+++ b/tests/fixtures/flattening/xsd/store2.xsd
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/fixtures/flattening/xsd/tnsless.xsd b/tests/fixtures/flattening/xsd/tnsless.xsd
new file mode 100644
index 0000000..6c77960
--- /dev/null
+++ b/tests/fixtures/flattening/xsd/tnsless.xsd
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tools/psalm.phar b/tools/psalm.phar
index 0452e69..2d61da6 100755
Binary files a/tools/psalm.phar and b/tools/psalm.phar differ