Skip to content

[Proposal]: New function for XML: DOM element namespace #2514

Description

@stloyd

Describe the Proposal

When working with XML documents, you often need to know the namespace of particular elements to determine which type of validation should be applied or how to read the document. Currently, there is no way to get a namespace from XML nodes in Flow.

I would propose adding a new function similar to DOMElementAttributeValue, i.e., DOMElementNamespaceValue.

As an example, I would suggest, i.e., UBL (Universal Business Language): https://docs.oasis-open.org/ubl/os-UBL-2.1/UBL-2.1.pdf (page 2 with list of known schemas that determine document logic)

API Adjustments

PoC (not tested at all)

<?php

declare(strict_types=1);

namespace Flow\ETL\Function;

final class DOMElementNamespaceValue extends ScalarFunctionChain
{
    public function __construct(
        private readonly ScalarFunction|DOMNode|HTMLElement $domElement,
        private readonly ScalarFunction|string $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\HTMLElement')) {
            $types[] = type_instance_of(HTMLElement::class);
            $types[] = type_list(type_instance_of(HTMLElement::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'));
        }

        $attributeName = (new Parameter($this->attribute))->asString($row, $context);

        if ($attributeName === null) {
            return $context
                ->functions()
                ->invalidResult(
                    new InvalidArgumentException('DOMElementNamespaceValue requires non-null attribute name'),
                );
        }

        if (!$node instanceof DOMNode && !$node instanceof HTMLElement) {
            return null;
        }

        $attributes = $node->attributes;

        if ($attributes === null) {
            return null;
        }

        foreach ($attributes as $attribute) {
            if ($attribute->nodeName === $attributeName || str_starts_with($attribute->nodeName, $attributeName.':')) {
                return $attribute->nodeValue;
            }
        }

        return null;
    }
}

Are you intending to also work on proposed change?

Yes

Are you interested in sponsoring this change?

No

Integration & Dependencies

None

Metadata

Metadata

Assignees

Labels

No labels
No labels

Fields

No fields configured for Feature.

Projects

Status
In Progress

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions