-
-
Notifications
You must be signed in to change notification settings - Fork 53
Add a new ref()->domElementNamespace() function
#2516
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 1.x
Are you sure you want to change the base?
Changes from all commits
0e8b049
34694a8
9b6dbac
f734a66
9609621
55b0cc1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,88 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Flow\ETL\Function; | ||
|
|
||
| use Dom\Document; | ||
| use Dom\Element; | ||
| use Dom\HTMLElement; | ||
| use Dom\XPath; | ||
| use DOMDocument; | ||
| use DOMNode; | ||
| use DOMXPath; | ||
| use Flow\ETL\Exception\InvalidArgumentException; | ||
| use Flow\ETL\FlowContext; | ||
| use Flow\ETL\Row; | ||
|
|
||
| use function class_exists; | ||
| use function count; | ||
| use function Flow\Types\DSL\type_instance_of; | ||
| use function Flow\Types\DSL\type_list; | ||
| use function is_array; | ||
| use function reset; | ||
|
|
||
| final class DOMElementNamespaceValue extends ScalarFunctionChain | ||
| { | ||
| public function __construct( | ||
| private readonly ScalarFunction|DOMNode|HTMLElement $domElement, | ||
| private readonly ScalarFunction|string|null $attribute, | ||
| ) {} | ||
|
|
||
| public function eval(Row $row, FlowContext $context): ?string | ||
| { | ||
| $types = [ | ||
| type_instance_of(DOMNode::class), | ||
| type_list(type_instance_of(DOMNode::class)), | ||
| ]; | ||
|
|
||
| if (class_exists('\Dom\Element')) { | ||
| $types[] = type_instance_of(Element::class); | ||
| $types[] = type_list(type_instance_of(Element::class)); | ||
| } | ||
|
|
||
| $node = (new Parameter($this->domElement))->as($row, $context, ...$types); | ||
|
|
||
| if ($node instanceof DOMDocument) { | ||
| $node = $node->documentElement; | ||
| } | ||
|
|
||
| if (is_array($node) && count($node)) { | ||
| $node = reset($node); | ||
| } | ||
|
|
||
| if ($node === null) { | ||
| return $context | ||
| ->functions() | ||
| ->invalidResult(new InvalidArgumentException('DOMElementNamespaceValue requires non-null DOMNode')); | ||
| } | ||
|
|
||
| if (!$node instanceof DOMNode && !$node instanceof Element) { | ||
| return null; | ||
| } | ||
|
|
||
| $attributeName = (new Parameter($this->attribute))->asString($row, $context); | ||
|
|
||
| if ($attributeName === null) { | ||
| return $node->namespaceURI; | ||
| } | ||
|
|
||
| if (null === $node->ownerDocument) { | ||
| return null; | ||
| } | ||
|
|
||
| $document = $node->ownerDocument; | ||
|
|
||
| $xpath = $document instanceof Document ? new XPath($document) : new DOMXPath($document); | ||
|
|
||
| /** @var \DOMNameSpaceNode $nsNode */ | ||
| // @mago-ignore analysis:invalid-iterator | ||
| foreach ($xpath->query('namespace::*') ?: [] as $nsNode) { | ||
| if ($nsNode->nodeName === $attributeName || str_starts_with($nsNode->nodeName, $attributeName . ':')) { | ||
| return $nsNode->nodeValue; | ||
| } | ||
| } | ||
|
|
||
| return null; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,6 +4,8 @@ | |
|
|
||
| namespace Flow\ETL\Row\Entry; | ||
|
|
||
| use Dom\Element; | ||
| use Dom\XMLDocument; | ||
| use DOMDocument; | ||
| use DOMElement; | ||
| use Flow\ETL\Exception\InvalidArgumentException; | ||
|
|
@@ -24,7 +26,7 @@ | |
| use function sprintf; | ||
|
|
||
| /** | ||
| * @template-covariant T of \DOMElement|null | ||
| * @template-covariant T of \DOMElement|Element|null | ||
| * | ||
| * @implements Entry<T> | ||
| */ | ||
|
|
@@ -39,7 +41,7 @@ final class XMLElementEntry implements Entry | |
| */ | ||
| public function __construct( | ||
| private readonly string $name, | ||
| private readonly ?DOMElement $value, | ||
| private readonly DOMElement|Element|null $value, | ||
| ?Metadata $metadata = null, | ||
| ) { | ||
| $this->definition = new XMLElementDefinition( | ||
|
|
@@ -168,7 +170,14 @@ public function toString(): string | |
| return ''; | ||
| } | ||
|
|
||
| $serialized = $ownerDocument->saveXML($this->value); | ||
| if ($ownerDocument instanceof XMLDocument) { | ||
| // @mago-ignore analysis:possibly-invalid-argument | ||
| $serialized = $ownerDocument->saveXml($this->value); | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In official docs, this method is not documented, but it's visible in the official stub. |
||
| } else { | ||
| /** @var string|false $serialized */ | ||
| // @mago-ignore analysis:possibly-invalid-argument,non-existent-method | ||
| $serialized = $ownerDocument->saveXML($this->value); | ||
| } | ||
|
|
||
| if ($serialized === false) { | ||
| throw new RuntimeException('Failed to serialize XML element.'); | ||
|
|
@@ -178,7 +187,7 @@ public function toString(): string | |
| } | ||
|
|
||
| /** | ||
| * @return Type<DOMElement> | ||
| * @return Type<DOMElement|Element> | ||
| */ | ||
| public function type(): Type | ||
| { | ||
|
|
@@ -188,7 +197,7 @@ public function type(): Type | |
| /** | ||
| * @return T | ||
| */ | ||
| public function value(): ?DOMElement | ||
| public function value(): DOMElement|Element|null | ||
| { | ||
| return $this->value; | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,6 +4,7 @@ | |
|
|
||
| namespace Flow\ETL\Row\Entry; | ||
|
|
||
| use Dom\XMLDocument; | ||
| use DOMDocument; | ||
| use Flow\ETL\Exception\InvalidArgumentException; | ||
| use Flow\ETL\Row\Entry; | ||
|
|
@@ -23,7 +24,7 @@ | |
| use function sprintf; | ||
|
|
||
| /** | ||
| * @template-covariant T of \DOMDocument|null | ||
| * @template-covariant T of \DOMDocument|XMLDocument|null | ||
| * | ||
| * @implements Entry<T> | ||
| */ | ||
|
|
@@ -38,7 +39,7 @@ final class XMLEntry implements Entry | |
| */ | ||
| public function __construct( | ||
| private readonly string $name, | ||
| private readonly ?DOMDocument $value, | ||
| private readonly DOMDocument|XMLDocument|null $value, | ||
| ?Metadata $metadata = null, | ||
| ) { | ||
| $this->definition = new XMLDefinition($this->name, $this->value === null, $metadata ?: Metadata::empty()); | ||
|
|
@@ -153,7 +154,12 @@ public function toString(): string | |
| return ''; | ||
| } | ||
|
|
||
| $serialized = $this->value->saveXML($this->value->documentElement); | ||
| if ($this->value instanceof XMLDocument) { | ||
| $serialized = $this->value->saveXml($this->value->documentElement); | ||
| } else { | ||
| // @mago-ignore analysis:possibly-invalid-argument,possibly-invalid-argument | ||
| $serialized = $this->value->saveXML($this->value->documentElement); | ||
|
Comment on lines
+157
to
+161
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Mago has a problem holding the Dom object type, even with an instance of pointing to its new or old version. |
||
| } | ||
|
|
||
| if ($serialized === false) { | ||
| throw new RuntimeException('Failed to serialize XML document.'); | ||
|
|
@@ -163,7 +169,7 @@ public function toString(): string | |
| } | ||
|
|
||
| /** | ||
| * @return Type<DOMDocument> | ||
| * @return Type<DOMDocument|XMLDocument> | ||
| */ | ||
| public function type(): Type | ||
| { | ||
|
|
@@ -173,7 +179,7 @@ public function type(): Type | |
| /** | ||
| * @return T | ||
| */ | ||
| public function value(): ?DOMDocument | ||
| public function value(): DOMDocument|XMLDocument|null | ||
| { | ||
| return $this->value; | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Flow\ETL\Tests\Unit\Function; | ||
|
|
||
| use Dom\Element; | ||
| use Dom\XMLDocument; | ||
| use DOMDocument; | ||
| use DOMElement; | ||
| use PHPUnit\Framework\Attributes\RequiresPhp; | ||
| use PHPUnit\Framework\TestCase; | ||
|
|
||
| use function Flow\ETL\DSL\config; | ||
| use function Flow\ETL\DSL\flow_context; | ||
| use function Flow\ETL\DSL\ref; | ||
| use function Flow\ETL\DSL\row; | ||
|
|
||
| use const LIBXML_HTML_NOIMPLIED; | ||
| use const LIBXML_NOERROR; | ||
|
|
||
| final class DOMElementNamespaceTest extends TestCase | ||
| { | ||
| private const string XML = <<<'XML' | ||
| <?xml version='1.0' encoding='UTF-8'?> | ||
| <Invoice xmlns:cec="urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2" | ||
| xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2" | ||
| xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2" | ||
| xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2" | ||
| xmlns:sh="http://www.unece.org/cefact/nodes/StandardBusinessDocumentHeader"> | ||
| </Invoice> | ||
| XML; | ||
|
|
||
| #[RequiresPhp('>= 8.4')] | ||
| public function test_html_getting_element_namespace(): void | ||
| { | ||
| // @mago-ignore analysis:unavailable-method | ||
| $element = XMLDocument::createFromString(self::XML, LIBXML_HTML_NOIMPLIED | LIBXML_NOERROR); | ||
|
|
||
| static::assertInstanceOf(Element::class, $element->documentElement); | ||
| static::assertSame('urn:oasis:names:specification:ubl:schema:xsd:Invoice-2', ref('node') | ||
| ->domElementNamespace() | ||
| ->eval( | ||
| row(flow_context(config())->entryFactory()->create('node', $element->documentElement)), | ||
| flow_context(), | ||
| )); | ||
| } | ||
|
|
||
| public function test_xml_getting_element_namespace(): void | ||
| { | ||
| $xml = new DOMDocument(); | ||
| $xml->loadXML(self::XML); | ||
|
|
||
| static::assertInstanceOf(DOMElement::class, $xml->documentElement); | ||
| static::assertSame('urn:oasis:names:specification:ubl:schema:xsd:Invoice-2', ref('node') | ||
| ->domElementNamespace() | ||
| ->eval(row(flow_context(config())->entryFactory()->create('node', $xml->documentElement)), flow_context())); | ||
| } | ||
|
|
||
| public function test_xml_getting_element_non_default_namespace(): void | ||
| { | ||
| $xml = new DOMDocument(); | ||
| $xml->loadXML(self::XML); | ||
|
|
||
| static::assertInstanceOf(DOMElement::class, $xml->documentElement); | ||
| static::assertSame('http://www.unece.org/cefact/nodes/StandardBusinessDocumentHeader', ref('node') | ||
| ->domElementNamespace('xmlns:sh') | ||
| ->eval(row(flow_context(config())->entryFactory()->create('node', $xml->documentElement)), flow_context())); | ||
| } | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.