AttributeValue::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 0

Duplication

Lines 0
Ratio 0 %

Importance

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

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