Passed
Pull Request — master (#276)
by Tim
02:23
created

ContactPerson::toXML()   B

Complexity

Conditions 8
Paths 128

Size

Total Lines 34
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 17
nc 128
nop 1
dl 0
loc 34
rs 8.2111
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace SimpleSAML\SAML2\XML\md;
6
7
use DOMElement;
8
use Exception;
9
use SimpleSAML\Assert\Assert;
10
use SimpleSAML\SAML2\Constants;
11
use SimpleSAML\SAML2\Exception\ProtocolViolationException;
12
use SimpleSAML\SAML2\Utils\XPath;
13
use SimpleSAML\SAML2\XML\ExtendableElementTrait;
14
use SimpleSAML\XML\DOMDocumentFactory;
15
use SimpleSAML\XML\Exception\InvalidDOMElementException;
16
use SimpleSAML\XML\Exception\TooManyElementsException;
17
use SimpleSAML\XML\ExtendableAttributesTrait;
18
use SimpleSAML\XML\Utils as XMLUtils;
19
20
use function array_map;
21
use function array_pop;
22
use function count;
23
use function filter_var;
24
use function preg_replace;
25
use function var_export;
26
27
/**
28
 * Class representing SAML 2 ContactPerson.
29
 *
30
 * @package simplesamlphp/saml2
31
 */
32
final class ContactPerson extends AbstractMdElement
33
{
34
    use ExtendableElementTrait;
35
    use ExtendableAttributesTrait;
1 ignored issue
show
introduced by
The trait SimpleSAML\XML\ExtendableAttributesTrait requires some properties which are not provided by SimpleSAML\SAML2\XML\md\ContactPerson: $namespaceURI, $nodeName, $attributes, $value
Loading history...
36
37
    /**
38
     * The several different contact types as defined per specification
39
     */
40
    public const CONTACT_TYPES = [
41
        'technical',
42
        'support',
43
        'administrative',
44
        'billing',
45
        'other',
46
    ];
47
48
    /**
49
     * The contact type.
50
     *
51
     * @var string
52
     */
53
    protected string $contactType;
54
55
    /**
56
     * The Company of this contact.
57
     *
58
     * @var \SimpleSAML\SAML2\XML\md\Company|null
59
     */
60
    protected ?Company $Company = null;
61
62
    /**
63
     * The GivenName of this contact.
64
     *
65
     * @var \SimpleSAML\SAML2\XML\md\GivenName|null
66
     */
67
    protected ?GivenName $GivenName = null;
68
69
    /**
70
     * The SurName of this contact.
71
     *
72
     * @var \SimpleSAML\SAML2\XML\md\SurName|null
73
     */
74
    protected ?SurName $SurName = null;
75
76
    /**
77
     * The EmailAddresses of this contact.
78
     *
79
     * @var \SimpleSAML\SAML2\XML\md\EmailAddress[]
80
     */
81
    protected array $EmailAddresses = [];
82
83
    /**
84
     * The TelephoneNumbers of this contact.
85
     *
86
     * @var \SimpleSAML\SAML2\XML\md\TelephoneNumber[]
87
     */
88
    protected array $TelephoneNumbers = [];
89
90
91
    /**
92
     * ContactPerson constructor.
93
     *
94
     * @param string                                      $contactType
95
     * @param \SimpleSAML\SAML2\XML\md\Company|null       $company
96
     * @param \SimpleSAML\SAML2\XML\md\GivenName|null     $givenName
97
     * @param \SimpleSAML\SAML2\XML\md\SurName|null       $surName
98
     * @param \SimpleSAML\SAML2\XML\md\Extensions|null    $extensions
99
     * @param \SimpleSAML\SAML2\XML\md\EmailAddress[]     $email
100
     * @param \SimpleSAML\SAML2\XML\md\TelephoneNumber[]  $telephone
101
     * @param \DOMAttr[]                                  $namespacedAttributes
102
     */
103
    public function __construct(
104
        string $contactType,
105
        ?Company $company = null,
106
        ?GivenName $givenName = null,
107
        ?SurName $surName = null,
108
        ?Extensions $extensions = null,
109
        array $email = [],
110
        array $telephone = [],
111
        array $namespacedAttributes = []
112
    ) {
113
        $this->setContactType($contactType);
114
        $this->setCompany($company);
115
        $this->setGivenName($givenName);
116
        $this->setSurName($surName);
117
        $this->setEmailAddresses($email);
118
        $this->setTelephoneNumbers($telephone);
119
        $this->setExtensions($extensions);
120
        $this->setAttributesNS($namespacedAttributes);
121
    }
122
123
124
    /**
125
     * Retrieve the value of a child \DOMElements as an array of strings.
126
     *
127
     * @param \DOMElement $parent The parent element.
128
     * @param string      $name The name of the child elements.
129
     *
130
     * @return string[]   The value of the child elements.
131
     */
132
    private static function getStringElements(DOMElement $parent, string $name): array
133
    {
134
        $e = XPath::xpQuery($parent, './saml_metadata:' . $name, XPath::getXPath($parent));
135
136
        $ret = [];
137
        foreach ($e as $i) {
138
            $ret[] = $i->textContent;
139
        }
140
141
        return $ret;
142
    }
143
144
145
    /**
146
     * Retrieve the value of a child \DOMElement as a string.
147
     *
148
     * @param \DOMElement $parent The parent element.
149
     * @param string      $name The name of the child element.
150
     *
151
     * @return string|null The value of the child element.
152
     * @throws \SimpleSAML\Assert\AssertionFailedException
153
     */
154
    private static function getStringElement(DOMElement $parent, string $name): ?string
0 ignored issues
show
Unused Code introduced by
The method getStringElement() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
155
    {
156
        $e = self::getStringElements($parent, $name);
157
        if (empty($e)) {
158
            return null;
159
        }
160
161
        Assert::maxCount($e, 1, 'More than one ' . $name . ' in ' . $parent->tagName);
162
        return $e[0];
163
    }
164
165
166
    /**
167
     * Collect the value of the contactType-property
168
     *
169
     * @return string
170
     */
171
    public function getContactType(): string
172
    {
173
        return $this->contactType;
174
    }
175
176
177
    /**
178
     * Set the value of the contactType-property
179
     *
180
     * @param string $contactType
181
     * @throws \SimpleSAML\Assert\AssertionFailedException if $contactType is not one of the predefined values
182
     */
183
    protected function setContactType(string $contactType): void
184
    {
185
        Assert::oneOf($contactType, self::CONTACT_TYPES);
186
        $this->contactType = $contactType;
187
    }
188
189
190
    /**
191
     * Collect the value of the Company-property
192
     *
193
     * @return \SimpleSAML\SAML2\XML\md\Company|null
194
     */
195
    public function getCompany(): ?Company
196
    {
197
        return $this->Company;
198
    }
199
200
201
    /**
202
     * Set the value of the Company-property
203
     *
204
     * @param \SimpleSAML\SAML2\XML\md\Company|null $company
205
     */
206
    protected function setCompany(?Company $company): void
207
    {
208
        $this->Company = $company;
209
    }
210
211
212
    /**
213
     * Collect the value of the GivenName-property
214
     *
215
     * @return \SimpleSAML\SAML2\XML\md\GivenName|null
216
     */
217
    public function getGivenName(): ?GivenName
218
    {
219
        return $this->GivenName;
220
    }
221
222
223
    /**
224
     * Set the value of the GivenName-property
225
     *
226
     * @param \SimpleSAML\SAML2\XML\md\GivenName|null $givenName
227
     */
228
    protected function setGivenName(?GivenName $givenName): void
229
    {
230
        $this->GivenName = $givenName;
231
    }
232
233
234
    /**
235
     * Collect the value of the SurName-property
236
     *
237
     * @return \SimpleSAML\SAML2\XML\md\SurName|null
238
     */
239
    public function getSurName(): ?SurName
240
    {
241
        return $this->SurName;
242
    }
243
244
245
    /**
246
     * Set the value of the SurName-property
247
     *
248
     * @param \SimpleSAML\SAML2\XML\md\SurName|null $surName
249
     */
250
    protected function setSurName(?SurName $surName): void
251
    {
252
        $this->SurName = $surName;
253
    }
254
255
256
    /**
257
     * Collect the value of the EmailAddress-property.
258
     *
259
     * @return \SimpleSAML\SAML2\XML\md\EmailAddress[]
260
     */
261
    public function getEmailAddresses(): array
262
    {
263
        return $this->EmailAddresses;
264
    }
265
266
267
    /**
268
     * Set the value of the EmailAddress-property
269
     *
270
     * @param \SimpleSAML\SAML2\XML\md\EmailAddress[] $emailAddresses
271
     * @throws \SimpleSAML\Assert\AssertionFailedException
272
     */
273
    protected function setEmailAddresses(array $emailAddresses): void
274
    {
275
        Assert::allIsInstanceOf($emailAddresses, EmailAddress::class);
276
        $this->EmailAddresses = $emailAddresses;
277
    }
278
279
280
    /**
281
     * Collect the value of the TelephoneNumber property
282
     *
283
     * @return \SimpleSAML\SAML2\XML\md\TelephoneNumber[]
284
     */
285
    public function getTelephoneNumbers(): array
286
    {
287
        return $this->TelephoneNumbers;
288
    }
289
290
291
    /**
292
     * Set the value of the TelephoneNumber property
293
     *
294
     * @param \SimpleSAML\SAML2\XML\md\TelephoneNumber[] $telephoneNumbers
295
     * @throws \SimpleSAML\Assert\AssertionFailedException
296
     */
297
    protected function setTelephoneNumbers(array $telephoneNumbers): void
298
    {
299
        Assert::allIsInstanceOf($telephoneNumbers, TelephoneNumber::class);
300
        $this->TelephoneNumbers = $telephoneNumbers;
301
    }
302
303
304
    /**
305
     * Initialize a ContactPerson element.
306
     *
307
     * @param \DOMElement $xml The XML element we should load.
308
     * @return self
309
     *
310
     * @throws \SimpleSAML\XML\Exception\InvalidDOMElementException if the qualified name of the supplied element is wrong
311
     * @throws \SimpleSAML\XML\Exception\MissingAttributeException if the supplied element is missing one of the mandatory attributes
312
     * @throws \SimpleSAML\XML\Exception\TooManyElementsException if too many child-elements of a type are specified
313
     */
314
    public static function fromXML(DOMElement $xml): object
315
    {
316
        Assert::same($xml->localName, 'ContactPerson', InvalidDOMElementException::class);
317
        Assert::same($xml->namespaceURI, ContactPerson::NS, InvalidDOMElementException::class);
318
319
        $contactType = self::getAttribute($xml, 'contactType');
320
321
        $company = Company::getChildrenOfClass($xml);
322
        Assert::maxCount($company, 1, 'More than one Company in md:ContactPerson');
323
324
        $givenName = GivenName::getChildrenOfClass($xml);
325
        Assert::maxCount($givenName, 1, 'More than one GivenName in md:ContactPerson');
326
327
        $surName = SurName::getChildrenOfClass($xml);
328
        Assert::maxCount($surName, 1, 'More than one SurName in md:ContactPerson');
329
330
        $email = EmailAddress::getChildrenOfClass($xml);
331
        $telephone = TelephoneNumber::getChildrenOfClass($xml);
332
333
        $extensions = Extensions::getChildrenOfClass($xml);
334
        Assert::maxCount($extensions, 1, 'Only one md:Extensions element is allowed.', TooManyElementsException::class);
335
336
        return new self(
337
            $contactType,
338
            array_pop($company),
339
            array_pop($givenName),
340
            array_pop($surName),
341
            (count($extensions) === 1) ? $extensions[0] : null,
342
            $email,
343
            $telephone,
344
            self::getAttributesNSFromXML($xml)
345
        );
346
    }
347
348
349
    /**
350
     * Convert this ContactPerson to XML.
351
     *
352
     * @param \DOMElement|null $parent The element we should add this contact to.
353
     *
354
     * @return \DOMElement The new ContactPerson-element.
355
     */
356
    public function toXML(DOMElement $parent = null): DOMElement
357
    {
358
        $e = $this->instantiateParentElement($parent);
359
360
        $e->setAttribute('contactType', $this->contactType);
361
362
        foreach ($this->getAttributesNS() as $attr) {
363
            $e->setAttributeNS($attr['namespaceURI'], $attr['qualifiedName'], $attr['value']);
364
        }
365
366
        if ($this->Extensions !== null) {
367
            $this->Extensions->toXML($e);
368
        }
369
370
        if ($this->Company !== null) {
371
            $this->Company->toXML($e);
372
        }
373
374
        if ($this->GivenName !== null) {
375
            $this->GivenName->toXML($e);
376
        }
377
        if ($this->SurName !== null) {
378
            $this->SurName->toXML($e);
379
        }
380
381
        foreach ($this->EmailAddresses as $mail) {
382
            $mail->toXML($e);
383
        }
384
385
        foreach ($this->TelephoneNumbers as $telephone) {
386
            $telephone->toXML($e);
387
        }
388
389
        return $e;
390
    }
391
392
393
    /**
394
     * Create a class from an array
395
     *
396
     * @param array $data
397
     * @return self
398
     */
399
    public static function fromArray(array $data): object
400
    {
401
        Assert::keyExists($data, 'ContactType');
402
403
        $ContactType = $data['ContactType'];
404
        $Company = isset($data['Company']) ? new Company($data['Company']) : null;
405
        $GivenName = isset($data['GivenName']) ? new GivenName($data['GivenName']) : null;
406
        $SurName = isset($data['SurName']) ? new SurName($data['SurName']) : null;
407
        $Extensions = $data['Extensions'] ?? null;
408
409
        $EmailAddresses = [];
410
        foreach ($data['EmailAddresses'] as $mail) {
411
            $EmailAddresses[] = new EmailAddress($mail);
412
        }
413
414
        $TelephoneNumbers = [];
415
        foreach ($data['TelephoneNumbers'] as $telephone) {
416
            $TelephoneNumbers[] = new TelephoneNumber($telephone);
417
        }
418
419
        // Anything after this should be (namespaced) attributes
420
        unset(
421
            $data['ContactType'],
422
            $data['Company'],
423
            $data['GivenName'],
424
            $data['SurName'],
425
            $data['Extensions'],
426
            $data['EmailAddresses'],
427
            $data['TelephoneNumbers']
428
        );
429
430
        $attributes = [];
431
        foreach ($data as $ns => $attribute) {
432
            $name = array_key_first($attribute);
433
            $value = $attribute[$name];
434
435
            $doc = DOMDocumentFactory::create();
436
            $elt = $doc->createElement("placeholder");
437
            $elt->setAttributeNS($ns, $name, $value);
438
439
            $attributes[] = $elt->getAttributeNode($name);
440
        }
441
442
        return new self(
443
            $ContactType,
444
            $Company,
445
            $GivenName,
446
            $SurName,
447
            $Extensions,
448
            $EmailAddresses,
449
            $TelephoneNumbers,
450
            $attributes
451
        );
452
    }
453
454
455
    /**
456
     * Create an array from this class
457
     *
458
     * @return array
459
     */
460
    public function toArray(): array
461
    {
462
        $data = [
463
            'ContactType' => $this->contactType,
464
            'Company' => $this->Company->getContent(),
0 ignored issues
show
Bug introduced by
The method getContent() does not exist on null. ( Ignorable by Annotation )

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

464
            'Company' => $this->Company->/** @scrutinizer ignore-call */ getContent(),

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
465
            'GivenName' => $this->GivenName->getContent(),
0 ignored issues
show
Bug introduced by
The method getContent() does not exist on null. ( Ignorable by Annotation )

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

465
            'GivenName' => $this->GivenName->/** @scrutinizer ignore-call */ getContent(),

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
466
            'SurName' => $this->SurName->getContent(),
0 ignored issues
show
Bug introduced by
The method getContent() does not exist on null. ( Ignorable by Annotation )

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

466
            'SurName' => $this->SurName->/** @scrutinizer ignore-call */ getContent(),

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
467
            'EmailAddresses' => $this->EmailAddresses,
468
            'TelephoneNumbers' => $this->TelephoneNumbers,
469
            'EmailAddresses' => [],
470
            'TelephoneNumbers' => [],
471
            'Extensions' => $this->Extensions,
472
        ];
473
474
        foreach ($this->EmailAddresses as $mail) {
475
            $data['EmailAddresses'] = array_merge($data['EmailAddresses'], $mail->toArray());
476
        }
477
478
        foreach ($this->TelephoneNumbers as $telephone) {
479
            $data['TelephoneNumbers'] = array_merge($data['TelephoneNumbers'], $telephone->toArray());
480
        }
481
482
        foreach ($this->getAttributesNS() as $a) {
483
            $data[$a['namespaceURI']] = [$a['qualifiedName'] => $a['value']];
484
        }
485
486
        return $data;
487
    }
488
}
489