diff --git a/.github/workflows/interoperability.yml b/.github/workflows/interoperability.yml
new file mode 100644
index 00000000..c0d376e3
--- /dev/null
+++ b/.github/workflows/interoperability.yml
@@ -0,0 +1,80 @@
+---
+
+name: Interoperability
+
+on: # yamllint disable-line rule:truthy
+ push:
+ branches: ['**']
+ paths-ignore:
+ - '**.md'
+ - '**.yml'
+ pull_request:
+ branches: [master, release-*]
+ paths-ignore:
+ - '**.md'
+ - '**.yml'
+ workflow_dispatch:
+
+jobs:
+ edugain:
+ name: "Interoperability tests, PHP ${{ matrix.php-versions }}, ${{ matrix.operating-system }}"
+ runs-on: ${{ matrix.operating-system }}
+ strategy:
+ fail-fast: false
+ matrix:
+ operating-system: [ubuntu-latest]
+ php-versions: ['8.2']
+
+ steps:
+ - name: Setup PHP, with composer and extensions
+ # https://github.com/shivammathur/setup-php
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: ${{ matrix.php-versions }}
+ extensions: ctype, date, dom, hash, mbstring, openssl, pcre, spl, xml
+ tools: composer:v2
+ ini-values: error_reporting=E_ALL, memory_limit=-1
+ coverage: none
+
+ - name: Setup problem matchers for PHP
+ run: echo "::add-matcher::${{ runner.tool_cache }}/php.json"
+
+ - name: Setup problem matchers for PHPUnit
+ run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"
+
+ - name: Set git to use LF
+ run: |
+ git config --global core.autocrlf false
+ git config --global core.eol lf
+
+ - uses: actions/checkout@v4
+
+ - name: Cache composer dependencies
+ uses: actions/cache@v4
+ with:
+ path: $(composer config cache-files-dir)
+ key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
+ restore-keys: ${{ runner.os }}-composer-
+
+ - name: Validate composer.json and composer.lock
+ run: composer validate
+
+ - name: Install Composer dependencies
+ run: composer install --no-progress --prefer-dist --optimize-autoloader
+
+ - name: Get current date
+ id: date
+ run: |
+ echo "{date}={$(date +'%Y-%m-%d')}" >> "$GITHUB_STATE"
+
+ - name: Cache metadata
+ id: cache-metadata
+ uses: actions/cache@v4
+ with:
+ path: /tmp/metadata
+ key: ${{ runner.os }}-metadata-${{ env.date }}
+ restore-keys: ${{ runner.os }}-metadata-
+
+ - name: Run unit tests
+ run: |
+ ./vendor/bin/phpunit -c phpunit-interoperability.xml
diff --git a/phpunit-interoperability.xml b/phpunit-interoperability.xml
new file mode 100644
index 00000000..ebfec763
--- /dev/null
+++ b/phpunit-interoperability.xml
@@ -0,0 +1,8 @@
+
+
+
+
+ ./tests/InterOperability
+
+
+
diff --git a/src/XML/wsdl/AbstractBinding.php b/src/XML/wsdl/AbstractBinding.php
new file mode 100644
index 00000000..77769832
--- /dev/null
+++ b/src/XML/wsdl/AbstractBinding.php
@@ -0,0 +1,104 @@
+name;
+ }
+
+
+ /**
+ * Collect the value of the type-property.
+ *
+ * @return string
+ */
+ public function getType(): string
+ {
+ return $this->type;
+ }
+
+
+ /**
+ * Collect the value of the operation-property.
+ *
+ * @return \SimpleSAML\WSSecurity\XML\wsdl\BindingOperation[]
+ */
+ public function getOperation(): array
+ {
+ return $this->operation;
+ }
+
+
+ /**
+ * Test if an object, at the state it's in, would produce an empty XML-element
+ *
+ * @return bool
+ */
+ public function isEmptyElement(): bool
+ {
+ // Upstream abstract elements can be empty, but this one cannot
+ return false;
+ }
+
+
+ /**
+ * Convert this tBinding to XML.
+ *
+ * @param \DOMElement|null $parent The element we are converting to XML.
+ * @return \DOMElement The XML element after adding the data corresponding to this tBinding.
+ */
+ public function toXML(DOMElement $parent = null): DOMElement
+ {
+ $e = parent::toXML($parent);
+
+ $e->setAttribute('name', $this->getName());
+ $e->setAttribute('type', $this->getType());
+
+ foreach ($this->getOperation() as $operation) {
+ $operation->toXML($e);
+ }
+
+ return $e;
+ }
+}
diff --git a/src/XML/wsdl/AbstractBindingOperation.php b/src/XML/wsdl/AbstractBindingOperation.php
new file mode 100644
index 00000000..8ede5a28
--- /dev/null
+++ b/src/XML/wsdl/AbstractBindingOperation.php
@@ -0,0 +1,118 @@
+name;
+ }
+
+
+ /**
+ * Collect the value of the input-property.
+ *
+ * @return \SimpleSAML\WSSecurity\XML\wsdl\BindingOperationInput|null
+ */
+ public function getInput(): ?BindingOperationInput
+ {
+ return $this->input;
+ }
+
+
+ /**
+ * Collect the value of the output-property.
+ *
+ * @return \SimpleSAML\WSSecurity\XML\wsdl\BindingOperationOutput|null
+ */
+ public function getOutput(): ?BindingOperationOutput
+ {
+ return $this->output;
+ }
+
+
+ /**
+ * Collect the value of the fault-property.
+ *
+ * @return \SimpleSAML\WSSecurity\XML\wsdl\BindingOperationFault[]
+ */
+ public function getFault(): array
+ {
+ return $this->fault;
+ }
+
+
+ /**
+ * Test if an object, at the state it's in, would produce an empty XML-element
+ *
+ * @return bool
+ */
+ public function isEmptyElement(): bool
+ {
+ // Upstream abstract elements can be empty, but this one cannot
+ return false;
+ }
+
+
+ /**
+ * Convert this tBindingOperation to XML.
+ *
+ * @param \DOMElement|null $parent The element we are converting to XML.
+ * @return \DOMElement The XML element after adding the data corresponding to this tBindingOperation.
+ */
+ public function toXML(DOMElement $parent = null): DOMElement
+ {
+ $e = parent::toXML($parent);
+
+ $e->setAttribute('name', $this->getName());
+
+ $this->getInput()?->toXML($e);
+ $this->getOutput()?->toXML($e);
+
+ foreach ($this->getFault() as $fault) {
+ $fault->toXML($e);
+ }
+
+ return $e;
+ }
+}
diff --git a/src/XML/wsdl/AbstractBindingOperationFault.php b/src/XML/wsdl/AbstractBindingOperationFault.php
new file mode 100644
index 00000000..dc26d3b6
--- /dev/null
+++ b/src/XML/wsdl/AbstractBindingOperationFault.php
@@ -0,0 +1,69 @@
+name;
+ }
+
+
+ /**
+ * Test if an object, at the state it's in, would produce an empty XML-element
+ *
+ * @return bool
+ */
+ public function isEmptyElement(): bool
+ {
+ // Upstream abstract elements can be empty, but this one cannot
+ return false;
+ }
+
+
+ /**
+ * Convert this tBindingOperationFault to XML.
+ *
+ * @param \DOMElement|null $parent The element we are converting to XML.
+ * @return \DOMElement The XML element after adding the data corresponding to this tBindingOperationFault.
+ */
+ public function toXML(DOMElement $parent = null): DOMElement
+ {
+ $e = parent::toXML($parent);
+ $e->setAttribute('name', $this->getName());
+
+ return $e;
+ }
+}
diff --git a/src/XML/wsdl/AbstractBindingOperationMessage.php b/src/XML/wsdl/AbstractBindingOperationMessage.php
new file mode 100644
index 00000000..d2fbd6e9
--- /dev/null
+++ b/src/XML/wsdl/AbstractBindingOperationMessage.php
@@ -0,0 +1,71 @@
+name;
+ }
+
+
+ /**
+ * Test if an object, at the state it's in, would produce an empty XML-element
+ *
+ * @return bool
+ */
+ public function isEmptyElement(): bool
+ {
+ return parent::isEmptyElement() && empty($this->getName());
+ }
+
+
+ /**
+ * Convert this tBindingOperationMessage to XML.
+ *
+ * @param \DOMElement|null $parent The element we are converting to XML.
+ * @return \DOMElement The XML element after adding the data corresponding to this tBindingOperationMessage.
+ */
+ public function toXML(DOMElement $parent = null): DOMElement
+ {
+ $e = parent::toXML($parent);
+
+ if ($this->getName() !== null) {
+ $e->setAttribute('name', $this->getName());
+ }
+
+ return $e;
+ }
+}
diff --git a/src/XML/wsdl/AbstractDefinitions.php b/src/XML/wsdl/AbstractDefinitions.php
new file mode 100644
index 00000000..0a6753a0
--- /dev/null
+++ b/src/XML/wsdl/AbstractDefinitions.php
@@ -0,0 +1,269 @@
+getName();
+ },
+ $message,
+ );
+ Assert::uniqueValues(
+ $messageNames,
+ "Message-elements must have unique names.",
+ SchemaViolationException::class,
+ );
+
+ $portTypeNames = array_map(
+ function ($x) {
+ return $x->getName();
+ },
+ $portType,
+ );
+ Assert::uniqueValues(
+ $portTypeNames,
+ "PortType-elements must have unique names.",
+ SchemaViolationException::class,
+ );
+
+ $bindingNames = array_map(
+ function ($x) {
+ return $x->getName();
+ },
+ $binding,
+ );
+ Assert::uniqueValues(
+ $bindingNames,
+ "Binding-elements must have unique names.",
+ SchemaViolationException::class,
+ );
+
+ $serviceNames = array_map(
+ function ($x) {
+ return $x->getName();
+ },
+ $service,
+ );
+ Assert::uniqueValues(
+ $serviceNames,
+ "Service-elements must have unique names.",
+ SchemaViolationException::class,
+ );
+
+ $importNamespaces = array_map(
+ function ($x) {
+ return $x->getNamespace();
+ },
+ $import,
+ );
+ Assert::uniqueValues(
+ $importNamespaces,
+ "Import-elements must have unique namespaces.",
+ SchemaViolationException::class,
+ );
+
+ parent::__construct($elements);
+ }
+
+
+ /**
+ * Collect the value of the name-property.
+ *
+ * @return string|null
+ */
+ public function getName(): ?string
+ {
+ return $this->name;
+ }
+
+
+ /**
+ * Collect the value of the targetNamespace-property.
+ *
+ * @return string|null
+ */
+ public function getTargetNamespace(): ?string
+ {
+ return $this->targetNamespace;
+ }
+
+
+ /**
+ * Collect the value of the import-property.
+ *
+ * @return \SimpleSAML\WSSecurity\XML\wsdl\Import[]
+ */
+ public function getImport(): array
+ {
+ return $this->import;
+ }
+
+
+ /**
+ * Collect the value of the typrd-property.
+ *
+ * @return \SimpleSAML\WSSecurity\XML\wsdl\Types[]
+ */
+ public function getTypes(): array
+ {
+ return $this->types;
+ }
+
+
+ /**
+ * Collect the value of the message-property.
+ *
+ * @return \SimpleSAML\WSSecurity\XML\wsdl\Message[]
+ */
+ public function getMessage(): array
+ {
+ return $this->message;
+ }
+
+
+ /**
+ * Collect the value of the portType-property.
+ *
+ * @return \SimpleSAML\WSSecurity\XML\wsdl\PortType[]
+ */
+ public function getPortType(): array
+ {
+ return $this->portType;
+ }
+
+
+ /**
+ * Collect the value of the binding-property.
+ *
+ * @return \SimpleSAML\WSSecurity\XML\wsdl\Binding[]
+ */
+ public function getBinding(): array
+ {
+ return $this->binding;
+ }
+
+
+ /**
+ * Collect the value of the service-property.
+ *
+ * @return \SimpleSAML\WSSecurity\XML\wsdl\Service[]
+ */
+ public function getService(): array
+ {
+ return $this->service;
+ }
+
+
+ /**
+ * Test if an object, at the state it's in, would produce an empty XML-element
+ *
+ * @return bool
+ */
+ public function isEmptyElement(): bool
+ {
+ return parent::isEmptyElement() &&
+ empty($this->getName()) &&
+ empty($this->getTargetNamespace()) &&
+ empty($this->getImport()) &&
+ empty($this->getTypes()) &&
+ empty($this->getMessage()) &&
+ empty($this->getPortType()) &&
+ empty($this->getBinding()) &&
+ empty($this->getService());
+ }
+
+
+ /**
+ * Convert this tDefinitions to XML.
+ *
+ * @param \DOMElement|null $parent The element we are converting to XML.
+ * @return \DOMElement The XML element after adding the data corresponding to this tDefinitions.
+ */
+ public function toXML(DOMElement $parent = null): DOMElement
+ {
+ $e = parent::toXML($parent);
+
+ if ($this->getTargetNamespace() !== null) {
+ $e->setAttribute('targetNamespace', $this->getTargetNamespace());
+ }
+
+ if ($this->getName() !== null) {
+ $e->setAttribute('name', $this->getName());
+ }
+
+ foreach ($this->getImport() as $import) {
+ $import->toXML($e);
+ }
+
+ foreach ($this->getTypes() as $types) {
+ $types->toXML($e);
+ }
+
+ foreach ($this->getMessage() as $message) {
+ $message->toXML($e);
+ }
+
+ foreach ($this->getPortType() as $portType) {
+ $portType->toXML($e);
+ }
+
+ foreach ($this->getBinding() as $binding) {
+ $binding->toXML($e);
+ }
+
+ foreach ($this->getService() as $service) {
+ $service->toXML($e);
+ }
+
+ return $e;
+ }
+}
diff --git a/src/XML/wsdl/AbstractDocumented.php b/src/XML/wsdl/AbstractDocumented.php
new file mode 100644
index 00000000..b17eb111
--- /dev/null
+++ b/src/XML/wsdl/AbstractDocumented.php
@@ -0,0 +1,63 @@
+documentation;
+ }
+ */
+
+
+ /**
+ * Test if an object, at the state it's in, would produce an empty XML-element
+ *
+ * @return bool
+ */
+ public function isEmptyElement(): bool
+ {
+ return /*empty($this->documentation)*/ true;
+ }
+
+
+ /**
+ * Convert this tDocumented to XML.
+ *
+ * @param \DOMElement|null $parent The element we are converting to XML.
+ * @return \DOMElement The XML element after adding the data corresponding to this tDocumented.
+ */
+ public function toXML(DOMElement $parent = null): DOMElement
+ {
+ $e = $this->instantiateParentElement($parent);
+
+ //$this->getDocumentation()?->toXML($e);
+
+ return $e;
+ }
+}
diff --git a/src/XML/wsdl/AbstractExtensibilityElement.php b/src/XML/wsdl/AbstractExtensibilityElement.php
new file mode 100644
index 00000000..b82c3f60
--- /dev/null
+++ b/src/XML/wsdl/AbstractExtensibilityElement.php
@@ -0,0 +1,54 @@
+required;
+ }
+
+
+ /**
+ * Convert this tExtensibilityElement to XML.
+ *
+ * @param \DOMElement|null $parent The element we are converting to XML.
+ * @return \DOMElement The XML element after adding the data corresponding to this tExtensibilityElement
+ */
+ public function toXML(DOMElement $parent = null): DOMElement
+ {
+ $e = $this->instantiateParentElement($parent);
+
+ if ($this->getRequired() !== null) {
+ $e->setAttribute('required', $this->getRequired() ? 'true' : 'false');
+ }
+
+ return $e;
+ }
+}
diff --git a/src/XML/wsdl/AbstractExtensibleAttributesDocumented.php b/src/XML/wsdl/AbstractExtensibleAttributesDocumented.php
new file mode 100644
index 00000000..a1bcd889
--- /dev/null
+++ b/src/XML/wsdl/AbstractExtensibleAttributesDocumented.php
@@ -0,0 +1,63 @@
+setAttributesNS($attributes);
+ }
+
+
+ /**
+ * Test if an object, at the state it's in, would produce an empty XML-element
+ *
+ * @return bool
+ */
+ public function isEmptyElement(): bool
+ {
+ return parent::isEmptyElement() && empty($this->getAttributesNS());
+ }
+
+
+ /**
+ * Convert this tExtensibleAttributesDocumented to XML.
+ *
+ * @param \DOMElement|null $parent The element we are converting to XML.
+ * @return \DOMElement The XML element after adding the data corresponding to this tExtensibleAttributesDocumented.
+ */
+ public function toXML(DOMElement $parent = null): DOMElement
+ {
+ $e = parent::toXML($parent);
+
+ foreach ($this->getAttributesNS() as $attr) {
+ $attr->toXML($e);
+ }
+
+ return $e;
+ }
+}
diff --git a/src/XML/wsdl/AbstractExtensibleDocumented.php b/src/XML/wsdl/AbstractExtensibleDocumented.php
new file mode 100644
index 00000000..1b26801a
--- /dev/null
+++ b/src/XML/wsdl/AbstractExtensibleDocumented.php
@@ -0,0 +1,62 @@
+setElements($elements);
+ }
+
+
+ /**
+ * Test if an object, at the state it's in, would produce an empty XML-element
+ *
+ * @return bool
+ */
+ public function isEmptyElement(): bool
+ {
+ return parent::isEmptyElement() && empty($this->elements);
+ }
+
+
+ /**
+ * Convert this tExtensibleDocumented to XML.
+ *
+ * @param \DOMElement|null $parent The element we are converting to XML.
+ * @return \DOMElement The XML element after adding the data corresponding to this tExtensibleDocumented.
+ */
+ public function toXML(DOMElement $parent = null): DOMElement
+ {
+ $e = parent::toXML($parent);
+
+ foreach ($this->getElements() as $elt) {
+ $elt->toXML($e);
+ }
+
+ return $e;
+ }
+}
diff --git a/src/XML/wsdl/AbstractFault.php b/src/XML/wsdl/AbstractFault.php
new file mode 100644
index 00000000..0c3feee2
--- /dev/null
+++ b/src/XML/wsdl/AbstractFault.php
@@ -0,0 +1,86 @@
+name;
+ }
+
+
+ /**
+ * Collect the value of the message-property.
+ *
+ * @return string
+ */
+ public function getMessage(): string
+ {
+ return $this->message;
+ }
+
+
+ /**
+ * Test if an object, at the state it's in, would produce an empty XML-element
+ *
+ * @return bool
+ */
+ public function isEmptyElement(): bool
+ {
+ // Upstream abstract elements can be empty, but this one cannot
+ return false;
+ }
+
+
+ /**
+ * Convert this tParam to XML.
+ *
+ * @param \DOMElement|null $parent The element we are converting to XML.
+ * @return \DOMElement The XML element after adding the data corresponding to this tParam.
+ */
+ public function toXML(DOMElement $parent = null): DOMElement
+ {
+ $e = parent::toXML($parent);
+
+ $e->setAttribute('name', $this->getName());
+ $e->setAttribute('message', $this->getMessage());
+
+ return $e;
+ }
+}
diff --git a/src/XML/wsdl/AbstractImport.php b/src/XML/wsdl/AbstractImport.php
new file mode 100644
index 00000000..56f0b71b
--- /dev/null
+++ b/src/XML/wsdl/AbstractImport.php
@@ -0,0 +1,86 @@
+ $attributes
+ */
+ public function __construct(
+ protected string $namespace,
+ protected string $location,
+ array $attributes = [],
+ ) {
+ Assert::validURI($namespace, SchemaViolationException::class);
+ Assert::validURI($location, SchemaViolationException::class);
+
+ parent::__construct($attributes);
+ }
+
+
+ /**
+ * Collect the value of the namespace-property.
+ *
+ * @return string
+ */
+ public function getNamespace(): string
+ {
+ return $this->namespace;
+ }
+
+
+ /**
+ * Collect the value of the location-property.
+ *
+ * @return string
+ */
+ public function getLocation(): string
+ {
+ return $this->location;
+ }
+
+
+ /**
+ * Test if an object, at the state it's in, would produce an empty XML-element
+ *
+ * @return bool
+ */
+ public function isEmptyElement(): bool
+ {
+ // Upstream abstract elements can be empty, but this one cannot
+ return false;
+ }
+
+
+ /**
+ * Convert this tImport to XML.
+ *
+ * @param \DOMElement|null $parent The element we are converting to XML.
+ * @return \DOMElement The XML element after adding the data corresponding to this tImport.
+ */
+ public function toXML(DOMElement $parent = null): DOMElement
+ {
+ $e = parent::toXML($parent);
+
+ $e->setAttribute('namespace', $this->getNamespace());
+ $e->setAttribute('location', $this->getLocation());
+
+ return $e;
+ }
+}
diff --git a/src/XML/wsdl/AbstractMessage.php b/src/XML/wsdl/AbstractMessage.php
new file mode 100644
index 00000000..87af88e8
--- /dev/null
+++ b/src/XML/wsdl/AbstractMessage.php
@@ -0,0 +1,98 @@
+getName();
+ },
+ $parts,
+ );
+ Assert::uniqueValues($partNames, "Part-elements must have unique names.", SchemaViolationException::class);
+
+ parent::__construct($elements);
+ }
+
+
+ /**
+ * Collect the value of the name-property.
+ *
+ * @return string
+ */
+ public function getName(): string
+ {
+ return $this->name;
+ }
+
+
+ /**
+ * Collect the value of the parts-property.
+ *
+ * @return \SimpleSAML\WSSecurity\XML\wsdl\Part[]
+ */
+ public function getParts(): array
+ {
+ return $this->parts;
+ }
+
+
+ /**
+ * Test if an object, at the state it's in, would produce an empty XML-element
+ *
+ * @return bool
+ */
+ public function isEmptyElement(): bool
+ {
+ // Upstream abstract elements can be empty, but this one cannot
+ return false;
+ }
+
+
+ /**
+ * Convert this tParam to XML.
+ *
+ * @param \DOMElement|null $parent The element we are converting to XML.
+ * @return \DOMElement The XML element after adding the data corresponding to this tParam.
+ */
+ public function toXML(DOMElement $parent = null): DOMElement
+ {
+ $e = parent::toXML($parent);
+ $e->setAttribute('name', $this->getName());
+
+ foreach ($this->getParts() as $part) {
+ $part->toXML($e);
+ }
+
+ return $e;
+ }
+}
diff --git a/src/XML/wsdl/AbstractParam.php b/src/XML/wsdl/AbstractParam.php
new file mode 100644
index 00000000..90e4f3e7
--- /dev/null
+++ b/src/XML/wsdl/AbstractParam.php
@@ -0,0 +1,89 @@
+name;
+ }
+
+
+ /**
+ * Collect the value of the message-property.
+ *
+ * @return string
+ */
+ public function getMessage(): string
+ {
+ return $this->message;
+ }
+
+
+ /**
+ * Test if an object, at the state it's in, would produce an empty XML-element
+ *
+ * @return bool
+ */
+ public function isEmptyElement(): bool
+ {
+ // Upstream abstract elements can be empty, but this one cannot
+ return false;
+ }
+
+
+ /**
+ * Convert this tParam to XML.
+ *
+ * @param \DOMElement|null $parent The element we are converting to XML.
+ * @return \DOMElement The XML element after adding the data corresponding to this tParam.
+ */
+ public function toXML(DOMElement $parent = null): DOMElement
+ {
+ $e = parent::toXML($parent);
+
+ if ($this->getName() !== null) {
+ $e->setAttribute('name', $this->getName());
+ }
+
+ $e->setAttribute('message', $this->getMessage());
+
+ return $e;
+ }
+}
diff --git a/src/XML/wsdl/AbstractPart.php b/src/XML/wsdl/AbstractPart.php
new file mode 100644
index 00000000..b2e9c73a
--- /dev/null
+++ b/src/XML/wsdl/AbstractPart.php
@@ -0,0 +1,107 @@
+name;
+ }
+
+
+ /**
+ * Collect the value of the element-property.
+ *
+ * @return string|null
+ */
+ public function getElement(): ?string
+ {
+ return $this->element;
+ }
+
+
+ /**
+ * Collect the value of the type-property.
+ *
+ * @return string|null
+ */
+ public function getType(): ?string
+ {
+ return $this->type;
+ }
+
+
+ /**
+ * Test if an object, at the state it's in, would produce an empty XML-element
+ *
+ * @return bool
+ */
+ public function isEmptyElement(): bool
+ {
+ // Upstream abstract elements can be empty, but this one cannot
+ return false;
+ }
+
+
+ /**
+ * Convert this tPart to XML.
+ *
+ * @param \DOMElement|null $parent The element we are converting to XML.
+ * @return \DOMElement The XML element after adding the data corresponding to this tPart.
+ */
+ public function toXML(DOMElement $parent = null): DOMElement
+ {
+ $e = parent::toXML($parent);
+
+ $e->setAttribute('name', $this->getName());
+
+ if ($this->getElement() !== null) {
+ $e->setAttribute('element', $this->getElement());
+ }
+
+ if ($this->getType() !== null) {
+ $e->setAttribute('type', $this->getType());
+ }
+
+ return $e;
+ }
+}
diff --git a/src/XML/wsdl/AbstractPort.php b/src/XML/wsdl/AbstractPort.php
new file mode 100644
index 00000000..9de13bea
--- /dev/null
+++ b/src/XML/wsdl/AbstractPort.php
@@ -0,0 +1,86 @@
+name;
+ }
+
+
+ /**
+ * Collect the value of the binding-property.
+ *
+ * @return string
+ */
+ public function getBinding(): string
+ {
+ return $this->binding;
+ }
+
+
+ /**
+ * Test if an object, at the state it's in, would produce an empty XML-element
+ *
+ * @return bool
+ */
+ public function isEmptyElement(): bool
+ {
+ // Upstream abstract elements can be empty, but this one cannot
+ return false;
+ }
+
+
+ /**
+ * Convert this tPort to XML.
+ *
+ * @param \DOMElement|null $parent The element we are converting to XML.
+ * @return \DOMElement The XML element after adding the data corresponding to this tPort.
+ */
+ public function toXML(DOMElement $parent = null): DOMElement
+ {
+ $e = parent::toXML($parent);
+
+ $e->setAttribute('name', $this->getName());
+ $e->setAttribute('binding', $this->getBinding());
+
+ return $e;
+ }
+}
diff --git a/src/XML/wsdl/AbstractPortType.php b/src/XML/wsdl/AbstractPortType.php
new file mode 100644
index 00000000..4651c3bd
--- /dev/null
+++ b/src/XML/wsdl/AbstractPortType.php
@@ -0,0 +1,89 @@
+name;
+ }
+
+
+ /**
+ * Collect the value of the operation-property.
+ *
+ * @return \SimpleSAML\WSSecurity\XML\wsdl\PortTypeOperation[]
+ */
+ public function getOperation(): array
+ {
+ return $this->operation;
+ }
+
+
+ /**
+ * Test if an object, at the state it's in, would produce an empty XML-element
+ *
+ * @return bool
+ */
+ public function isEmptyElement(): bool
+ {
+ // Upstream abstract elements can be empty, but this one cannot
+ return false;
+ }
+
+
+ /**
+ * Convert this tPortType to XML.
+ *
+ * @param \DOMElement|null $parent The element we are converting to XML.
+ * @return \DOMElement The XML element after adding the data corresponding to this tPortType.
+ */
+ public function toXML(DOMElement $parent = null): DOMElement
+ {
+ $e = parent::toXML($parent);
+
+ $e->setAttribute('name', $this->getName());
+
+ foreach ($this->getOperation() as $operation) {
+ $operation->toXML($e);
+ }
+
+ return $e;
+ }
+}
diff --git a/src/XML/wsdl/AbstractPortTypeOperation.php b/src/XML/wsdl/AbstractPortTypeOperation.php
new file mode 100644
index 00000000..f443700a
--- /dev/null
+++ b/src/XML/wsdl/AbstractPortTypeOperation.php
@@ -0,0 +1,132 @@
+name;
+ }
+
+
+ /**
+ * Collect the value of the parameterOrder-property
+ *
+ * @return string|null
+ */
+ public function getParameterOrder(): ?string
+ {
+ return $this->parameterOrder;
+ }
+
+
+ /**
+ * Collect the value of the input-property
+ *
+ * @return \SimpleSAML\WSSecurity\XML\wsdl\AbstractParam|null
+ */
+ public function getInput(): ?AbstractParam
+ {
+ return $this->input;
+ }
+
+
+ /**
+ * Collect the value of the output-property
+ *
+ * @return \SimpleSAML\WSSecurity\XML\wsdl\AbstractParam|null
+ */
+ public function getOutput(): ?AbstractParam
+ {
+ return $this->output;
+ }
+
+
+ /**
+ * Collect the value of the fault-property
+ *
+ * @return \SimpleSAML\WSSecurity\XML\wsdl\Fault[]
+ */
+ public function getFault(): array
+ {
+ return $this->fault;
+ }
+
+
+ /**
+ * Convert this RequestResponseOrOneWayOperation to XML.
+ *
+ * @param \DOMElement|null $parent The element we should add this organization to.
+ * @return \DOMElement This Organization-element.
+ */
+ public function toXML(DOMElement $parent = null): DOMElement
+ {
+ $e = $this->instantiateParentElement($parent);
+ $e->setAttribute('name', $this->getName());
+
+ if ($this->getParameterOrder() !== null) {
+ $e->setAttribute('parameterOrder', $this->getParameterOrder());
+ }
+
+ $this->getInput()?->toXML($e);
+ $this->getOutput()?->toXML($e);
+
+ foreach ($this->getFault() as $fault) {
+ $fault->toXML($e);
+ }
+
+ return $e;
+ }
+}
diff --git a/src/XML/wsdl/AbstractService.php b/src/XML/wsdl/AbstractService.php
new file mode 100644
index 00000000..d65cc6ab
--- /dev/null
+++ b/src/XML/wsdl/AbstractService.php
@@ -0,0 +1,99 @@
+getName();
+ },
+ $ports,
+ );
+ Assert::uniqueValues($portNames, "Port-elements must have unique names.", SchemaViolationException::class);
+
+ parent::__construct($elements);
+ }
+
+
+ /**
+ * Collect the value of the name-property.
+ *
+ * @return string
+ */
+ public function getName(): string
+ {
+ return $this->name;
+ }
+
+
+ /**
+ * Collect the value of the ports-property.
+ *
+ * @return \SimpleSAML\WSSecurity\XML\wsdl\Port[]
+ */
+ public function getPorts(): array
+ {
+ return $this->ports;
+ }
+
+
+ /**
+ * Test if an object, at the state it's in, would produce an empty XML-element
+ *
+ * @return bool
+ */
+ public function isEmptyElement(): bool
+ {
+ // Upstream abstract elements can be empty, but this one cannot
+ return false;
+ }
+
+
+ /**
+ * Convert this tService to XML.
+ *
+ * @param \DOMElement|null $parent The element we are converting to XML.
+ * @return \DOMElement The XML element after adding the data corresponding to this tService.
+ */
+ public function toXML(DOMElement $parent = null): DOMElement
+ {
+ $e = parent::toXML($parent);
+
+ $e->setAttribute('name', $this->getName());
+
+ foreach ($this->getPorts() as $port) {
+ $port->toXML($e);
+ }
+
+ return $e;
+ }
+}
diff --git a/src/XML/wsdl/AbstractTypes.php b/src/XML/wsdl/AbstractTypes.php
new file mode 100644
index 00000000..694ed419
--- /dev/null
+++ b/src/XML/wsdl/AbstractTypes.php
@@ -0,0 +1,24 @@
+localName, static::LOCALNAME, InvalidDOMElementException::class);
+ Assert::same($xml->namespaceURI, static::NS, InvalidDOMElementException::class);
+
+ $operation = BindingOperation::getChildrenOfClass($xml);
+
+ $children = [];
+ foreach ($xml->childNodes as $child) {
+ if (!($child instanceof DOMElement)) {
+ continue;
+ } elseif ($child->namespaceURI === static::NS) {
+ // Only other namespaces are allowed
+ continue;
+ }
+
+ $children[] = new Chunk($child);
+ }
+
+ return new static(
+ self::getAttribute($xml, 'name'),
+ self::getAttribute($xml, 'type'),
+ $operation,
+ $children,
+ );
+ }
+}
diff --git a/src/XML/wsdl/BindingOperation.php b/src/XML/wsdl/BindingOperation.php
new file mode 100644
index 00000000..bce4b44f
--- /dev/null
+++ b/src/XML/wsdl/BindingOperation.php
@@ -0,0 +1,63 @@
+localName, static::LOCALNAME, InvalidDOMElementException::class);
+ Assert::same($xml->namespaceURI, static::NS, InvalidDOMElementException::class);
+
+ $input = BindingOperationInput::getChildrenOfClass($xml);
+ $output = BindingOperationOutput::getChildrenOfClass($xml);
+ $faults = BindingOperationFault::getChildrenOfClass($xml);
+
+ $children = [];
+ foreach ($xml->childNodes as $child) {
+ if (!($child instanceof DOMElement)) {
+ continue;
+ } elseif ($child->namespaceURI === static::NS) {
+ // Only other namespaces are allowed
+ continue;
+ }
+
+ $children[] = new Chunk($child);
+ }
+
+ return new static(
+ self::getAttribute($xml, 'name'),
+ array_pop($input),
+ array_pop($output),
+ $faults,
+ $children,
+ );
+ }
+}
diff --git a/src/XML/wsdl/BindingOperationFault.php b/src/XML/wsdl/BindingOperationFault.php
new file mode 100644
index 00000000..73dc2208
--- /dev/null
+++ b/src/XML/wsdl/BindingOperationFault.php
@@ -0,0 +1,54 @@
+localName, static::LOCALNAME, InvalidDOMElementException::class);
+ Assert::same($xml->namespaceURI, static::NS, InvalidDOMElementException::class);
+
+ $children = [];
+ foreach ($xml->childNodes as $child) {
+ if (!($child instanceof DOMElement)) {
+ continue;
+ } elseif ($child->namespaceURI === static::NS) {
+ // Only other namespaces are allowed
+ continue;
+ }
+
+ $children[] = new Chunk($child);
+ }
+
+ return new static(
+ self::getAttribute($xml, 'name'),
+ $children,
+ );
+ }
+}
diff --git a/src/XML/wsdl/BindingOperationInput.php b/src/XML/wsdl/BindingOperationInput.php
new file mode 100644
index 00000000..d4182d19
--- /dev/null
+++ b/src/XML/wsdl/BindingOperationInput.php
@@ -0,0 +1,54 @@
+localName, static::LOCALNAME, InvalidDOMElementException::class);
+ Assert::same($xml->namespaceURI, static::NS, InvalidDOMElementException::class);
+
+ $children = [];
+ foreach ($xml->childNodes as $child) {
+ if (!($child instanceof DOMElement)) {
+ continue;
+ } elseif ($child->namespaceURI === static::NS) {
+ // Only other namespaces are allowed
+ continue;
+ }
+
+ $children[] = new Chunk($child);
+ }
+
+ return new static(
+ self::getOptionalAttribute($xml, 'name', null),
+ $children,
+ );
+ }
+}
diff --git a/src/XML/wsdl/BindingOperationOutput.php b/src/XML/wsdl/BindingOperationOutput.php
new file mode 100644
index 00000000..a55e2458
--- /dev/null
+++ b/src/XML/wsdl/BindingOperationOutput.php
@@ -0,0 +1,54 @@
+localName, static::LOCALNAME, InvalidDOMElementException::class);
+ Assert::same($xml->namespaceURI, static::NS, InvalidDOMElementException::class);
+
+ $children = [];
+ foreach ($xml->childNodes as $child) {
+ if (!($child instanceof DOMElement)) {
+ continue;
+ } elseif ($child->namespaceURI === static::NS) {
+ // Only other namespaces are allowed
+ continue;
+ }
+
+ $children[] = new Chunk($child);
+ }
+
+ return new static(
+ self::getOptionalAttribute($xml, 'name', null),
+ $children,
+ );
+ }
+}
diff --git a/src/XML/wsdl/Definitions.php b/src/XML/wsdl/Definitions.php
new file mode 100644
index 00000000..a011e3d9
--- /dev/null
+++ b/src/XML/wsdl/Definitions.php
@@ -0,0 +1,68 @@
+localName, static::LOCALNAME, InvalidDOMElementException::class);
+ Assert::same($xml->namespaceURI, static::NS, InvalidDOMElementException::class);
+
+ $import = Import::getChildrenOfClass($xml);
+ $types = Types::getChildrenOfClass($xml);
+ $message = Message::getChildrenOfClass($xml);
+ $portType = PortType::getChildrenOfClass($xml);
+ $binding = Binding::getChildrenOfClass($xml);
+ $service = Service::getChildrenOfClass($xml);
+
+ $children = [];
+ foreach ($xml->childNodes as $child) {
+ if (!($child instanceof DOMElement)) {
+ continue;
+ } elseif ($child->namespaceURI === static::NS) {
+ // Only other namespaces are allowed
+ continue;
+ }
+
+ $children[] = new Chunk($child);
+ }
+
+ return new static(
+ self::getOptionalAttribute($xml, 'targetNamespace'),
+ self::getOptionalAttribute($xml, 'name'),
+ $import,
+ $types,
+ $message,
+ $portType,
+ $binding,
+ $service,
+ $children,
+ );
+ }
+}
diff --git a/src/XML/wsdl/Fault.php b/src/XML/wsdl/Fault.php
new file mode 100644
index 00000000..ca0b5085
--- /dev/null
+++ b/src/XML/wsdl/Fault.php
@@ -0,0 +1,42 @@
+localName, static::LOCALNAME, InvalidDOMElementException::class);
+ Assert::same($xml->namespaceURI, static::NS, InvalidDOMElementException::class);
+
+ return new static(
+ self::getAttribute($xml, 'name'),
+ self::getAttribute($xml, 'message'),
+ self::getAttributesNSFromXML($xml),
+ );
+ }
+}
diff --git a/src/XML/wsdl/Import.php b/src/XML/wsdl/Import.php
new file mode 100644
index 00000000..20520a45
--- /dev/null
+++ b/src/XML/wsdl/Import.php
@@ -0,0 +1,42 @@
+localName, static::LOCALNAME, InvalidDOMElementException::class);
+ Assert::same($xml->namespaceURI, static::NS, InvalidDOMElementException::class);
+
+ return new static(
+ self::getAttribute($xml, 'namespace'),
+ self::getAttribute($xml, 'location'),
+ self::getAttributesNSFromXML($xml),
+ );
+ }
+}
diff --git a/src/XML/wsdl/Input.php b/src/XML/wsdl/Input.php
new file mode 100644
index 00000000..f136ac30
--- /dev/null
+++ b/src/XML/wsdl/Input.php
@@ -0,0 +1,42 @@
+localName, static::LOCALNAME, InvalidDOMElementException::class);
+ Assert::same($xml->namespaceURI, static::NS, InvalidDOMElementException::class);
+
+ return new static(
+ self::getAttribute($xml, 'message'),
+ self::getOptionalAttribute($xml, 'name'),
+ self::getAttributesNSFromXML($xml),
+ );
+ }
+}
diff --git a/src/XML/wsdl/Message.php b/src/XML/wsdl/Message.php
new file mode 100644
index 00000000..a4a620d0
--- /dev/null
+++ b/src/XML/wsdl/Message.php
@@ -0,0 +1,55 @@
+localName, static::LOCALNAME, InvalidDOMElementException::class);
+ Assert::same($xml->namespaceURI, static::NS, InvalidDOMElementException::class);
+
+ $children = [];
+ foreach ($xml->childNodes as $child) {
+ if (!($child instanceof DOMElement)) {
+ continue;
+ } elseif ($child->namespaceURI === static::NS) {
+ // Only other namespaces are allowed
+ continue;
+ }
+
+ $children[] = new Chunk($child);
+ }
+
+ return new static(
+ self::getAttribute($xml, 'name'),
+ Part::getChildrenOfClass($xml),
+ $children,
+ );
+ }
+}
diff --git a/src/XML/wsdl/Output.php b/src/XML/wsdl/Output.php
new file mode 100644
index 00000000..02c3f8c9
--- /dev/null
+++ b/src/XML/wsdl/Output.php
@@ -0,0 +1,42 @@
+localName, static::LOCALNAME, InvalidDOMElementException::class);
+ Assert::same($xml->namespaceURI, static::NS, InvalidDOMElementException::class);
+
+ return new static(
+ self::getAttribute($xml, 'message'),
+ self::getOptionalAttribute($xml, 'name'),
+ self::getAttributesNSFromXML($xml),
+ );
+ }
+}
diff --git a/src/XML/wsdl/Part.php b/src/XML/wsdl/Part.php
new file mode 100644
index 00000000..33821e95
--- /dev/null
+++ b/src/XML/wsdl/Part.php
@@ -0,0 +1,43 @@
+localName, static::LOCALNAME, InvalidDOMElementException::class);
+ Assert::same($xml->namespaceURI, static::NS, InvalidDOMElementException::class);
+
+ return new static(
+ self::getAttribute($xml, 'name'),
+ self::getOptionalAttribute($xml, 'element'),
+ self::getOptionalAttribute($xml, 'type'),
+ self::getAttributesNSFromXML($xml),
+ );
+ }
+}
diff --git a/src/XML/wsdl/Port.php b/src/XML/wsdl/Port.php
new file mode 100644
index 00000000..037e56b9
--- /dev/null
+++ b/src/XML/wsdl/Port.php
@@ -0,0 +1,55 @@
+localName, static::LOCALNAME, InvalidDOMElementException::class);
+ Assert::same($xml->namespaceURI, static::NS, InvalidDOMElementException::class);
+
+ $children = [];
+ foreach ($xml->childNodes as $child) {
+ if (!($child instanceof DOMElement)) {
+ continue;
+ } elseif ($child->namespaceURI === static::NS) {
+ // Only other namespaces are allowed
+ continue;
+ }
+
+ $children[] = new Chunk($child);
+ }
+
+ return new static(
+ self::getAttribute($xml, 'name'),
+ self::getAttribute($xml, 'binding'),
+ $children,
+ );
+ }
+}
diff --git a/src/XML/wsdl/PortType.php b/src/XML/wsdl/PortType.php
new file mode 100644
index 00000000..4b48d39a
--- /dev/null
+++ b/src/XML/wsdl/PortType.php
@@ -0,0 +1,42 @@
+localName, static::LOCALNAME, InvalidDOMElementException::class);
+ Assert::same($xml->namespaceURI, static::NS, InvalidDOMElementException::class);
+
+ return new static(
+ self::getAttribute($xml, 'name'),
+ PortTypeOperation::getChildrenOfClass($xml),
+ self::getAttributesNSFromXML($xml),
+ );
+ }
+}
diff --git a/src/XML/wsdl/PortTypeOperation.php b/src/XML/wsdl/PortTypeOperation.php
new file mode 100644
index 00000000..d5d6db49
--- /dev/null
+++ b/src/XML/wsdl/PortTypeOperation.php
@@ -0,0 +1,93 @@
+localName, static::LOCALNAME, InvalidDOMElementException::class);
+ Assert::same($xml->namespaceURI, static::NS, InvalidDOMElementException::class);
+
+ $first = null;
+ foreach ($xml->childNodes as $element) {
+ if (!($element instanceof DOMElement)) {
+ continue;
+ } elseif ($element->namespaceURI === static::NS) {
+ if ($element->localName === 'input') {
+ $first = Input::class;
+ break;
+ } elseif ($element->localName === 'output') {
+ $first = Output::class;
+ break;
+ }
+ }
+ }
+
+ Assert::notNull($first, SchemaViolationException::class);
+
+ if ($first === Input::class) {
+ // xs:group solicit-response-or-notification-operation
+ $input = Input::getChildrenOfClass($xml);
+ $input = array_pop($input);
+ Assert::notNull($input, SchemaViolationException::class);
+ $output = Output::getChildrenOfClass($xml);
+ $output = array_pop($output);
+ } else {
+ // xs:group request-response-or-one-way-operation
+ // NOTE: input is really output and vice versa!!
+ $input = Output::getChildrenOfClass($xml);
+ $input = array_pop($input);
+ Assert::notNull($input, SchemaViolationException::class);
+ $output = Input::getChildrenOfClass($xml);
+ $output = array_pop($output);
+ }
+
+ $elements = [];
+ foreach ($xml->childNodes as $element) {
+ if (!($element instanceof DOMElement)) {
+ continue;
+ } elseif ($element->namespaceURI === static::NS) {
+ // Only other namespaces are allowed
+ continue;
+ }
+
+ $elements[] = new Chunk($element);
+ }
+
+ return new static(
+ self::getAttribute($xml, 'name'),
+ self::getOptionalAttribute($xml, 'parameterOrder'),
+ $input,
+ $output,
+ Fault::getChildrenOfClass($xml),
+ $elements,
+ );
+ }
+}
diff --git a/src/XML/wsdl/Service.php b/src/XML/wsdl/Service.php
new file mode 100644
index 00000000..c0fe82bd
--- /dev/null
+++ b/src/XML/wsdl/Service.php
@@ -0,0 +1,55 @@
+localName, static::LOCALNAME, InvalidDOMElementException::class);
+ Assert::same($xml->namespaceURI, static::NS, InvalidDOMElementException::class);
+
+ $children = [];
+ foreach ($xml->childNodes as $child) {
+ if (!($child instanceof DOMElement)) {
+ continue;
+ } elseif ($child->namespaceURI === static::NS) {
+ // Only other namespaces are allowed
+ continue;
+ }
+
+ $children[] = new Chunk($child);
+ }
+
+ return new static(
+ self::getAttribute($xml, 'name'),
+ Port::getChildrenOfClass($xml),
+ $children,
+ );
+ }
+}
diff --git a/src/XML/wsdl/Types.php b/src/XML/wsdl/Types.php
new file mode 100644
index 00000000..71703334
--- /dev/null
+++ b/src/XML/wsdl/Types.php
@@ -0,0 +1,53 @@
+localName, static::LOCALNAME, InvalidDOMElementException::class);
+ Assert::same($xml->namespaceURI, static::NS, InvalidDOMElementException::class);
+
+ $children = [];
+ foreach ($xml->childNodes as $child) {
+ if (!($child instanceof DOMElement)) {
+ continue;
+ } elseif ($child->namespaceURI === static::NS) {
+ // Only other namespaces are allowed
+ continue;
+ }
+
+ $children[] = new Chunk($child);
+ }
+
+ return new static(
+ $children,
+ );
+ }
+}
diff --git a/tests/InterOperability/DefinitionsTest.php b/tests/InterOperability/DefinitionsTest.php
new file mode 100644
index 00000000..a7a511fb
--- /dev/null
+++ b/tests/InterOperability/DefinitionsTest.php
@@ -0,0 +1,53 @@
+assertTrue($shouldPass);
+ } catch (AssertionFailedException $e) {
+ fwrite(STDERR, $e->getFile() . '(' . strval($e->getLine()) . '):' . $e->getMessage());
+ fwrite(STDERR, $e->getTraceAsString());
+ $this->assertFalse($shouldPass);
+ }
+ }
+
+
+ /**
+ * @return array
+ */
+ public static function provideMex(): array
+ {
+ return [
+ 'MicrosoftAdfs' => [
+ true,
+ DOMDocumentFactory::fromFile(
+ dirname(__FILE__, 2) . '/resources/interoperability/adfs_mex.xml',
+ )->documentElement,
+ ],
+ ];
+ }
+}
diff --git a/tests/WSSecurity/XML/wsdl/BindingOperationTest.php b/tests/WSSecurity/XML/wsdl/BindingOperationTest.php
new file mode 100644
index 00000000..5009e106
--- /dev/null
+++ b/tests/WSSecurity/XML/wsdl/BindingOperationTest.php
@@ -0,0 +1,95 @@
+SomeChunk',
+ );
+ $inputChild = DOMDocumentFactory::fromString(
+ 'InputChunk',
+ );
+ $outputChild = DOMDocumentFactory::fromString(
+ 'OutputChunk',
+ );
+ $faultOneChild = DOMDocumentFactory::fromString(
+ 'FaultOneChunk',
+ );
+ $faultTwoChild = DOMDocumentFactory::fromString(
+ 'FaultTwoChunk',
+ );
+
+ $input = new BindingOperationInput('CustomInputName', [new Chunk($inputChild->documentElement)]);
+ $output = new BindingOperationOutput('CustomOutputName', [new Chunk($outputChild->documentElement)]);
+ $faultOne = new BindingOperationFault('CustomFaultOne', [new Chunk($faultOneChild->documentElement)]);
+ $faultTwo = new BindingOperationFault('CustomFaultTwo', [new Chunk($faultTwoChild->documentElement)]);
+
+ $bindingOperation = new BindingOperation(
+ 'SomeName',
+ $input,
+ $output,
+ [$faultOne, $faultTwo],
+ [new Chunk($child->documentElement)],
+ );
+
+ $this->assertEquals(
+ self::$xmlRepresentation->saveXML(self::$xmlRepresentation->documentElement),
+ strval($bindingOperation),
+ );
+ }
+}
diff --git a/tests/WSSecurity/XML/wsdl/BindingTest.php b/tests/WSSecurity/XML/wsdl/BindingTest.php
new file mode 100644
index 00000000..03517156
--- /dev/null
+++ b/tests/WSSecurity/XML/wsdl/BindingTest.php
@@ -0,0 +1,107 @@
+SomeChunk',
+ );
+ $operationChild = DOMDocumentFactory::fromString(
+ 'OperationChunk',
+ );
+ $inputChild = DOMDocumentFactory::fromString(
+ 'InputChunk',
+ );
+ $outputChild = DOMDocumentFactory::fromString(
+ 'OutputChunk',
+ );
+ $faultOneChild = DOMDocumentFactory::fromString(
+ 'FaultOneChunk',
+ );
+ $faultTwoChild = DOMDocumentFactory::fromString(
+ 'FaultTwoChunk',
+ );
+
+ $input = new BindingOperationInput('CustomInputName', [new Chunk($inputChild->documentElement)]);
+ $output = new BindingOperationOutput('CustomOutputName', [new Chunk($outputChild->documentElement)]);
+ $faultOne = new BindingOperationFault('CustomFaultOne', [new Chunk($faultOneChild->documentElement)]);
+ $faultTwo = new BindingOperationFault('CustomFaultTwo', [new Chunk($faultTwoChild->documentElement)]);
+
+ $operationOne = new BindingOperation(
+ 'OperationOne',
+ $input,
+ $output,
+ [$faultOne, $faultTwo],
+ [new Chunk($operationChild->documentElement)],
+ );
+ $operationTwo = new BindingOperation('OperationTwo');
+
+ $binding = new Binding(
+ 'MyBinding',
+ 'ssp:CustomType',
+ [$operationOne, $operationTwo],
+ [new Chunk($child->documentElement)],
+ );
+
+ $this->assertEquals(
+ self::$xmlRepresentation->saveXML(self::$xmlRepresentation->documentElement),
+ strval($binding),
+ );
+ }
+}
diff --git a/tests/WSSecurity/XML/wsdl/DefinitionsTest.php b/tests/WSSecurity/XML/wsdl/DefinitionsTest.php
new file mode 100644
index 00000000..9e4f0664
--- /dev/null
+++ b/tests/WSSecurity/XML/wsdl/DefinitionsTest.php
@@ -0,0 +1,192 @@
+TypesChunk',
+ );
+
+ $types = new Types([new Chunk($typesChild->documentElement)]);
+
+ // Message
+ $messageChild = DOMDocumentFactory::fromString(
+ 'MessageChunk',
+ );
+
+ $messageAttr1 = new XMLAttribute(C::NAMESPACE, 'ssp', 'attr1', 'value1');
+ $messageAttr2 = new XMLAttribute(C::NAMESPACE, 'ssp', 'attr2', 'value2');
+ $part1 = new Part('CustomName', 'ssp:CustomElement', 'wsdl:part', [$messageAttr1]);
+
+ $message = new Message('SomeName', [$part1], [new Chunk($messageChild->documentElement)]);
+
+ // PortType
+ $port = new XMLAttribute(C::NAMESPACE, 'ssp', 'port', '1234');
+ $portAttr1 = new XMLAttribute(C::NAMESPACE, 'ssp', 'attr1', 'value1');
+ $portAttr2 = new XMLAttribute(C::NAMESPACE, 'ssp', 'attr2', 'value2');
+ $portAttr3 = new XMLAttribute(C::NAMESPACE, 'ssp', 'attr3', 'value3');
+
+ $input = new Input('ssp:CustomInputMessage', 'CustomInputName', [$portAttr1]);
+ $output = new Output('ssp:CustomOutputMessage', 'CustomOutputName', [$portAttr2]);
+ $fault = new Fault('CustomFaultName', 'ssp:CustomFaultMessage', [$portAttr3]);
+
+ $inputOperation = new Operation('Input', '0836217462 0836217463', $input, $output, [$fault]);
+
+ $portType = new PortType('MyPort', [$inputOperation], [$port]);
+
+ // Binding
+ $bindingChild = DOMDocumentFactory::fromString(
+ 'BindingChunk',
+ );
+ $operationChild = DOMDocumentFactory::fromString(
+ 'OperationChunk',
+ );
+ $inputChild = DOMDocumentFactory::fromString(
+ 'InputChunk',
+ );
+ $outputChild = DOMDocumentFactory::fromString(
+ 'OutputChunk',
+ );
+ $faultOneChild = DOMDocumentFactory::fromString(
+ 'FaultOneChunk',
+ );
+ $faultTwoChild = DOMDocumentFactory::fromString(
+ 'FaultTwoChunk',
+ );
+
+ $input = new BindingOperationInput('CustomInputName', [new Chunk($inputChild->documentElement)]);
+ $output = new BindingOperationOutput('CustomOutputName', [new Chunk($outputChild->documentElement)]);
+ $faultOne = new BindingOperationFault('CustomFaultOne', [new Chunk($faultOneChild->documentElement)]);
+
+ $operationOne = new BindingOperation(
+ 'OperationOne',
+ $input,
+ $output,
+ [$faultOne],
+ [new Chunk($operationChild->documentElement)],
+ );
+
+ $binding = new Binding(
+ 'MyBinding',
+ 'wsdl:binding',
+ [$operationOne],
+ [new Chunk($bindingChild->documentElement)],
+ );
+
+ // Service
+ $serviceChild = DOMDocumentFactory::fromString(
+ 'ServiceChunk',
+ );
+ $chunkOne = DOMDocumentFactory::fromString(
+ 'ChunkOne',
+ );
+ $chunkTwo = DOMDocumentFactory::fromString(
+ 'ChunkTwo',
+ );
+
+ $portOne = new Port('PortOne', 'wsdl:binding', [new Chunk($chunkOne->documentElement)]);
+
+ $service = new Service('MyService', [$portOne], [new Chunk($serviceChild->documentElement)]);
+
+ // Child
+ $child = DOMDocumentFactory::fromString(
+ 'SomeChunk',
+ );
+
+ $definitions = new Definitions(
+ 'urn:x-simplesamlphp:namespace',
+ 'MyDefinitions',
+ [$import],
+ [$types],
+ [$message],
+ [$portType],
+ [$binding],
+ [$service],
+ [new Chunk($child->documentElement)],
+ );
+
+ $this->assertEquals(
+ self::$xmlRepresentation->saveXML(self::$xmlRepresentation->documentElement),
+ strval($definitions),
+ );
+ }
+}
diff --git a/tests/WSSecurity/XML/wsdl/FaultTest.php b/tests/WSSecurity/XML/wsdl/FaultTest.php
new file mode 100644
index 00000000..860b5f6f
--- /dev/null
+++ b/tests/WSSecurity/XML/wsdl/FaultTest.php
@@ -0,0 +1,65 @@
+assertEquals(
+ self::$xmlRepresentation->saveXML(self::$xmlRepresentation->documentElement),
+ strval($fault),
+ );
+ }
+}
diff --git a/tests/WSSecurity/XML/wsdl/ImportTest.php b/tests/WSSecurity/XML/wsdl/ImportTest.php
new file mode 100644
index 00000000..b665002c
--- /dev/null
+++ b/tests/WSSecurity/XML/wsdl/ImportTest.php
@@ -0,0 +1,65 @@
+assertEquals(
+ self::$xmlRepresentation->saveXML(self::$xmlRepresentation->documentElement),
+ strval($import),
+ );
+ }
+}
diff --git a/tests/WSSecurity/XML/wsdl/InputTest.php b/tests/WSSecurity/XML/wsdl/InputTest.php
new file mode 100644
index 00000000..e93dd8df
--- /dev/null
+++ b/tests/WSSecurity/XML/wsdl/InputTest.php
@@ -0,0 +1,67 @@
+assertEquals(
+ self::$xmlRepresentation->saveXML(self::$xmlRepresentation->documentElement),
+ strval($input),
+ );
+ }
+}
diff --git a/tests/WSSecurity/XML/wsdl/MessageTest.php b/tests/WSSecurity/XML/wsdl/MessageTest.php
new file mode 100644
index 00000000..6db278a6
--- /dev/null
+++ b/tests/WSSecurity/XML/wsdl/MessageTest.php
@@ -0,0 +1,89 @@
+SomeChunk',
+ );
+
+ $attr1 = new XMLAttribute(C::NAMESPACE, 'ssp', 'attr1', 'value1');
+ $attr2 = new XMLAttribute(C::NAMESPACE, 'ssp', 'attr2', 'value2');
+ $part1 = new Part('CustomName', 'ssp:CustomElement', 'ssp:CustomType', [$attr1]);
+ $part2 = new Part('CustomOtherName', 'ssp:CustomElement', 'ssp:CustomType', [$attr2]);
+
+ $message = new Message('SomeName', [$part1, $part2], [new Chunk($child->documentElement)]);
+
+ $this->assertEquals(
+ self::$xmlRepresentation->saveXML(self::$xmlRepresentation->documentElement),
+ strval($message),
+ );
+ }
+
+
+ /**
+ * Test creating an Message object with multiple parts with the same name fails.
+ */
+ public function testMarshallingMultiplePartsSameName(): void
+ {
+ $part1 = new Part('CustomSameName', 'ssp:CustomElement', 'ssp:CustomType');
+ $part2 = new Part('CustomSameName', 'ssp:CustomElement', 'ssp:CustomType');
+
+ $this->expectException(SchemaViolationException::class);
+ new Message('SomeName', [$part1, $part2]);
+ }
+}
diff --git a/tests/WSSecurity/XML/wsdl/OutputTest.php b/tests/WSSecurity/XML/wsdl/OutputTest.php
new file mode 100644
index 00000000..47fe19ef
--- /dev/null
+++ b/tests/WSSecurity/XML/wsdl/OutputTest.php
@@ -0,0 +1,67 @@
+assertEquals(
+ self::$xmlRepresentation->saveXML(self::$xmlRepresentation->documentElement),
+ strval($Output),
+ );
+ }
+}
diff --git a/tests/WSSecurity/XML/wsdl/PartTest.php b/tests/WSSecurity/XML/wsdl/PartTest.php
new file mode 100644
index 00000000..89c1a480
--- /dev/null
+++ b/tests/WSSecurity/XML/wsdl/PartTest.php
@@ -0,0 +1,65 @@
+assertEquals(
+ self::$xmlRepresentation->saveXML(self::$xmlRepresentation->documentElement),
+ strval($part),
+ );
+ }
+}
diff --git a/tests/WSSecurity/XML/wsdl/PortTest.php b/tests/WSSecurity/XML/wsdl/PortTest.php
new file mode 100644
index 00000000..5b0e0951
--- /dev/null
+++ b/tests/WSSecurity/XML/wsdl/PortTest.php
@@ -0,0 +1,66 @@
+SomeChunk',
+ );
+ $port = new Port('CustomName', 'ssp:CustomBinding', [new Chunk($child->documentElement)]);
+
+ $this->assertEquals(
+ self::$xmlRepresentation->saveXML(self::$xmlRepresentation->documentElement),
+ strval($port),
+ );
+ }
+}
diff --git a/tests/WSSecurity/XML/wsdl/PortTypeOperationInputTest.php b/tests/WSSecurity/XML/wsdl/PortTypeOperationInputTest.php
new file mode 100644
index 00000000..92efc240
--- /dev/null
+++ b/tests/WSSecurity/XML/wsdl/PortTypeOperationInputTest.php
@@ -0,0 +1,77 @@
+assertEquals(
+ self::$xmlRepresentation->saveXML(self::$xmlRepresentation->documentElement),
+ strval($operation),
+ );
+ }
+}
diff --git a/tests/WSSecurity/XML/wsdl/PortTypeOperationOutputTest.php b/tests/WSSecurity/XML/wsdl/PortTypeOperationOutputTest.php
new file mode 100644
index 00000000..460ef785
--- /dev/null
+++ b/tests/WSSecurity/XML/wsdl/PortTypeOperationOutputTest.php
@@ -0,0 +1,77 @@
+assertEquals(
+ self::$xmlRepresentation->saveXML(self::$xmlRepresentation->documentElement),
+ strval($operation),
+ );
+ }
+}
diff --git a/tests/WSSecurity/XML/wsdl/PortTypeTest.php b/tests/WSSecurity/XML/wsdl/PortTypeTest.php
new file mode 100644
index 00000000..5655aa77
--- /dev/null
+++ b/tests/WSSecurity/XML/wsdl/PortTypeTest.php
@@ -0,0 +1,82 @@
+assertEquals(
+ self::$xmlRepresentation->saveXML(self::$xmlRepresentation->documentElement),
+ strval($portType),
+ );
+ }
+}
diff --git a/tests/WSSecurity/XML/wsdl/ServiceTest.php b/tests/WSSecurity/XML/wsdl/ServiceTest.php
new file mode 100644
index 00000000..0909d004
--- /dev/null
+++ b/tests/WSSecurity/XML/wsdl/ServiceTest.php
@@ -0,0 +1,77 @@
+SomeChunk',
+ );
+ $chunkOne = DOMDocumentFactory::fromString(
+ 'ChunkOne',
+ );
+ $chunkTwo = DOMDocumentFactory::fromString(
+ 'ChunkTwo',
+ );
+
+ $portOne = new Port('PortOne', 'ssp:CustomBinding', [new Chunk($chunkOne->documentElement)]);
+ $portTwo = new Port('PortTwo', 'ssp:CustomBinding', [new Chunk($chunkTwo->documentElement)]);
+
+ $service = new Service('MyService', [$portOne, $portTwo], [new Chunk($child->documentElement)]);
+
+ $this->assertEquals(
+ self::$xmlRepresentation->saveXML(self::$xmlRepresentation->documentElement),
+ strval($service),
+ );
+ }
+}
diff --git a/tests/WSSecurity/XML/wsdl/TypesTest.php b/tests/WSSecurity/XML/wsdl/TypesTest.php
new file mode 100644
index 00000000..efef7b49
--- /dev/null
+++ b/tests/WSSecurity/XML/wsdl/TypesTest.php
@@ -0,0 +1,67 @@
+SomeChunk',
+ );
+
+ $types = new Types([new Chunk($child->documentElement)]);
+
+ $this->assertEquals(
+ self::$xmlRepresentation->saveXML(self::$xmlRepresentation->documentElement),
+ strval($types),
+ );
+ }
+}
diff --git a/tests/resources/interoperability/adfs_mex.xml b/tests/resources/interoperability/adfs_mex.xml
new file mode 100644
index 00000000..425e90f8
--- /dev/null
+++ b/tests/resources/interoperability/adfs_mex.xml
@@ -0,0 +1 @@
+http://schemas.xmlsoap.org/ws/2005/02/trust/PublicKeyhttp://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1phttp://www.w3.org/2000/09/xmldsig#rsa-sha1http://www.w3.org/2001/10/xml-exc-c14n#http://www.w3.org/2001/04/xmlenc#aes256-cbchttp://schemas.xmlsoap.org/ws/2005/02/trust/SymmetricKey256http://www.w3.org/2001/04/xmlenc#aes256-cbchttp://www.w3.org/2000/09/xmldsig#hmac-sha1http://www.w3.org/2001/10/xml-exc-c14n#http://www.w3.org/2001/04/xmlenc#aes256-cbchttp://docs.oasis-open.org/ws-sx/ws-trust/200512/PublicKeyhttp://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1phttp://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1phttp://www.w3.org/2000/09/xmldsig#rsa-sha1http://www.w3.org/2001/10/xml-exc-c14n#http://www.w3.org/2001/04/xmlenc#aes256-cbchttp://docs.oasis-open.org/ws-sx/ws-trust/200512/SymmetricKey256http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1phttp://www.w3.org/2001/04/xmlenc#aes256-cbchttp://www.w3.org/2000/09/xmldsig#hmac-sha1http://www.w3.org/2001/10/xml-exc-c14n#http://www.w3.org/2001/04/xmlenc#aes256-cbchttps://adfs.example.org/adfs/services/trust/2005/certificatemixedhttps://certauth.adfs.example.org/adfs/services/trust/2005/certificatetransporthttps://adfs.example.org/adfs/services/trust/2005/usernamemixedhttps://adfs.example.org/adfs/services/trust/2005/issuedtokenmixedasymmetricbasic256https://adfs.example.org/adfs/services/trust/2005/issuedtokenmixedsymmetricbasic256https://adfs.example.org/adfs/services/trust/13/certificatemixedhttps://adfs.example.org/adfs/services/trust/13/usernamemixedhttps://adfs.example.org/adfs/services/trust/13/issuedtokenmixedasymmetricbasic256https://adfs.example.org/adfs/services/trust/13/issuedtokenmixedsymmetricbasic256
diff --git a/tests/resources/xml/wsdl_Binding.xml b/tests/resources/xml/wsdl_Binding.xml
new file mode 100644
index 00000000..c23ad288
--- /dev/null
+++ b/tests/resources/xml/wsdl_Binding.xml
@@ -0,0 +1,19 @@
+
+ SomeChunk
+
+ OperationChunk
+
+ InputChunk
+
+
+ OutputChunk
+
+
+ FaultOneChunk
+
+
+ FaultTwoChunk
+
+
+
+
diff --git a/tests/resources/xml/wsdl_BindingOperation.xml b/tests/resources/xml/wsdl_BindingOperation.xml
new file mode 100644
index 00000000..9fe87f56
--- /dev/null
+++ b/tests/resources/xml/wsdl_BindingOperation.xml
@@ -0,0 +1,15 @@
+
+ SomeChunk
+
+ InputChunk
+
+
+ OutputChunk
+
+
+ FaultOneChunk
+
+
+ FaultTwoChunk
+
+
diff --git a/tests/resources/xml/wsdl_Definitions.xml b/tests/resources/xml/wsdl_Definitions.xml
new file mode 100644
index 00000000..3a997ccf
--- /dev/null
+++ b/tests/resources/xml/wsdl_Definitions.xml
@@ -0,0 +1,39 @@
+
+ SomeChunk
+
+
+ TypesChunk
+
+
+ MessageChunk
+
+
+
+
+
+
+
+
+
+
+ BindingChunk
+
+ OperationChunk
+
+ InputChunk
+
+
+ OutputChunk
+
+
+ FaultOneChunk
+
+
+
+
+ ServiceChunk
+
+ ChunkOne
+
+
+
diff --git a/tests/resources/xml/wsdl_Fault.xml b/tests/resources/xml/wsdl_Fault.xml
new file mode 100644
index 00000000..cbd29c39
--- /dev/null
+++ b/tests/resources/xml/wsdl_Fault.xml
@@ -0,0 +1 @@
+
diff --git a/tests/resources/xml/wsdl_Import.xml b/tests/resources/xml/wsdl_Import.xml
new file mode 100644
index 00000000..34c29e1c
--- /dev/null
+++ b/tests/resources/xml/wsdl_Import.xml
@@ -0,0 +1 @@
+
diff --git a/tests/resources/xml/wsdl_Input.xml b/tests/resources/xml/wsdl_Input.xml
new file mode 100644
index 00000000..8b31f6ca
--- /dev/null
+++ b/tests/resources/xml/wsdl_Input.xml
@@ -0,0 +1 @@
+
diff --git a/tests/resources/xml/wsdl_Message.xml b/tests/resources/xml/wsdl_Message.xml
new file mode 100644
index 00000000..388f296d
--- /dev/null
+++ b/tests/resources/xml/wsdl_Message.xml
@@ -0,0 +1,5 @@
+
+ SomeChunk
+
+
+
diff --git a/tests/resources/xml/wsdl_Output.xml b/tests/resources/xml/wsdl_Output.xml
new file mode 100644
index 00000000..57adaa16
--- /dev/null
+++ b/tests/resources/xml/wsdl_Output.xml
@@ -0,0 +1 @@
+
diff --git a/tests/resources/xml/wsdl_Part.xml b/tests/resources/xml/wsdl_Part.xml
new file mode 100644
index 00000000..2bc83e39
--- /dev/null
+++ b/tests/resources/xml/wsdl_Part.xml
@@ -0,0 +1 @@
+
diff --git a/tests/resources/xml/wsdl_Port.xml b/tests/resources/xml/wsdl_Port.xml
new file mode 100644
index 00000000..bc94f21c
--- /dev/null
+++ b/tests/resources/xml/wsdl_Port.xml
@@ -0,0 +1,3 @@
+
+ SomeChunk
+
diff --git a/tests/resources/xml/wsdl_PortType.xml b/tests/resources/xml/wsdl_PortType.xml
new file mode 100644
index 00000000..9392e7a1
--- /dev/null
+++ b/tests/resources/xml/wsdl_PortType.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/resources/xml/wsdl_PortTypeOperation_Input.xml b/tests/resources/xml/wsdl_PortTypeOperation_Input.xml
new file mode 100644
index 00000000..40e29eb5
--- /dev/null
+++ b/tests/resources/xml/wsdl_PortTypeOperation_Input.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/tests/resources/xml/wsdl_PortTypeOperation_Output.xml b/tests/resources/xml/wsdl_PortTypeOperation_Output.xml
new file mode 100644
index 00000000..058211d3
--- /dev/null
+++ b/tests/resources/xml/wsdl_PortTypeOperation_Output.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/tests/resources/xml/wsdl_Service.xml b/tests/resources/xml/wsdl_Service.xml
new file mode 100644
index 00000000..09bd0045
--- /dev/null
+++ b/tests/resources/xml/wsdl_Service.xml
@@ -0,0 +1,9 @@
+
+ SomeChunk
+
+ ChunkOne
+
+
+ ChunkTwo
+
+
diff --git a/tests/resources/xml/wsdl_Types.xml b/tests/resources/xml/wsdl_Types.xml
new file mode 100644
index 00000000..4497ff21
--- /dev/null
+++ b/tests/resources/xml/wsdl_Types.xml
@@ -0,0 +1,3 @@
+
+ SomeChunk
+