Passed
Branch feature/php83 (cb5cde)
by Tim
14:20
created

ContactPerson   A

Complexity

Total Complexity 34

Size/Duplication

Total Lines 343
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 129
dl 0
loc 343
rs 9.68
c 0
b 0
f 0
wmc 34
1
<?php
2
3
declare(strict_types=1);
4
5
namespace SimpleSAML\SAML2\XML\md;
6
7
use DOMElement;
8
use SimpleSAML\SAML2\Assert\Assert;
9
use SimpleSAML\SAML2\Exception\ArrayValidationException;
10
use SimpleSAML\SAML2\Type\SAMLStringValue;
11
use SimpleSAML\SAML2\XML\ExtendableElementTrait;
12
use SimpleSAML\XML\ArrayizableElementInterface;
13
use SimpleSAML\XML\Attribute as XMLAttribute;
14
use SimpleSAML\XML\Constants as C;
15
use SimpleSAML\XML\ExtendableAttributesTrait;
16
use SimpleSAML\XML\SchemaValidatableElementInterface;
17
use SimpleSAML\XML\SchemaValidatableElementTrait;
18
use SimpleSAML\XMLSchema\Exception\InvalidDOMElementException;
19
use SimpleSAML\XMLSchema\Exception\TooManyElementsException;
20
use SimpleSAML\XMLSchema\XML\Constants\NS;
21
22
use function array_change_key_case;
23
use function array_filter;
24
use function array_key_exists;
25
use function array_keys;
26
use function array_map;
27
use function array_pop;
28
use function count;
29
30
/**
31
 * Class representing SAML 2 ContactPerson.
32
 *
33
 * @package simplesamlphp/saml2
34
 */
35
final class ContactPerson extends AbstractMdElement implements
36
    ArrayizableElementInterface,
37
    SchemaValidatableElementInterface
38
{
39
    use ExtendableAttributesTrait;
40
    use ExtendableElementTrait;
41
    use SchemaValidatableElementTrait;
42
43
44
    /** The namespace-attribute for the xs:anyAttribute element */
45
    public const string XS_ANY_ATTR_NAMESPACE = NS::OTHER;
0 ignored issues
show
Bug introduced by
A parse error occurred: Syntax error, unexpected T_STRING, expecting '=' on line 45 at column 24
Loading history...
46
47
    /**
48
     * The several different contact types as defined per specification
49
     *
50
     * @var string[]
51
     */
52
    public const array CONTACT_TYPES = [
53
        'technical',
54
        'support',
55
        'administrative',
56
        'billing',
57
        'other',
58
    ];
59
60
61
    /**
62
     * ContactPerson constructor.
63
     *
64
     * @param \SimpleSAML\SAML2\Type\SAMLStringValue $contactType
65
     * @param \SimpleSAML\SAML2\XML\md\Company|null $company
66
     * @param \SimpleSAML\SAML2\XML\md\GivenName|null $givenName
67
     * @param \SimpleSAML\SAML2\XML\md\SurName|null $surName
68
     * @param \SimpleSAML\SAML2\XML\md\Extensions|null $extensions
69
     * @param \SimpleSAML\SAML2\XML\md\EmailAddress[] $emailAddress
70
     * @param \SimpleSAML\SAML2\XML\md\TelephoneNumber[] $telephoneNumber
71
     * @param \SimpleSAML\XML\Attribute[] $namespacedAttribute
72
     */
73
    public function __construct(
74
        protected SAMLStringValue $contactType,
75
        protected ?Company $company = null,
76
        protected ?GivenName $givenName = null,
77
        protected ?SurName $surName = null,
78
        ?Extensions $extensions = null,
79
        protected array $emailAddress = [],
80
        protected array $telephoneNumber = [],
81
        array $namespacedAttribute = [],
82
    ) {
83
        Assert::oneOf($contactType->getValue(), self::CONTACT_TYPES);
84
        Assert::maxCount($emailAddress, C::UNBOUNDED_LIMIT);
85
        Assert::allIsInstanceOf($emailAddress, EmailAddress::class);
86
        Assert::maxCount($telephoneNumber, C::UNBOUNDED_LIMIT);
87
        Assert::allIsInstanceOf($telephoneNumber, TelephoneNumber::class);
88
89
        $this->setExtensions($extensions);
90
        $this->setAttributesNS($namespacedAttribute);
91
    }
92
93
94
    /**
95
     * Collect the value of the contactType-property
96
     *
97
     * @return \SimpleSAML\SAML2\Type\SAMLStringValue
98
     */
99
    public function getContactType(): SAMLStringValue
100
    {
101
        return $this->contactType;
102
    }
103
104
105
    /**
106
     * Collect the value of the Company-property
107
     *
108
     * @return \SimpleSAML\SAML2\XML\md\Company|null
109
     */
110
    public function getCompany(): ?Company
111
    {
112
        return $this->company;
113
    }
114
115
116
    /**
117
     * Collect the value of the GivenName-property
118
     *
119
     * @return \SimpleSAML\SAML2\XML\md\GivenName|null
120
     */
121
    public function getGivenName(): ?GivenName
122
    {
123
        return $this->givenName;
124
    }
125
126
127
    /**
128
     * Collect the value of the SurName-property
129
     *
130
     * @return \SimpleSAML\SAML2\XML\md\SurName|null
131
     */
132
    public function getSurName(): ?SurName
133
    {
134
        return $this->surName;
135
    }
136
137
138
    /**
139
     * Collect the value of the EmailAddress-property.
140
     *
141
     * @return \SimpleSAML\SAML2\XML\md\EmailAddress[]
142
     */
143
    public function getEmailAddress(): array
144
    {
145
        return $this->emailAddress;
146
    }
147
148
149
    /**
150
     * Collect the value of the TelephoneNumber property
151
     *
152
     * @return \SimpleSAML\SAML2\XML\md\TelephoneNumber[]
153
     */
154
    public function getTelephoneNumber(): array
155
    {
156
        return $this->telephoneNumber;
157
    }
158
159
160
    /**
161
     * Initialize a ContactPerson element.
162
     *
163
     * @throws \SimpleSAML\XMLSchema\Exception\InvalidDOMElementException
164
     *   if the qualified name of the supplied element is wrong
165
     * @throws \SimpleSAML\XMLSchema\Exception\MissingAttributeException
166
     *   if the supplied element is missing one of the mandatory attributes
167
     * @throws \SimpleSAML\XMLSchema\Exception\TooManyElementsException
168
     *   if too many child-elements of a type are specified
169
     */
170
    public static function fromXML(DOMElement $xml): static
171
    {
172
        Assert::same($xml->localName, 'ContactPerson', InvalidDOMElementException::class);
173
        Assert::same($xml->namespaceURI, ContactPerson::NS, InvalidDOMElementException::class);
174
175
        $contactType = self::getAttribute($xml, 'contactType', SAMLStringValue::class);
176
177
        $company = Company::getChildrenOfClass($xml);
178
        Assert::maxCount($company, 1, 'More than one Company in md:ContactPerson');
179
180
        $givenName = GivenName::getChildrenOfClass($xml);
181
        Assert::maxCount($givenName, 1, 'More than one GivenName in md:ContactPerson');
182
183
        $surName = SurName::getChildrenOfClass($xml);
184
        Assert::maxCount($surName, 1, 'More than one SurName in md:ContactPerson');
185
186
        $email = EmailAddress::getChildrenOfClass($xml);
187
        $telephone = TelephoneNumber::getChildrenOfClass($xml);
188
189
        $extensions = Extensions::getChildrenOfClass($xml);
190
        Assert::maxCount($extensions, 1, 'Only one md:Extensions element is allowed.', TooManyElementsException::class);
191
192
        return new static(
193
            $contactType,
194
            array_pop($company),
195
            array_pop($givenName),
196
            array_pop($surName),
197
            (count($extensions) === 1) ? $extensions[0] : null,
198
            $email,
199
            $telephone,
200
            self::getAttributesNSFromXML($xml),
201
        );
202
    }
203
204
205
    /**
206
     * Convert this ContactPerson to XML.
207
     */
208
    public function toXML(?DOMElement $parent = null): DOMElement
209
    {
210
        $e = $this->instantiateParentElement($parent);
211
212
        $e->setAttribute('contactType', $this->getContactType()->getValue());
213
214
        foreach ($this->getAttributesNS() as $attr) {
215
            $attr->toXML($e);
216
        }
217
218
        $this->getExtensions()?->toXML($e);
219
        $this->getCompany()?->toXML($e);
220
        $this->getGivenName()?->toXML($e);
221
        $this->getSurName()?->toXML($e);
222
223
        foreach ($this->getEmailAddress() as $mail) {
224
            $mail->toXML($e);
225
        }
226
227
        foreach ($this->getTelephoneNumber() as $telephone) {
228
            $telephone->toXML($e);
229
        }
230
231
        return $e;
232
    }
233
234
235
    /**
236
     * Create a class from an array
237
     *
238
     * @param array{
239
     *   'contactType': string,
240
     *   'Company'?: string,
241
     *   'GivenName'?: string,
242
     *   'SurName'?: string,
243
     *   'Extensions'?: array,
244
     *   'EmailAddress'?: array,
245
     *   'TelephoneNumber'?: array,
246
     *   'attributes'?: array,
247
     * } $data
248
     */
249
    public static function fromArray(array $data): static
250
    {
251
        $data = self::processArrayContents($data);
252
253
        return new static(
254
            SAMLStringValue::fromString($data['contactType']),
255
            $data['Company'] !== null ? new Company(SAMLStringValue::fromString($data['Company'])) : null,
256
            $data['GivenName'] !== null ? new GivenName(SAMLStringValue::fromString($data['GivenName'])) : null,
257
            $data['SurName'] !== null ? new SurName(SAMLStringValue::fromString($data['SurName'])) : null,
258
            $data['Extensions'] ?? null,
259
            $data['EmailAddress'] !== null
260
                ? array_map([EmailAddress::class, 'fromArray'], [$data['EmailAddress']])
261
                : [],
262
            $data['TelephoneNumber'] !== null
263
                ? array_map([TelephoneNumber::class, 'fromArray'], [$data['TelephoneNumber']])
264
                : [],
265
            $data['attributes'] ?? [],
266
        );
267
    }
268
269
270
    /**
271
     * Validates an array representation of this object and returns the same array with
272
     * rationalized keys (casing) and parsed sub-elements.
273
     *
274
     * @param array{
275
     *   'contactType': string,
276
     *   'Company'?: string,
277
     *   'GivenName'?: string,
278
     *   'SurName'?: string,
279
     *   'Extensions'?: array,
280
     *   'EmailAddress'?: array,
281
     *   'TelephoneNumber'?: array,
282
     *   'attributes'?: array,
283
     * } $data
284
     * @return array{
285
     *   'contactType': string,
286
     *   'Company'?: string,
287
     *   'GivenName'?: string,
288
     *   'SurName'?: string,
289
     *   'Extensions'?: array,
290
     *   'EmailAddress'?: array,
291
     *   'TelephoneNumber'?: array,
292
     *   'attributes'?: array,
293
     * }
294
     */
295
    private static function processArrayContents(array $data): array
296
    {
297
        $data = array_change_key_case($data, CASE_LOWER);
298
299
        // Make sure the array keys are known for this kind of object
300
        Assert::allOneOf(
301
            array_keys($data),
302
            [
303
                'contacttype',
304
                'company',
305
                'givenname',
306
                'surname',
307
                'emailaddress',
308
                'telephonenumber',
309
                'extensions',
310
                'attributes',
311
            ],
312
            ArrayValidationException::class,
313
        );
314
315
        Assert::keyExists($data, 'contacttype', ArrayValidationException::class);
316
        Assert::string($data['contacttype'], ArrayValidationException::class);
317
318
        $retval = ['contactType' => $data['contacttype']];
319
320
        if (array_key_exists('company', $data)) {
321
            Assert::string($data['company'], ArrayValidationException::class);
322
            $retval['Company'] = $data['company'];
323
        }
324
325
        if (array_key_exists('givenname', $data)) {
326
            Assert::string($data['givenname'], ArrayValidationException::class);
327
            $retval['GivenName'] = $data['givenname'];
328
        }
329
330
        if (array_key_exists('surname', $data)) {
331
            Assert::string($data['surname'], ArrayValidationException::class);
332
            $retval['SurName'] = $data['surname'];
333
        }
334
335
        if (array_key_exists('emailaddress', $data)) {
336
            Assert::isArray($data['emailaddress'], ArrayValidationException::class);
337
            Assert::allString($data['emailaddress'], ArrayValidationException::class);
338
            foreach ($data['emailaddress'] as $email) {
339
                $retval['EmailAddress'][] = $email;
340
            }
341
        }
342
343
        if (array_key_exists('telephonenumber', $data)) {
344
            Assert::isArray($data['telephonenumber'], ArrayValidationException::class);
345
            Assert::allString($data['telephonenumber'], ArrayValidationException::class);
346
            foreach ($data['telephonenumber'] as $telephone) {
347
                $retval['TelephoneNumber'][] = $telephone;
348
            }
349
        }
350
351
        if (array_key_exists('extensions', $data)) {
352
            Assert::isArray($data['extensions'], ArrayValidationException::class);
353
            $retval['Extensions'] = new Extensions($data['extensions']);
354
        }
355
356
        if (array_key_exists('attributes', $data)) {
357
            Assert::isArray($data['attributes'], ArrayValidationException::class);
358
            Assert::allIsArray($data['attributes'], ArrayValidationException::class);
359
            foreach ($data['attributes'] as $i => $attr) {
360
                $retval['attributes'][] = XMLAttribute::fromArray($attr);
361
            }
362
        }
363
364
        return $retval;
365
    }
366
367
368
    /**
369
     * Create an array from this class
370
     *
371
     * @return array{
372
     *   'contactType': string,
373
     *   'Company'?: string,
374
     *   'GivenName'?: string,
375
     *   'SurName'?: string,
376
     *   'Extensions'?: array,
377
     *   'EmailAddress'?: array,
378
     *   'TelephoneNumber'?: array,
379
     *   'attributes'?: array,
380
     * }
381
     */
382
    public function toArray(): array
383
    {
384
        $data = [
385
            'ContactType' => $this->getContactType()->getValue(),
386
            'Company' => $this->getCompany()?->getContent()->getValue(),
387
            'GivenName' => $this->getGivenName()?->getContent()->getValue(),
388
            'SurName' => $this->getSurName()?->getContent()->getValue(),
389
            'EmailAddress' => [],
390
            'TelephoneNumber' => [],
391
            'Extensions' => $this->Extensions?->getList(),
392
            'attributes' => [],
393
        ];
394
395
        foreach ($this->getEmailAddress() as $mail) {
396
            $data['EmailAddress'] = array_merge($data['EmailAddress'], $mail->toArray());
397
        }
398
399
        foreach ($this->getTelephoneNumber() as $telephone) {
400
            $data['TelephoneNumber'] = array_merge($data['TelephoneNumber'], $telephone->toArray());
401
        }
402
403
        foreach ($this->getAttributesNS() as $attr) {
404
            $data['attributes'][] = $attr->toArray();
405
        }
406
407
        return array_filter($data);
408
    }
409
}
410