Passed
Push — develop ( a74f99...91eb6f )
by Mikaël
07:15
created

Element::setNamespace()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

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