4
4
5
5
namespace GeekCell \Ddd \Domain ;
6
6
7
+ use ArrayAccess ;
7
8
use ArrayIterator ;
8
9
use Assert ;
9
10
use Countable ;
10
11
use IteratorAggregate ;
11
12
use Traversable ;
12
13
13
- class Collection implements Countable, IteratorAggregate
14
+ class Collection implements ArrayAccess, Countable, IteratorAggregate
14
15
{
15
16
/**
16
17
* @template T of object
17
18
* @extends IteratorAggregate<T>
18
19
*
19
20
* @param T[] $items
20
21
* @param class-string<T> $itemType
22
+ *
23
+ * @throws Assert\AssertionFailedException
21
24
*/
22
25
final public function __construct (
23
26
private readonly array $ items = [],
24
- ?string $ itemType = null ,
27
+ private ?string $ itemType = null ,
25
28
) {
26
29
if ($ itemType !== null ) {
27
30
Assert \Assertion::allIsInstanceOf ($ items , $ itemType );
28
31
}
29
32
}
30
33
34
+ /**
35
+ * Add one or more items to the collection. It **does not** modify the
36
+ * current collection, but returns a new one.
37
+ *
38
+ * @param mixed $item One or more items to add to the collection.
39
+ * @return static
40
+ *
41
+ * @throws Assert\AssertionFailedException
42
+ */
43
+ public function add (mixed $ item ): static
44
+ {
45
+ if (!is_array ($ item )) {
46
+ $ item = [$ item ];
47
+ }
48
+
49
+ if ($ this ->itemType !== null ) {
50
+ Assert \Assertion::allIsInstanceOf ($ item , $ this ->itemType );
51
+ }
52
+
53
+ return new static ([...$ this ->items , ...$ item ], $ this ->itemType );
54
+ }
55
+
56
+ /**
57
+ * Filter the collection using the given callback. It **does not** modify
58
+ * the current collection, but returns a new one.
59
+ *
60
+ * @param callable $callback The callback to use for filtering.
61
+ * @return static
62
+ */
63
+ public function filter (callable $ callback ): static
64
+ {
65
+ return new static (
66
+ array_filter ($ this ->items , $ callback ),
67
+ $ this ->itemType ,
68
+ );
69
+ }
70
+
71
+ /**
72
+ * Map the collection using the given callback. It **does not** modify
73
+ * the current collection, but returns a new one.
74
+ *
75
+ * @param callable $callback The callback to use for mapping.
76
+ * @param bool $inferTypes Whether to infer the type of the items in the
77
+ * collection based on the first item in the
78
+ * mapping result. Defaults to `true`.
79
+ *
80
+ * @return static
81
+ */
82
+ public function map (callable $ callback , bool $ inferTypes = true ): static
83
+ {
84
+ $ mapResult = array_map ($ callback , $ this ->items );
85
+ $ firstItem = reset ($ mapResult );
86
+
87
+ if ($ firstItem === false || !is_object ($ firstItem )) {
88
+ return new static ($ mapResult );
89
+ }
90
+
91
+ if ($ inferTypes && $ this ->itemType !== null ) {
92
+ return new static ($ mapResult , get_class ($ firstItem ));
93
+ }
94
+
95
+ return new static ($ mapResult );
96
+ }
97
+
98
+ /**
99
+ * Reduce the collection using the given callback.
100
+ *
101
+ * @param callable $callback The callback to use for reducing.
102
+ * @param mixed $initial The initial value to use for reducing.
103
+ *
104
+ * @return mixed
105
+ */
106
+ public function reduce (callable $ callback , mixed $ initial = null ): mixed
107
+ {
108
+ return array_reduce ($ this ->items , $ callback , $ initial );
109
+ }
110
+
31
111
/**
32
112
* @inheritDoc
33
113
*/
34
- public function getIterator ( ): Traversable
114
+ public function offsetExists ( mixed $ offset ): bool
35
115
{
36
- return new ArrayIterator ($ this ->items );
116
+ if (!is_int ($ offset )) {
117
+ return false ;
118
+ }
119
+
120
+ return isset ($ this ->items [$ offset ]);
121
+ }
122
+
123
+ /**
124
+ * @inheritDoc
125
+ */
126
+ public function offsetGet (mixed $ offset ): mixed
127
+ {
128
+ if (!$ this ->offsetExists ($ offset )) {
129
+ return null ;
130
+ }
131
+
132
+ return $ this ->items [$ offset ];
133
+ }
134
+
135
+ /**
136
+ * This method is not supported since it would break the immutability of the
137
+ * collection.
138
+ *
139
+ * @inheritDoc
140
+ */
141
+ public function offsetSet (mixed $ offset , mixed $ value ): void
142
+ {
143
+ // Unsupported since it would break the immutability of the collection.
144
+ }
145
+
146
+ /**
147
+ * This method is not supported since it would break the immutability of the
148
+ * collection.
149
+ *
150
+ * @inheritDoc
151
+ */
152
+ public function offsetUnset (mixed $ offset ): void
153
+ {
154
+ // Unsupported since it would break the immutability of the collection.
37
155
}
38
156
39
157
/**
@@ -43,4 +161,12 @@ public function count(): int
43
161
{
44
162
return count ($ this ->items );
45
163
}
164
+
165
+ /**
166
+ * @inheritDoc
167
+ */
168
+ public function getIterator (): Traversable
169
+ {
170
+ return new ArrayIterator ($ this ->items );
171
+ }
46
172
}
0 commit comments