Passed
Branch feature/php8.3 (4d3b0a)
by Tim
17:15
created

QNameValue   A

Complexity

Total Complexity 18

Size/Duplication

Total Lines 169
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 41
dl 0
loc 169
rs 10
c 1
b 0
f 0
wmc 18
1
<?php
2
3
declare(strict_types=1);
4
5
namespace SimpleSAML\XMLSchema\Type;
6
7
use DOMElement;
8
use SimpleSAML\XML\Assert\Assert;
9
use SimpleSAML\XML\DOMDocumentFactory;
10
use SimpleSAML\XMLSchema\Exception\SchemaViolationException;
11
use SimpleSAML\XMLSchema\Type\Interface\AbstractAnySimpleType;
12
13
use function preg_match;
14
15
/**
16
 * @package simplesaml/xml-common
17
 */
18
class QNameValue extends AbstractAnySimpleType
19
{
20
    public const string SCHEMA_TYPE = 'QName';
0 ignored issues
show
Bug introduced by
A parse error occurred: Syntax error, unexpected T_STRING, expecting '=' on line 20 at column 24
Loading history...
21
22
23
    protected ?AnyURIValue $namespaceURI;
24
25
    protected ?NCNameValue $namespacePrefix;
26
27
    protected NCNameValue $localName;
28
29
    private static string $qname_regex = '/^
30
        (?:
31
          \{                 # Match a literal {
32
            (\S+)            # Match one or more non-whitespace character
33
          \}                 # Match a literal }
34
          (?:
35
            ([\w_][\w.-]*)   # Match a-z or underscore followed by any word-character, dot or dash
36
            :                # Match a literal :
37
          )?
38
        )?                   # Namespace and prefix are optional
39
        ([\w_][\w.-]*)       # Match a-z or underscore followed by any word-character, dot or dash
40
        $/Dimx';
41
42
43
    /**
44
     * Sanitize the value.
45
     *
46
     * @param string $value  The unsanitized value
47
     */
48
    protected function sanitizeValue(string $value): string
49
    {
50
        return static::collapseWhitespace(static::normalizeWhitespace($value));
51
    }
52
53
54
    /**
55
     * Validate the value.
56
     *
57
     * @param string $value
58
     * @throws \SimpleSAML\XMLSchema\Exception\SchemaViolationException on failure
59
     */
60
    protected function validateValue(string $value): void
61
    {
62
        $qName = $this->sanitizeValue($value);
63
64
        /**
65
         * Split our custom format of {<namespaceURI>}<prefix>:<localName> into individual parts
66
         */
67
        $result = preg_match(
68
            self::$qname_regex,
69
            $qName,
70
            $matches,
71
            PREG_UNMATCHED_AS_NULL,
72
        );
73
74
        if ($result && count($matches) === 4) {
75
            list($qName, $namespaceURI, $namespacePrefix, $localName) = $matches;
76
77
            $this->namespaceURI = ($namespaceURI !== null) ? AnyURIValue::fromString($namespaceURI) : null;
78
            $this->namespacePrefix = ($namespacePrefix !== null) ? NCNameValue::fromString($namespacePrefix) : null;
79
            $this->localName = NCNameValue::fromString($localName);
80
        } else {
81
            throw new SchemaViolationException(sprintf('\'%s\' is not a valid xs:QName.', $qName));
82
        }
83
    }
84
85
86
    /**
87
     * Get the value.
88
     */
89
    public function getValue(): string
90
    {
91
        $prefix = $this->getNamespacePrefix();
92
        return ($prefix ? $prefix . ':' : '') . $this->getLocalName();
93
    }
94
95
96
    /**
97
     * Get the namespaceURI for this qualified name.
98
     *
99
     * @return \SimpleSAML\XMLSchema\Type\AnyURIValue|null
100
     */
101
    public function getNamespaceURI(): ?AnyURIValue
102
    {
103
        return $this->namespaceURI;
104
    }
105
106
107
    /**
108
     * Get the namespace-prefix for this qualified name.
109
     *
110
     * @return \SimpleSAML\XMLSchema\Type\NCNameValue|null
111
     */
112
    public function getNamespacePrefix(): ?NCNameValue
113
    {
114
        return $this->namespacePrefix;
115
    }
116
117
118
    /**
119
     * Get the local name for this qualified name.
120
     *
121
     * @return \SimpleSAML\XMLSchema\Type\NCNameValue
122
     */
123
    public function getLocalName(): NCNameValue
124
    {
125
        return $this->localName;
126
    }
127
128
129
    /**
130
     * @param \SimpleSAML\XMLSchema\Type\NCNameValue $localName
131
     * @param \SimpleSAML\XMLSchema\Type\AnyURIValue|null $namespaceURI
132
     * @param \SimpleSAML\XMLSchema\Type\NCNameValue|null $namespacePrefix
133
     */
134
    public static function fromParts(
135
        NCNameValue $localName,
136
        ?AnyURIValue $namespaceURI,
137
        ?NCNameValue $namespacePrefix,
138
    ): static {
139
        if ($namespaceURI === null) {
140
            // If we don't have a namespace, we can't have a prefix either
141
            Assert::null($namespacePrefix, SchemaViolationException::class);
142
            return new static($localName->getValue());
143
        }
144
145
        return new static(
146
            '{' . $namespaceURI->getValue() . '}'
147
            . ($namespacePrefix ? ($namespacePrefix->getValue() . ':') : '')
148
            . $localName,
149
        );
150
    }
151
152
153
    /**
154
     * @param string $qName
155
     * @param \DOMElement $element
156
     */
157
    public static function fromDocument(
158
        string $qName,
159
        DOMElement $element,
160
    ): static {
161
        $namespacePrefix = null;
162
        if (str_contains($qName, ':')) {
163
            list($namespacePrefix, $localName) = explode(':', $qName, 2);
164
        } else {
165
            // No prefix
166
            $localName = $qName;
167
        }
168
169
        // Will return the default namespace (if any) when prefix is NULL
170
        if ($namespacePrefix === null) {
171
            $namespaceURI = $element->lookupNamespaceURI($namespacePrefix);
172
        } else {
173
            $namespaceURI = DOMDocumentFactory::lookupNamespaceUri($element, $namespacePrefix);
174
        }
175
176
        return new static('{' . $namespaceURI . '}' . ($namespacePrefix ? $namespacePrefix . ':' : '') . $localName);
177
    }
178
}
179