AbstractDTOCollection::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 3
c 1
b 0
f 0
dl 0
loc 7
rs 10
cc 1
nc 1
nop 1
1
<?php
2
3
/*
4
 * dto (https://github.com/phpgears/dto).
5
 * General purpose immutable Data Transfer Objects for PHP.
6
 *
7
 * @license MIT
8
 * @link https://github.com/phpgears/dto
9
 * @author Julián Gutiérrez <[email protected]>
10
 */
11
12
declare(strict_types=1);
13
14
namespace Gears\DTO;
15
16
use Gears\DTO\Exception\DTOException;
17
use Gears\DTO\Exception\InvalidCollectionTypeException;
18
use Gears\DTO\Exception\InvalidParameterException;
19
use Gears\Immutability\ImmutabilityBehaviour;
20
21
/**
22
 * Abstract immutable Data Transfer Object collection.
23
 */
24
abstract class AbstractDTOCollection implements DTOCollection
25
{
26
    use ImmutabilityBehaviour, PayloadBehaviour {
27
        PayloadBehaviour::__call insteadof ImmutabilityBehaviour;
28
        __call as private payloadCall;
29
    }
30
31
    /**
32
     * DTOCollection constructor.
33
     *
34
     * @param array<string, mixed> $elements
35
     */
36
    final protected function __construct(array $elements)
37
    {
38
        $this->assertImmutable();
39
40
        $this->verifyElementsType($elements);
41
42
        $this->setPayload(['elements' => \array_values($elements)]);
43
    }
44
45
    /**
46
     * {@inheritdoc}
47
     */
48
    final public static function fromElements(array $elements): DTOCollection
49
    {
50
        return new static($elements);
51
    }
52
53
    /**
54
     * {@inheritdoc}
55
     */
56
    final public function getElements(): \Traversable
57
    {
58
        return $this->get('elements');
59
    }
60
61
    /**
62
     * {@inheritdoc}
63
     */
64
    final public function getIterator(): \Traversable
65
    {
66
        return $this->get('elements');
67
    }
68
69
    /**
70
     * @return string[]
71
     */
72
    final public function __sleep(): array
73
    {
74
        throw new DTOException(\sprintf('DTO collection "%s" cannot be serialized.', static::class));
75
    }
76
77
    final public function __wakeup(): void
78
    {
79
        throw new DTOException(\sprintf('DTO collection "%s" cannot be unserialized.', static::class));
80
    }
81
82
    /**
83
     * @return array<string, mixed>
84
     */
85
    final public function __serialize(): array
86
    {
87
        throw new DTOException(\sprintf('DTO collection "%s" cannot be serialized.', static::class));
88
    }
89
90
    /**
91
     * @param array<string, mixed> $data
92
     *
93
     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
94
     */
95
    final public function __unserialize(array $data): void
96
    {
97
        throw new DTOException(\sprintf('DTO collection "%s" cannot be unserialized.', static::class));
98
    }
99
100
    /**
101
     * Verify collection elements type.
102
     *
103
     * @param mixed[] $elements
104
     *
105
     * @throws InvalidCollectionTypeException
106
     * @throws InvalidParameterException
107
     */
108
    private function verifyElementsType(array $elements): void
109
    {
110
        $allowedType = $this->getAllowedType();
111
        if ($allowedType !== DTO::class
112
            && (!\class_exists($allowedType) || !\in_array(DTO::class, \class_implements($allowedType), true))
113
        ) {
114
            throw new InvalidCollectionTypeException(\sprintf(
115
                'Allowed class type for "%s" should be a "%s", "%s" given.',
116
                static::class,
117
                DTO::class,
118
                $allowedType
119
            ));
120
        }
121
122
        foreach ($elements as $element) {
123
            if (!\is_object($element) || !\is_a($element, $allowedType)) {
124
                throw new InvalidParameterException(\sprintf(
125
                    'All elements of "%s" should be instances of "%s", "%s" given.',
126
                    static::class,
127
                    $allowedType,
128
                    \is_object($element) ? \get_class($element) : \gettype($element)
129
                ));
130
            }
131
        }
132
    }
133
134
    /**
135
     * Transform collection elements for output.
136
     *
137
     * @param mixed[] $elements
138
     *
139
     * @return \Traversable<mixed>
140
     */
141
    final protected function outputElements(array $elements): \Traversable
142
    {
143
        return new \ArrayIterator($elements);
144
    }
145
146
    /**
147
     * Get allowed DTO type for collection elements.
148
     *
149
     * @return string
150
     */
151
    abstract protected function getAllowedType(): string;
152
153
    /**
154
     * {@inheritdoc}
155
     *
156
     * @return string[]
157
     */
158
    final protected function getAllowedInterfaces(): array
159
    {
160
        return [DTOCollection::class];
161
    }
162
}
163