AttributeValue   A
last analyzed

Complexity

Total Complexity 23

Size/Duplication

Total Lines 171
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 78
dl 0
loc 171
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
    /**
35
     * Create an AttributeValue.
36
     *
37
     * The value of this element. Can be one of:
38
     *  - string
39
     *  - int
40
     *  - null
41
     *  - \DateTimeInterface
42
     *  - \SimpleSAML\XML\AbstractElement
43
     *
44
     * @param string|int|null|\DateTimeInterface|\SimpleSAML\XML\AbstractElement $value
45
     * @throws \SimpleSAML\Assert\AssertionFailedException if the supplied value is neither a string or a DOMElement
46
     */
47
    final public function __construct(
48
        protected string|int|null|DateTimeInterface|AbstractElement $value,
49
    ) {
50
    }
51
52
53
    /**
54
     * Get the XSI type of this attribute value.
55
     *
56
     * @return string
57
     */
58
    public function getXsiType(): string
59
    {
60
        $value = $this->getValue();
61
        $type = gettype($value);
62
63
        switch ($type) {
64
            case "integer":
65
                return "xs:integer";
66
            case "NULL":
67
                return "xs:nil";
68
            case "object":
69
                if ($value instanceof DateTimeInterface) {
70
                    return 'xs:dateTime';
71
                }
72
73
                return sprintf(
74
                    '%s:%s',
75
                    $value::getNamespacePrefix(),
76
                    AbstractElement::getClassName(get_class($value)),
77
                );
78
            default:
79
                return "xs:string";
80
        }
81
    }
82
83
84
    /**
85
     * Get this attribute value.
86
     *
87
     * @return string|int|\SimpleSAML\XML\AbstractElement|null
88
     */
89
    public function getValue()
90
    {
91
        return $this->value;
92
    }
93
94
95
    /**
96
     * Convert XML into a AttributeValue
97
     *
98
     * @param \DOMElement $xml The XML element we should load
99
     * @return static
100
     *
101
     * @throws \SimpleSAML\XML\Exception\InvalidDOMElementException
102
     *   if the qualified name of the supplied element is wrong
103
     */
104
    public static function fromXML(DOMElement $xml): static
105
    {
106
        Assert::same($xml->localName, 'AttributeValue', InvalidDOMElementException::class);
107
        Assert::same($xml->namespaceURI, AttributeValue::NS, InvalidDOMElementException::class);
108
109
        if ($xml->childElementCount > 0) {
110
            $node = $xml->firstElementChild;
111
112
            if (str_contains($node->tagName, ':')) {
113
                list($prefix, $eltName) = explode(':', $node->tagName);
114
                $className = sprintf('\SimpleSAML\SAML2\XML\%s\%s', $prefix, $eltName);
115
116
                if (class_exists($className)) {
117
                    $value = $className::fromXML($node);
118
                } else {
119
                    $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

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