Element::getNamespacedName()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 1
c 1
b 0
f 0
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
cc 1
nc 1
nop 0
crap 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace WsdlToPhp\WsSecurity;
6
7
use DOMDocument;
8
use DOMElement;
9
10
/**
11
 * Base class to represent any element that must be included for a WS-Security header.
12
 * Each element must be named with the actual targeted element tag name.
13
 * The namespace is also mandatory.
14
 * Finally the attributes are optional.
15
 */
16
class Element
17
{
18
    public const NS_WSSE = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd';
19
20
    public const NS_WSSE_NAME = 'wsse';
21
22
    /**
23
     * @deprecated
24
     */
25
    public const NS_WSSU = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd';
26
27
    public const NS_WSU = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd';
28
29
    /**
30
     * @deprecated
31
     */
32
    public const NS_WSSU_NAME = 'wssu';
33
34
    public const NS_WSU_NAME = 'wsu';
35
36
    protected string $name = '';
37
38
    /**
39
     * Value of the element.
40
     * It can either be a string value or a Element object.
41
     *
42
     * @var Element|string
43
     */
44
    protected $value = '';
45
46
    /**
47
     * Array of attributes that must contains the element.
48
     *
49
     * @var array<string, mixed>
50
     */
51
    protected array $attributes = [];
52
53
    /**
54
     * The namespace the element belongs to.
55
     */
56
    protected string $namespace = '';
57
58
    /**
59
     * Nonce used to generate digest password.
60
     */
61
    protected string $nonceValue;
62
63
    /**
64
     * Timestamp used to generate digest password.
65
     */
66
    protected int $timestampValue;
67
68
    /**
69
     * Current DOMDocument used to generate XML content.
70
     */
71
    protected static ?DOMDocument $dom = null;
72
73
    /**
74
     * @param mixed                $value
75
     * @param array<string, mixed> $attributes
76
     */
77 18
    public function __construct(string $name, string $namespace, $value = null, array $attributes = [])
78
    {
79
        $this
80 18
            ->setName($name)
81 18
            ->setNamespace($namespace)
82 18
            ->setValue($value)
83 18
            ->setAttributes($attributes)
84
        ;
85
    }
86
87
    /**
88
     * Method called to generate the string XML request to be sent among the SOAP Header.
89
     *
90
     * @param bool $asDomElement returns elements as a \DOMElement or as a string
91
     *
92
     * @return DOMElement|false|string
93
     */
94 18
    protected function __toSend(bool $asDomElement = false)
95
    {
96
        // Create element tag.
97 18
        $element = self::getDom()->createElement($this->getNamespacedName());
98 18
        $element->setAttributeNS('http://www.w3.org/2000/xmlns/', sprintf('xmlns:%s', $this->getNamespacePrefix()), $this->getNamespace());
99
100
        // Define element value, add attributes if there are any
101
        $this
102 18
            ->appendValueToElementToSend($this->getValue(), $element)
103 18
            ->appendAttributesToElementToSend($element)
104
        ;
105
106 18
        return $asDomElement ? $element : self::getDom()->saveXML($element);
107
    }
108
109 18
    public function getName(): string
110
    {
111 18
        return $this->name;
112
    }
113
114 18
    public function setName(string $name): self
115
    {
116 18
        $this->name = $name;
117
118 18
        return $this;
119
    }
120
121
    /**
122
     * @return array<string, mixed>
123
     */
124 18
    public function getAttributes(): array
125
    {
126 18
        return $this->attributes;
127
    }
128
129
    /**
130
     * @param array<string, mixed> $attributes
131
     *
132
     * @return Element
133
     */
134 18
    public function setAttributes(array $attributes): self
135
    {
136 18
        $this->attributes = $attributes;
137
138 18
        return $this;
139
    }
140
141
    /**
142
     * @param mixed $value
143
     *
144
     * @return $this
145
     */
146 14
    public function setAttribute(string $name, $value): self
147
    {
148 14
        $this->attributes[$name] = $value;
149
150 14
        return $this;
151
    }
152
153 18
    public function hasAttributes(): bool
154
    {
155 18
        return 0 < count($this->attributes);
156
    }
157
158 18
    public function getNamespace(): string
159
    {
160 18
        return $this->namespace;
161
    }
162
163 18
    public function setNamespace(string $namespace): self
164
    {
165 18
        $this->namespace = $namespace;
166
167 18
        return $this;
168
    }
169
170
    /**
171
     * @return Element|string
172
     */
173 18
    public function getValue()
174
    {
175 18
        return $this->value;
176
    }
177
178
    /**
179
     * @param mixed $value
180
     *
181
     * @return Element
182
     */
183 18
    public function setValue($value): self
184
    {
185 18
        $this->value = $value;
186
187 18
        return $this;
188
    }
189
190 16
    public function getNonceValue(): string
191
    {
192 16
        return $this->nonceValue;
193
    }
194
195 18
    public function setNonceValue(string $nonceValue): self
196
    {
197 18
        $this->nonceValue = $nonceValue;
198
199 18
        return $this;
200
    }
201
202
    /**
203
     * @return int|string
204
     */
205 18
    public function getTimestampValue(bool $formatted = false)
206
    {
207 18
        return ($formatted && $this->timestampValue > 0) ? gmdate('Y-m-d\TH:i:s\Z', $this->timestampValue) : $this->timestampValue;
208
    }
209
210 18
    public function setTimestampValue(int $timestampValue): self
211
    {
212 18
        $this->timestampValue = $timestampValue;
213
214 18
        return $this;
215
    }
216
217
    /**
218
     * Returns the element to send as WS-Security header.
219
     *
220
     * @return DOMElement|false|string
221
     */
222 18
    public function toSend()
223
    {
224 18
        self::setDom(new DOMDocument('1.0', 'UTF-8'));
225
226 18
        return $this->__toSend();
227
    }
228
229
    /**
230
     * Handle adding value to element according to the value type.
231
     *
232
     * @param mixed $value
233
     *
234
     * @return Element
235
     */
236 18
    protected function appendValueToElementToSend($value, DOMElement $element): self
237
    {
238 18
        if ($value instanceof Element) {
239 18
            $this->appendElementToElementToSend($value, $element);
240 18
        } elseif (is_array($value)) {
241 18
            $this->appendValuesToElementToSend($value, $element);
242
        } elseif (!empty($value)) {
243 18
            $element->appendChild(self::getDom()->createTextNode($value));
244
        }
245
246 18
        return $this;
247
    }
248
249 18
    protected function appendElementToElementToSend(Element $value, DOMElement $element): void
250
    {
251 18
        $toSend = $value->__toSend(true);
252 18
        if ($toSend instanceof DOMElement) {
0 ignored issues
show
introduced by
$toSend is always a sub-type of DOMElement.
Loading history...
253 18
            $element->appendChild($toSend);
254
        }
255
    }
256
257
    /**
258
     * @param array<mixed> $values
259
     */
260 18
    protected function appendValuesToElementToSend(array $values, DOMElement $element): void
261
    {
262 18
        foreach ($values as $value) {
263 18
            $this->appendValueToElementToSend($value, $element);
264
        }
265
    }
266
267 18
    protected function appendAttributesToElementToSend(DOMElement $element): self
268
    {
269 18
        if (!$this->hasAttributes()) {
270 18
            return $this;
271
        }
272
273 18
        foreach ($this->getAttributes() as $attributeName => $attributeValue) {
274 18
            $matches = [];
275 18
            if (0 === preg_match(sprintf('/(%s|%s):/', self::NS_WSU_NAME, self::NS_WSSE_NAME), $attributeName, $matches)) {
276 18
                $element->setAttribute($attributeName, (string) $attributeValue);
277
            } else {
278 4
                $element->setAttributeNS(self::NS_WSSE_NAME === $matches[1] ? self::NS_WSSE : self::NS_WSU, $attributeName, $attributeValue);
279
            }
280
        }
281
282 18
        return $this;
283
    }
284
285
    /**
286
     * Returns the name with its namespace.
287
     */
288 18
    protected function getNamespacedName(): string
289
    {
290 18
        return sprintf('%s:%s', $this->getNamespacePrefix(), $this->getName());
291
    }
292
293 18
    private function getNamespacePrefix(): string
294
    {
295 18
        $namespacePrefix = '';
296
297 18
        switch ($this->getNamespace()) {
298 9
            case self::NS_WSSE:
299 18
                $namespacePrefix = self::NS_WSSE_NAME;
300
301 18
                break;
302
303 9
            case self::NS_WSU:
304 18
                $namespacePrefix = self::NS_WSU_NAME;
305
306 18
                break;
307
        }
308
309 18
        return $namespacePrefix;
310
    }
311
312 18
    private static function getDom(): ?DOMDocument
313
    {
314 18
        return self::$dom;
315
    }
316
317 18
    private static function setDom(DOMDocument $dom): void
318
    {
319 18
        self::$dom = $dom;
320
    }
321
}
322