AttributeValue   A
last analyzed

Complexity

Total Complexity 23

Size/Duplication

Total Lines 170
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 78
dl 0
loc 170
rs 10
c 0
b 0
f 0
wmc 23

5 Methods

Rating   Name   Duplication   Size   Complexity  
A getValue() 0 3 1
A __construct() 0 3 1
A getXsiType() 0 22 5
A toXML() 0 35 5
B fromXML() 0 51 11
1
<?php
2
3
declare(strict_types=1);
4
5
namespace SimpleSAML\SAML2\XML\saml;
6
7
use DateTimeImmutable;
8
use DateTimeInterface;
9
use DOMElement;
10
use SimpleSAML\SAML2\Assert\Assert;
11
use SimpleSAML\SAML2\Constants as C;
12
use SimpleSAML\XML\AbstractElement;
13
use SimpleSAML\XML\Chunk;
14
use SimpleSAML\XML\Exception\InvalidDOMElementException;
15
use SimpleSAML\XML\SchemaValidatableElementInterface;
16
use SimpleSAML\XML\SchemaValidatableElementTrait;
17
18
use function class_exists;
19
use function explode;
20
use function gettype;
21
use function intval;
22
use function str_contains;
23
24
/**
25
 * Serializable class representing an AttributeValue.
26
 *
27
 * @package simplesamlphp/saml2
28
 */
29
class AttributeValue extends AbstractSamlElement implements SchemaValidatableElementInterface
30
{
31
    use SchemaValidatableElementTrait;
32
33
    /**
34
     * Create an AttributeValue.
35
     *
36
     * The value of this element. Can be one of:
37
     *  - string
38
     *  - int
39
     *  - null
40
     *  - \DateTimeInterface
41
     *  - \SimpleSAML\XML\AbstractElement
42
     *
43
     * @param string|int|null|\DateTimeInterface|\SimpleSAML\XML\AbstractElement $value
44
     * @throws \SimpleSAML\Assert\AssertionFailedException if the supplied value is neither a string or a DOMElement
45
     */
46
    final public function __construct(
47
        protected string|int|null|DateTimeInterface|AbstractElement $value,
48
    ) {
49
    }
50
51
52
    /**
53
     * Get the XSI type of this attribute value.
54
     *
55
     * @return string
56
     */
57
    public function getXsiType(): string
58
    {
59
        $value = $this->getValue();
60
        $type = gettype($value);
61
62
        switch ($type) {
63
            case "integer":
64
                return "xs:integer";
65
            case "NULL":
66
                return "xs:nil";
67
            case "object":
68
                if ($value instanceof DateTimeInterface) {
69
                    return 'xs:dateTime';
70
                }
71
72
                return sprintf(
73
                    '%s:%s',
74
                    $value::getNamespacePrefix(),
75
                    AbstractElement::getClassName(get_class($value)),
76
                );
77
            default:
78
                return "xs:string";
79
        }
80
    }
81
82
83
    /**
84
     * Get this attribute value.
85
     *
86
     * @return string|int|\SimpleSAML\XML\AbstractElement|null
87
     */
88
    public function getValue()
89
    {
90
        return $this->value;
91
    }
92
93
94
    /**
95
     * Convert XML into a AttributeValue
96
     *
97
     * @param \DOMElement $xml The XML element we should load
98
     * @return static
99
     *
100
     * @throws \SimpleSAML\XML\Exception\InvalidDOMElementException
101
     *   if the qualified name of the supplied element is wrong
102
     */
103
    public static function fromXML(DOMElement $xml): static
104
    {
105
        Assert::same($xml->localName, 'AttributeValue', InvalidDOMElementException::class);
106
        Assert::same($xml->namespaceURI, AttributeValue::NS, InvalidDOMElementException::class);
107
108
        if ($xml->childElementCount > 0) {
109
            $node = $xml->firstElementChild;
110
111
            if (str_contains($node->tagName, ':')) {
112
                list($prefix, $eltName) = explode(':', $node->tagName);
113
                $className = sprintf('\SimpleSAML\SAML2\XML\%s\%s', $prefix, $eltName);
114
115
                if (class_exists($className)) {
116
                    $value = $className::fromXML($node);
117
                } else {
118
                    $value = Chunk::fromXML($node);
0 ignored issues
show
Bug introduced by
It seems like $node can also be of type null; however, parameter $xml of SimpleSAML\XML\Chunk::fromXML() does only seem to accept DOMElement, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

118
                    $value = Chunk::fromXML(/** @scrutinizer ignore-type */ $node);
Loading history...
119
                }
120
            } else {
121
                $value = Chunk::fromXML($node);
122
            }
123
        } elseif (
124
            $xml->hasAttributeNS(C::NS_XSI, "type") &&
125
            $xml->getAttributeNS(C::NS_XSI, "type") === "xs:integer"
126
        ) {
127
            Assert::numeric($xml->textContent);
128
129
            // we have an integer as value
130
            $value = intval($xml->textContent);
131
        } elseif (
132
            $xml->hasAttributeNS(C::NS_XSI, "type") &&
133
            $xml->getAttributeNS(C::NS_XSI, "type") === "xs:dateTime"
134
        ) {
135
            Assert::validDateTime($xml->textContent);
136
137
            // we have a dateTime as value
138
            $value = new DateTimeImmutable($xml->textContent);
139
        } elseif (
140
            // null value
141
            $xml->hasAttributeNS(C::NS_XSI, "nil") &&
142
            ($xml->getAttributeNS(C::NS_XSI, "nil") === "1" ||
143
                $xml->getAttributeNS(C::NS_XSI, "nil") === "true")
144
        ) {
145
            Assert::isEmpty($xml->nodeValue);
146
            Assert::isEmpty($xml->textContent);
147
148
            $value = null;
149
        } else {
150
            $value = $xml->textContent;
151
        }
152
153
        return new static($value);
154
    }
155
156
157
    /**
158
     * Append this attribute value to an element.
159
     *
160
     * @param \DOMElement|null $parent The element we should append this attribute value to.
161
     *
162
     * @return \DOMElement The generated AttributeValue element.
163
     */
164
    public function toXML(?DOMElement $parent = null): DOMElement
165
    {
166
        $e = parent::instantiateParentElement($parent);
167
168
        $value = $this->getValue();
169
        $type = gettype($value);
170
171
        switch ($type) {
172
            case "integer":
173
                // make sure that the xs namespace is available in the AttributeValue
174
                $e->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:xsi', C::NS_XSI);
175
                $e->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:xs', C::NS_XS);
176
                $e->setAttributeNS(C::NS_XSI, 'xsi:type', 'xs:integer');
177
                $e->textContent = strval($value);
178
                break;
179
            case "NULL":
180
                $e->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:xsi', C::NS_XSI);
181
                $e->setAttributeNS(C::NS_XSI, 'xsi:nil', '1');
182
                break;
183
            case "object":
184
                if ($value instanceof DateTimeInterface) {
185
                    $e->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:xsi', C::NS_XSI);
186
                    $e->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:xs', C::NS_XS);
187
                    $e->setAttributeNS(C::NS_XSI, 'xsi:type', 'xs:dateTime');
188
                    $e->textContent = $value->format(C::DATETIME_FORMAT);
189
                } else {
190
                    $value->toXML($e);
191
                }
192
                break;
193
            default: // string
194
                $e->textContent = $value;
195
                break;
196
        }
197
198
        return $e;
199
    }
200
}
201