Skip to content

Commit 7249942

Browse files
committed
Allow collections to only contain a certain type
1 parent ed8710e commit 7249942

24 files changed

+259
-13
lines changed

CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
Change Log
22
==========
33

4+
2024-06-10
5+
----------
6+
7+
* Add support for a "typed" collection that contains only values of type
8+
* Set default collection types to SimpleCollection / MutableCollection to allow for typed collections
9+
410
2024-02-24 - 5.4.0
511
------------------
612

CHANGELOG_V5.md

+20
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,23 @@ All features previously marked as deprecated in the 4.X series have been removed
2828

2929
* Keys are typed to `int|string` across the board instead of inferred mixed/string
3030
* `GetWithDotNotation::get()` no longer supports `null` for all values; use `all()` instead
31+
32+
### 5.5.0 addition of type to collections
33+
34+
From 5.5.0, collections can be extended and the "type" set to restrict the collection to a specific type
35+
of values. The type can be any of:
36+
37+
* int
38+
* float
39+
* bool
40+
* string
41+
* scalar
42+
* array
43+
* object or interface class
44+
45+
To accommodate this change, `SimpleCollection` and `MutableCollection` now have the collectionClass set to themselves
46+
to prevent issues of methods like `map()` causing errors due to returning values not supported by the type.
47+
48+
To set the type: extend the collection type you wish to make type specific, and then set the property `$type` to
49+
the type you want the collection to contain. This will typically be a class name, though the standard PHP types
50+
can be used - except for resources.

src/AbstractCollection.php

+18
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,13 @@ abstract class AbstractCollection implements Collection
3939
*/
4040
protected ?string $collectionClass = null;
4141

42+
/**
43+
* The type of values that this collection is restricted to, can use scalar types or class names
44+
*
45+
* @var string|null
46+
*/
47+
protected ?string $type = null;
48+
4249
protected array $items = [];
4350

4451
public static function collect(mixed $items = []): Collection|static
@@ -80,6 +87,7 @@ public static function __set_state($array): object
8087
{
8188
$object = new static();
8289
$object->items = $array['items'];
90+
$object->type = $array['type'];
8391

8492
return $object;
8593
}
@@ -128,6 +136,16 @@ public function setCollectionClass(string $class): void
128136
$this->collectionClass = $class;
129137
}
130138

139+
public function isTyped(): bool
140+
{
141+
return $this->type !== null;
142+
}
143+
144+
public function type(): ?string
145+
{
146+
return $this->type;
147+
}
148+
131149
public function offsetExists($offset): bool
132150
{
133151
return array_key_exists($offset, $this->items);

src/Behaviours/CanAddAndRemoveItems.php

+5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
namespace Somnambulist\Components\Collection\Behaviours;
44

5+
use Somnambulist\Components\Collection\Exceptions\InvalidItemTypeException;
6+
use Somnambulist\Components\Collection\Utils\Value;
7+
58
/**
69
* @property array $items
710
*/
@@ -10,6 +13,8 @@ trait CanAddAndRemoveItems
1013

1114
final public function offsetSet($offset, $value): void
1215
{
16+
Value::assertIsOfType($value, $this->type);
17+
1318
if (null === $offset) {
1419
$this->items[] = $value;
1520
} else {

src/Behaviours/CannotAddDuplicateItems.php

+4
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
namespace Somnambulist\Components\Collection\Behaviours;
44

55
use Somnambulist\Components\Collection\Exceptions\DuplicateItemException;
6+
use Somnambulist\Components\Collection\Exceptions\InvalidItemTypeException;
7+
use Somnambulist\Components\Collection\Utils\Value;
68

79
/**
810
* @property array $items
@@ -12,6 +14,8 @@ trait CannotAddDuplicateItems
1214

1315
final public function offsetSet($offset, $value): void
1416
{
17+
Value::assertIsOfType($value, $this->type);
18+
1519
if ($this->contains($value)) {
1620
throw DuplicateItemException::found($value, array_search($value, $this->items));
1721
}

src/Behaviours/Mutate/AppendOnlyUniqueValues.php

+5-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use Somnambulist\Components\Collection\Contracts\Collection;
66
use Somnambulist\Components\Collection\Exceptions\DuplicateItemException;
7+
use Somnambulist\Components\Collection\Utils\Value;
78
use function array_push;
89

910
/**
@@ -38,6 +39,8 @@ public function add(mixed $value): Collection|static
3839
public function append(mixed ...$value): Collection|static
3940
{
4041
foreach ($value as $item) {
42+
Value::assertIsOfType($item, $this->type);
43+
4144
if ($this->contains($item)) {
4245
throw DuplicateItemException::found($value, $this->keys($item)->first());
4346
}
@@ -50,7 +53,7 @@ public function append(mixed ...$value): Collection|static
5053
}
5154

5255
/**
53-
* Push all of the given items onto the collection.
56+
* Push all the given items onto the collection.
5457
*
5558
* @param iterable $items
5659
*
@@ -59,7 +62,7 @@ public function append(mixed ...$value): Collection|static
5962
public function concat(iterable $items): Collection|static
6063
{
6164
foreach ($items as $item) {
62-
$this->push($item);
65+
$this->append($item);
6366
}
6467

6568
return $this;

src/Behaviours/Mutate/AppendValues.php

+5-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Somnambulist\Components\Collection\Behaviours\Mutate;
44

55
use Somnambulist\Components\Collection\Contracts\Collection;
6+
use Somnambulist\Components\Collection\Utils\Value;
67
use function array_push;
78

89
/**
@@ -36,13 +37,15 @@ public function add(mixed $value): Collection|static
3637
*/
3738
public function append(mixed ...$value): Collection|static
3839
{
40+
Value::assertAllOfType($value, $this->type);
41+
3942
array_push($this->items, ...$value);
4043

4144
return $this;
4245
}
4346

4447
/**
45-
* Push all of the given items onto the collection.
48+
* Push all the given items onto the collection.
4649
*
4750
* @param iterable $items
4851
*
@@ -51,7 +54,7 @@ public function append(mixed ...$value): Collection|static
5154
public function concat(iterable $items): Collection|static
5255
{
5356
foreach ($items as $item) {
54-
$this->push($item);
57+
$this->append($item);
5558
}
5659

5760
return $this;

src/Behaviours/Mutate/CombineOnlyUniqueValues.php

+2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ public function combine(mixed $items): Collection|static
2727
$items = Value::toArray($items);
2828
$unique = array_unique($items);
2929

30+
Value::assertAllOfType($items, $this->type);
31+
3032
if (count($items) !== count($unique)) {
3133
throw DuplicateItemException::preparedValuesContainDuplicates(__FUNCTION__);
3234
}

src/Behaviours/Mutate/CombineValues.php

+5-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ trait CombineValues
2323
*/
2424
public function combine(mixed $items): Collection|static
2525
{
26-
return $this->new(array_combine($this->items, Value::toArray($items)));
26+
$items = Value::toArray($items);
27+
28+
Value::assertAllOfType($items, $this->type);
29+
30+
return $this->new(array_combine($this->items, $items));
2731
}
2832
}

src/Behaviours/Mutate/Fill.php

+5
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Somnambulist\Components\Collection\Behaviours\Mutate;
44

55
use Somnambulist\Components\Collection\Contracts\Collection;
6+
use Somnambulist\Components\Collection\Utils\Value;
67
use function array_fill;
78
use function array_fill_keys;
89

@@ -27,6 +28,8 @@ trait Fill
2728
*/
2829
public function fill(int $start, int $count, mixed $value): Collection|static
2930
{
31+
Value::assertIsOfType($value, $this->type);
32+
3033
return $this->new(array_fill($start, $count, $value));
3134
}
3235

@@ -44,6 +47,8 @@ public function fill(int $start, int $count, mixed $value): Collection|static
4447
*/
4548
public function fillKeysWith(mixed $value): Collection|static
4649
{
50+
Value::assertIsOfType($value, $this->type);
51+
4752
return $this->new(array_fill_keys($this->values()->toArray(), $value));
4853
}
4954
}

src/Behaviours/Mutate/MergeOnlyUniqueValues.php

+2
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ public function merge(mixed $value): Collection|static
2828
$items = Value::toArray($value);
2929
$unique = array_unique($items);
3030

31+
Value::assertAllOfType($items, $this->type);
32+
3133
if (count($items) !== count($unique)) {
3234
throw DuplicateItemException::preparedValuesContainDuplicates(__FUNCTION__);
3335
}

src/Behaviours/Mutate/MergeValues.php

+5-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,11 @@ trait MergeValues
2727
*/
2828
public function merge(mixed $value): Collection|static
2929
{
30-
$this->items = array_merge($this->items, Value::toArray($value));
30+
$value = Value::toArray($value);
31+
32+
Value::assertAllOfType($value, $this->type);
33+
34+
$this->items = array_merge($this->items, $value);
3135

3236
return $this;
3337
}

src/Behaviours/Mutate/Pad.php

+3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Somnambulist\Components\Collection\Behaviours\Mutate;
44

55
use Somnambulist\Components\Collection\Contracts\Collection;
6+
use Somnambulist\Components\Collection\Utils\Value;
67
use function array_pad;
78

89
/**
@@ -23,6 +24,8 @@ trait Pad
2324
*/
2425
public function pad(int $size, mixed $value): Collection|static
2526
{
27+
Value::assertIsOfType($value, $this->type);
28+
2629
$this->items = array_pad($this->items, $size, $value);
2730

2831
return $this;

src/Behaviours/Mutate/PrependOnlyUniqueValues.php

+3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use Somnambulist\Components\Collection\Contracts\Collection;
66
use Somnambulist\Components\Collection\Exceptions\DuplicateItemException;
7+
use Somnambulist\Components\Collection\Utils\Value;
78
use function array_unshift;
89

910
/**
@@ -24,6 +25,8 @@ trait PrependOnlyUniqueValues
2425
public function prepend(mixed ...$value): Collection|static
2526
{
2627
foreach ($value as $item) {
28+
Value::assertIsOfType($item, $this->type);
29+
2730
if ($this->contains($item)) {
2831
throw DuplicateItemException::found($value, $this->keys($item)->first());
2932
}

src/Behaviours/Mutate/PrependValues.php

+3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Somnambulist\Components\Collection\Behaviours\Mutate;
44

55
use Somnambulist\Components\Collection\Contracts\Collection;
6+
use Somnambulist\Components\Collection\Utils\Value;
67
use function array_unshift;
78

89
/**
@@ -22,6 +23,8 @@ trait PrependValues
2223
*/
2324
public function prepend(mixed ...$value): Collection|static
2425
{
26+
Value::assertAllOfType($value, $this->type);
27+
2528
array_unshift($this->items, ...$value);
2629

2730
return $this;

src/Behaviours/Mutate/ReplaceValues.php

+5
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Somnambulist\Components\Collection\Behaviours\Mutate;
44

55
use Somnambulist\Components\Collection\Contracts\Collection;
6+
use Somnambulist\Components\Collection\Utils\Value;
67
use function array_replace;
78
use function array_replace_recursive;
89

@@ -21,6 +22,8 @@ trait ReplaceValues
2122
*/
2223
public function replace(array ...$items): Collection|static
2324
{
25+
Value::assertAllOfType($items, $this->type);
26+
2427
$this->items = array_replace($this->items, ...$items);
2528

2629
return $this;
@@ -35,6 +38,8 @@ public function replace(array ...$items): Collection|static
3538
*/
3639
public function replaceRecursively(array ...$items): Collection|static
3740
{
41+
Value::assertAllOfType($items, $this->type);
42+
3843
$this->items = array_replace_recursive($this->items, ...$items);
3944

4045
return $this;

src/Behaviours/Mutate/UnionOnlyUniqueValues.php

+2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ public function union(mixed $items): Collection|static
2323
{
2424
$items = Value::toArray($items);
2525

26+
Value::assertAllOfType($items, $this->type);
27+
2628
foreach ($items as $key => $item) {
2729
if ($this->contains($item)) {
2830
throw DuplicateItemException::found($item, $this->keys($item)->first());

src/Behaviours/Mutate/UnionValues.php

+5-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,11 @@ trait UnionValues
2020
*/
2121
public function union(mixed $items): Collection|static
2222
{
23-
$this->items = $this->items + Value::toArray($items);
23+
$items = Value::toArray($items);
24+
25+
Value::assertAllOfType($items, $this->type);
26+
27+
$this->items = $this->items + $items;
2428

2529
return $this;
2630
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace Somnambulist\Components\Collection\Exceptions;
4+
5+
use DomainException;
6+
use function get_class;
7+
use function get_debug_type;
8+
use function gettype;
9+
use function is_object;
10+
use function sprintf;
11+
12+
class InvalidItemTypeException extends DomainException
13+
{
14+
private mixed $value;
15+
private string $type;
16+
17+
public static function invalidItem($value, $type): InvalidItemTypeException
18+
{
19+
$e = new self(
20+
sprintf('Values must be of type "%s", "%s" is not permitted',
21+
$type, get_debug_type($value),
22+
)
23+
);
24+
$e->value = $value;
25+
$e->type = $type;
26+
27+
return $e;
28+
}
29+
30+
public function getValue(): mixed
31+
{
32+
return $this->value;
33+
}
34+
35+
public function getType(): string
36+
{
37+
return $this->type;
38+
}
39+
}

0 commit comments

Comments
 (0)