Passed
Pull Request — master (#267)
by Tim
07:30
created

ContactPerson::getStringElements()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 2
dl 0
loc 10
rs 10
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 InvalidArgumentException;
10
use SimpleSAML\Assert\Assert;
11
use SimpleSAML\SAML2\Constants;
12
use SimpleSAML\SAML2\XML\ExtendableElementTrait;
13
use SimpleSAML\XML\DOMDocumentFactory;
14
use SimpleSAML\XML\Exception\InvalidDOMElementException;
15
use SimpleSAML\XML\Exception\TooManyElementsException;
16
use SimpleSAML\XML\ExtendableAttributesTrait;
17
use SimpleSAML\XML\Utils as XMLUtils;
18
19
use function array_map;
20
use function array_pop;
21
use function count;
22
use function filter_var;
23
use function preg_replace;
24
use function var_export;
25
26
/**
27
 * Class representing SAML 2 ContactPerson.
28
 *
29
 * @package simplesamlphp/saml2
30
 */
31
final class ContactPerson extends AbstractMdElement
32
{
33
    use ExtendableElementTrait;
34
    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...
35
36
    /**
37
     * The contact type.
38
     *
39
     * @var string
40
     */
41
    protected string $contactType;
42
43
    /**
44
     * The Company of this contact.
45
     *
46
     * @var \SimpleSAML\SAML2\XML\md\Company|null
47
     */
48
    protected ?Company $Company = null;
49
50
    /**
51
     * The GivenName of this contact.
52
     *
53
     * @var \SimpleSAML\SAML2\XML\md\GivenName|null
54
     */
55
    protected ?GivenName $GivenName = null;
56
57
    /**
58
     * The SurName of this contact.
59
     *
60
     * @var \SimpleSAML\SAML2\XML\md\SurName|null
61
     */
62
    protected ?SurName $SurName = null;
63
64
    /**
65
     * The EmailAddresses of this contact.
66
     *
67
     * @var string[]
68
     */
69
    protected array $EmailAddresses = [];
70
71
    /**
72
     * The TelephoneNumbers of this contact.
73
     *
74
     * @var string[]
75
     */
76
    protected array $TelephoneNumbers = [];
77
78
79
    /**
80
     * ContactPerson constructor.
81
     *
82
     * @param string                                    $contactType
83
     * @param \SimpleSAML\SAML2\XML\md\Company|null     $company
84
     * @param \SimpleSAML\SAML2\XML\md\GivenName|null   $givenName
85
     * @param \SimpleSAML\SAML2\XML\md\SurName|null     $surName
86
     * @param \SimpleSAML\SAML2\XML\md\Extensions|null  $extensions
87
     * @param string[]                                  $email
88
     * @param string[]                                  $telephone
89
     * @param \DOMAttr[]                                $namespacedAttributes
90
     */
91
    public function __construct(
92
        string $contactType,
93
        ?Company $company = null,
94
        ?GivenName $givenName = null,
95
        ?SurName $surName = null,
96
        ?Extensions $extensions = null,
97
        array $email = [],
98
        array $telephone = [],
99
        array $namespacedAttributes = []
100
    ) {
101
        $this->setContactType($contactType);
102
        $this->setCompany($company);
103
        $this->setGivenName($givenName);
104
        $this->setSurName($surName);
105
        $this->setEmailAddresses($email);
106
        $this->setTelephoneNumbers($telephone);
107
        $this->setExtensions($extensions);
108
        $this->setAttributesNS($namespacedAttributes);
109
    }
110
111
112
    /**
113
     * Collect the value of the contactType-property
114
     *
115
     * @return string
116
     */
117
    public function getContactType(): string
118
    {
119
        return $this->contactType;
120
    }
121
122
123
    /**
124
     * Set the value of the contactType-property
125
     *
126
     * @param string $contactType
127
     * @throws \SimpleSAML\Assert\AssertionFailedException if $contactType is not one of the predefined values
128
     */
129
    protected function setContactType(string $contactType): void
130
    {
131
        Assert::oneOf($contactType, ['technical', 'support', 'administrative', 'billing', 'other']);
132
        $this->contactType = $contactType;
133
    }
134
135
136
    /**
137
     * Collect the value of the Company-property
138
     *
139
     * @return \SimpleSAML\SAML2\XML\md\Company|null
140
     */
141
    public function getCompany(): ?Company
142
    {
143
        return $this->Company;
144
    }
145
146
147
    /**
148
     * Set the value of the Company-property
149
     *
150
     * @param \SimpleSAML\SAML2\XML\md\Company|null $company
151
     */
152
    protected function setCompany(?Company $company): void
153
    {
154
        $this->Company = $company;
155
    }
156
157
158
    /**
159
     * Collect the value of the GivenName-property
160
     *
161
     * @return \SimpleSAML\SAML2\XML\md\GivenName|null
162
     */
163
    public function getGivenName(): ?GivenName
164
    {
165
        return $this->GivenName;
166
    }
167
168
169
    /**
170
     * Set the value of the GivenName-property
171
     *
172
     * @param \SimpleSAML\SAML2\XML\md\GivenName|null $givenName
173
     */
174
    protected function setGivenName(?GivenName $givenName): void
175
    {
176
        $this->GivenName = $givenName;
177
    }
178
179
180
    /**
181
     * Collect the value of the SurName-property
182
     *
183
     * @return \SimpleSAML\SAML2\XML\md\SurName|null
184
     */
185
    public function getSurName(): ?SurName
186
    {
187
        return $this->SurName;
188
    }
189
190
191
    /**
192
     * Set the value of the SurName-property
193
     *
194
     * @param \SimpleSAML\SAML2\XML\md\SurName|null $surName
195
     */
196
    protected function setSurName(?SurName $surName): void
197
    {
198
        $this->SurName = $surName;
199
    }
200
201
    /**
202
     * Collect the value of the EmailAddress-property.
203
     *
204
     * @return string[]
205
     */
206
    public function getEmailAddresses(): array
207
    {
208
        return $this->EmailAddresses;
209
    }
210
211
    /**
212
     * Remove a "mailto:" prefix on an email address, if present.
213
     * Check the address for syntactical validity. If not, throw an exception.
214
     *
215
     * @param string $emailAddress
216
     * @return string
217
     * @throws \InvalidArgumentException if supplied email address is not valid
218
     */
219
    private function validateEmailAddress(string $emailAddress): string
220
    {
221
        $address = preg_replace('/^mailto:/i', '', $emailAddress);
222
        if (filter_var($address, FILTER_VALIDATE_EMAIL) === false) {
223
            throw new InvalidArgumentException("Invalid email address for ContactPerson: " . var_export($address, true));
224
        }
225
        return $address;
226
    }
227
228
    /**
229
     * Set the value of the EmailAddress-property
230
     *
231
     * @param string[] $emailAddresses
232
     * @throws \SimpleSAML\Assert\AssertionFailedException
233
     */
234
    protected function setEmailAddresses(array $emailAddresses): void
235
    {
236
        $addresses = array_map([$this, 'validateEmailAddress'], $emailAddresses);
237
        Assert::allEmail($addresses, 'Invalid email addresses found.');
238
        $this->EmailAddresses = $addresses;
239
    }
240
241
242
    /**
243
     * Collect the value of the TelephoneNumber property
244
     *
245
     * @return string[]
246
     */
247
    public function getTelephoneNumbers(): array
248
    {
249
        return $this->TelephoneNumbers;
250
    }
251
252
253
    /**
254
     * Set the value of the TelephoneNumber property
255
     *
256
     * @param string[] $telephoneNumbers
257
     * @throws \SimpleSAML\Assert\AssertionFailedException
258
     */
259
    protected function setTelephoneNumbers(array $telephoneNumbers): void
260
    {
261
        Assert::allString($telephoneNumbers, 'Incorrect type for telephone number.');
262
        $this->TelephoneNumbers = $telephoneNumbers;
263
    }
264
265
266
    /**
267
     * Initialize a ContactPerson element.
268
     *
269
     * @param \DOMElement $xml The XML element we should load.
270
     * @return self
271
     *
272
     * @throws \SimpleSAML\XML\Exception\InvalidDOMElementException if the qualified name of the supplied element is wrong
273
     * @throws \SimpleSAML\XML\Exception\MissingAttributeException if the supplied element is missing one of the mandatory attributes
274
     * @throws \SimpleSAML\XML\Exception\TooManyElementsException if too many child-elements of a type are specified
275
     */
276
    public static function fromXML(DOMElement $xml): object
277
    {
278
        Assert::same($xml->localName, 'ContactPerson', InvalidDOMElementException::class);
279
        Assert::same($xml->namespaceURI, ContactPerson::NS, InvalidDOMElementException::class);
280
281
        $contactType = self::getAttribute($xml, 'contactType');
282
283
        $company = Company::getChildrenOfClass($xml);
284
        Assert::maxCount($company, 1, 'More than one Company in md:ContactPerson');
285
286
        $givenName = GivenName::getChildrenOfClass($xml);
287
        Assert::maxCount($givenName, 1, 'More than one GivenName in md:ContactPerson');
288
289
        $surName = SurName::getChildrenOfClass($xml);
290
        Assert::maxCount($surName, 1, 'More than one SurName in md:ContactPerson');
291
292
        $email = EmailAddress::getChildrenOfClass($xml);
0 ignored issues
show
Bug introduced by
The type SimpleSAML\SAML2\XML\md\EmailAddress was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
293
        $telephone = TelephoneNumber::getChildrenOfClass($xml);
0 ignored issues
show
Bug introduced by
The type SimpleSAML\SAML2\XML\md\TelephoneNumber was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
294
        $extensions = Extensions::getChildrenOfClass($xml);
295
        Assert::maxCount($extensions, 1, 'Only one md:Extensions element is allowed.', TooManyElementsException::class);
296
297
        return new self(
298
            $contactType,
299
            array_pop($company),
300
            array_pop($givenName),
301
            array_pop($surName),
302
            (count($extensions) === 1) ? $extensions[0] : null,
303
            $email,
304
            $telephone,
305
            self::getAttributesNSFromXML($xml)
306
        );
307
    }
308
309
310
    /**
311
     * Convert this ContactPerson to XML.
312
     *
313
     * @param \DOMElement|null $parent The element we should add this contact to.
314
     *
315
     * @return \DOMElement The new ContactPerson-element.
316
     */
317
    public function toXML(DOMElement $parent = null): DOMElement
318
    {
319
        $e = $this->instantiateParentElement($parent);
320
321
        $e->setAttribute('contactType', $this->contactType);
322
323
        foreach ($this->getAttributesNS() as $attr) {
324
            $e->setAttributeNS($attr['namespaceURI'], $attr['qualifiedName'], $attr['value']);
325
        }
326
327
        if ($this->Extensions !== null) {
328
            $this->Extensions->toXML($e);
329
        }
330
331
        if ($this->Company !== null) {
332
            $this->Company->toXML($e);
333
        }
334
335
        if ($this->GivenName !== null) {
336
            $this->GivenName->toXML($e);
337
        }
338
        if ($this->SurName !== null) {
339
            $this->SurName->toXML($e);
340
        }
341
342
        $addresses = preg_filter('/^/', 'mailto:', $this->EmailAddresses);
343
        XMLUtils::addStrings($e, Constants::NS_MD, 'md:EmailAddress', false, $addresses);
0 ignored issues
show
Bug introduced by
It seems like $addresses can also be of type null and string; however, parameter $values of SimpleSAML\XML\Utils::addStrings() does only seem to accept array, 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

343
        XMLUtils::addStrings($e, Constants::NS_MD, 'md:EmailAddress', false, /** @scrutinizer ignore-type */ $addresses);
Loading history...
344
345
        XMLUtils::addStrings($e, Constants::NS_MD, 'md:TelephoneNumber', false, $this->TelephoneNumbers);
346
347
        return $e;
348
    }
349
350
351
    /**
352
     * Create a class from an array
353
     *
354
     * @param array $data
355
     * @return self
356
     */
357
    public static function fromArray(array $data): object
358
    {
359
        Assert::keyExists($data, 'ContactType');
360
361
        $ContactType = $data['ContactType'];
362
        $Company = isset($data['Company']) ? new Company($data['Company']) : null;
363
        $GivenName = isset($data['GivenName']) ? new GivenName($data['GivenName']) : null;
364
        $SurName = isset($data['SurName']) ? new SurName($data['SurName']) : null;
365
        $Extensions = $data['Extensions'] ?? null;
366
        $EmailAddresses = $data['EmailAddresses'] ?? [];
367
        $TelephoneNumbers = $data['TelephoneNumbers'] ?? [];
368
369
        // Anything after this should be (namespaced) attributes
370
        unset(
371
            $data['ContactType'],
372
            $data['Company'],
373
            $data['GivenName'],
374
            $data['SurName'],
375
            $data['Extensions'],
376
            $data['EmailAddresses'],
377
            $data['TelephoneNumbers']
378
        );
379
380
        $attributes = [];
381
        foreach ($data as $ns => $attribute) {
382
            $name = array_key_first($attribute);
383
            $value = $attribute[$name];
384
385
            $doc = DOMDocumentFactory::create();
386
            $elt = $doc->createElement("placeholder");
387
            $elt->setAttributeNS($ns, $name, $value);
388
389
            $attributes[] = $elt->getAttributeNode($name);
390
        }
391
392
        return new self(
393
            $ContactType,
394
            $Company,
395
            $GivenName,
396
            $SurName,
397
            $Extensions,
398
            $EmailAddresses,
399
            $TelephoneNumbers,
400
            $attributes
401
        );
402
    }
403
404
405
    /**
406
     * Create an array from this class
407
     *
408
     * @return array
409
     */
410
    public function toArray(): array
411
    {
412
        $data = [
413
            'ContactType' => $this->contactType,
414
            '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

414
            '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...
415
            '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

415
            '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...
416
            '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

416
            '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...
417
            'EmailAddresses' => $this->EmailAddresses,
418
            'TelephoneNumbers' => $this->TelephoneNumbers,
419
            'Extensions' => $this->Extensions,
420
        ];
421
422
        foreach ($this->getAttributesNS() as $a) {
423
            $data[$a['namespaceURI']] = [$a['qualifiedName'] => $a['value']];
424
        }
425
426
        return $data;
427
    }
428
}
429