Skip to content

Commit

Permalink
Allow collections to only contain a certain type
Browse files Browse the repository at this point in the history
  • Loading branch information
dave-redfern committed Jun 15, 2024
1 parent ed8710e commit 7249942
Show file tree
Hide file tree
Showing 24 changed files with 259 additions and 13 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
Change Log
==========

2024-06-10
----------

* Add support for a "typed" collection that contains only values of type
* Set default collection types to SimpleCollection / MutableCollection to allow for typed collections

2024-02-24 - 5.4.0
------------------

Expand Down
20 changes: 20 additions & 0 deletions CHANGELOG_V5.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,23 @@ All features previously marked as deprecated in the 4.X series have been removed

* Keys are typed to `int|string` across the board instead of inferred mixed/string
* `GetWithDotNotation::get()` no longer supports `null` for all values; use `all()` instead

### 5.5.0 addition of type to collections

From 5.5.0, collections can be extended and the "type" set to restrict the collection to a specific type
of values. The type can be any of:

* int
* float
* bool
* string
* scalar
* array
* object or interface class

To accommodate this change, `SimpleCollection` and `MutableCollection` now have the collectionClass set to themselves
to prevent issues of methods like `map()` causing errors due to returning values not supported by the type.

To set the type: extend the collection type you wish to make type specific, and then set the property `$type` to
the type you want the collection to contain. This will typically be a class name, though the standard PHP types
can be used - except for resources.
18 changes: 18 additions & 0 deletions src/AbstractCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@ abstract class AbstractCollection implements Collection
*/
protected ?string $collectionClass = null;

/**
* The type of values that this collection is restricted to, can use scalar types or class names
*
* @var string|null
*/
protected ?string $type = null;

protected array $items = [];

public static function collect(mixed $items = []): Collection|static
Expand Down Expand Up @@ -80,6 +87,7 @@ public static function __set_state($array): object
{
$object = new static();
$object->items = $array['items'];
$object->type = $array['type'];

return $object;
}
Expand Down Expand Up @@ -128,6 +136,16 @@ public function setCollectionClass(string $class): void
$this->collectionClass = $class;
}

public function isTyped(): bool
{
return $this->type !== null;
}

public function type(): ?string
{
return $this->type;
}

public function offsetExists($offset): bool
{
return array_key_exists($offset, $this->items);
Expand Down
5 changes: 5 additions & 0 deletions src/Behaviours/CanAddAndRemoveItems.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

namespace Somnambulist\Components\Collection\Behaviours;

use Somnambulist\Components\Collection\Exceptions\InvalidItemTypeException;
use Somnambulist\Components\Collection\Utils\Value;

/**
* @property array $items
*/
Expand All @@ -10,6 +13,8 @@ trait CanAddAndRemoveItems

final public function offsetSet($offset, $value): void
{
Value::assertIsOfType($value, $this->type);

if (null === $offset) {
$this->items[] = $value;
} else {
Expand Down
4 changes: 4 additions & 0 deletions src/Behaviours/CannotAddDuplicateItems.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
namespace Somnambulist\Components\Collection\Behaviours;

use Somnambulist\Components\Collection\Exceptions\DuplicateItemException;
use Somnambulist\Components\Collection\Exceptions\InvalidItemTypeException;
use Somnambulist\Components\Collection\Utils\Value;

/**
* @property array $items
Expand All @@ -12,6 +14,8 @@ trait CannotAddDuplicateItems

final public function offsetSet($offset, $value): void
{
Value::assertIsOfType($value, $this->type);

if ($this->contains($value)) {
throw DuplicateItemException::found($value, array_search($value, $this->items));
}
Expand Down
7 changes: 5 additions & 2 deletions src/Behaviours/Mutate/AppendOnlyUniqueValues.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Somnambulist\Components\Collection\Contracts\Collection;
use Somnambulist\Components\Collection\Exceptions\DuplicateItemException;
use Somnambulist\Components\Collection\Utils\Value;
use function array_push;

/**
Expand Down Expand Up @@ -38,6 +39,8 @@ public function add(mixed $value): Collection|static
public function append(mixed ...$value): Collection|static
{
foreach ($value as $item) {
Value::assertIsOfType($item, $this->type);

if ($this->contains($item)) {
throw DuplicateItemException::found($value, $this->keys($item)->first());
}
Expand All @@ -50,7 +53,7 @@ public function append(mixed ...$value): Collection|static
}

/**
* Push all of the given items onto the collection.
* Push all the given items onto the collection.
*
* @param iterable $items
*
Expand All @@ -59,7 +62,7 @@ public function append(mixed ...$value): Collection|static
public function concat(iterable $items): Collection|static
{
foreach ($items as $item) {
$this->push($item);
$this->append($item);
}

return $this;
Expand Down
7 changes: 5 additions & 2 deletions src/Behaviours/Mutate/AppendValues.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Somnambulist\Components\Collection\Behaviours\Mutate;

use Somnambulist\Components\Collection\Contracts\Collection;
use Somnambulist\Components\Collection\Utils\Value;
use function array_push;

/**
Expand Down Expand Up @@ -36,13 +37,15 @@ public function add(mixed $value): Collection|static
*/
public function append(mixed ...$value): Collection|static
{
Value::assertAllOfType($value, $this->type);

array_push($this->items, ...$value);

return $this;
}

/**
* Push all of the given items onto the collection.
* Push all the given items onto the collection.
*
* @param iterable $items
*
Expand All @@ -51,7 +54,7 @@ public function append(mixed ...$value): Collection|static
public function concat(iterable $items): Collection|static
{
foreach ($items as $item) {
$this->push($item);
$this->append($item);
}

return $this;
Expand Down
2 changes: 2 additions & 0 deletions src/Behaviours/Mutate/CombineOnlyUniqueValues.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ public function combine(mixed $items): Collection|static
$items = Value::toArray($items);
$unique = array_unique($items);

Value::assertAllOfType($items, $this->type);

if (count($items) !== count($unique)) {
throw DuplicateItemException::preparedValuesContainDuplicates(__FUNCTION__);
}
Expand Down
6 changes: 5 additions & 1 deletion src/Behaviours/Mutate/CombineValues.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ trait CombineValues
*/
public function combine(mixed $items): Collection|static
{
return $this->new(array_combine($this->items, Value::toArray($items)));
$items = Value::toArray($items);

Value::assertAllOfType($items, $this->type);

return $this->new(array_combine($this->items, $items));
}
}
5 changes: 5 additions & 0 deletions src/Behaviours/Mutate/Fill.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Somnambulist\Components\Collection\Behaviours\Mutate;

use Somnambulist\Components\Collection\Contracts\Collection;
use Somnambulist\Components\Collection\Utils\Value;
use function array_fill;
use function array_fill_keys;

Expand All @@ -27,6 +28,8 @@ trait Fill
*/
public function fill(int $start, int $count, mixed $value): Collection|static
{
Value::assertIsOfType($value, $this->type);

return $this->new(array_fill($start, $count, $value));
}

Expand All @@ -44,6 +47,8 @@ public function fill(int $start, int $count, mixed $value): Collection|static
*/
public function fillKeysWith(mixed $value): Collection|static
{
Value::assertIsOfType($value, $this->type);

return $this->new(array_fill_keys($this->values()->toArray(), $value));
}
}
2 changes: 2 additions & 0 deletions src/Behaviours/Mutate/MergeOnlyUniqueValues.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ public function merge(mixed $value): Collection|static
$items = Value::toArray($value);
$unique = array_unique($items);

Value::assertAllOfType($items, $this->type);

if (count($items) !== count($unique)) {
throw DuplicateItemException::preparedValuesContainDuplicates(__FUNCTION__);
}
Expand Down
6 changes: 5 additions & 1 deletion src/Behaviours/Mutate/MergeValues.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,11 @@ trait MergeValues
*/
public function merge(mixed $value): Collection|static
{
$this->items = array_merge($this->items, Value::toArray($value));
$value = Value::toArray($value);

Value::assertAllOfType($value, $this->type);

$this->items = array_merge($this->items, $value);

return $this;
}
Expand Down
3 changes: 3 additions & 0 deletions src/Behaviours/Mutate/Pad.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Somnambulist\Components\Collection\Behaviours\Mutate;

use Somnambulist\Components\Collection\Contracts\Collection;
use Somnambulist\Components\Collection\Utils\Value;
use function array_pad;

/**
Expand All @@ -23,6 +24,8 @@ trait Pad
*/
public function pad(int $size, mixed $value): Collection|static
{
Value::assertIsOfType($value, $this->type);

$this->items = array_pad($this->items, $size, $value);

return $this;
Expand Down
3 changes: 3 additions & 0 deletions src/Behaviours/Mutate/PrependOnlyUniqueValues.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Somnambulist\Components\Collection\Contracts\Collection;
use Somnambulist\Components\Collection\Exceptions\DuplicateItemException;
use Somnambulist\Components\Collection\Utils\Value;
use function array_unshift;

/**
Expand All @@ -24,6 +25,8 @@ trait PrependOnlyUniqueValues
public function prepend(mixed ...$value): Collection|static
{
foreach ($value as $item) {
Value::assertIsOfType($item, $this->type);

if ($this->contains($item)) {
throw DuplicateItemException::found($value, $this->keys($item)->first());
}
Expand Down
3 changes: 3 additions & 0 deletions src/Behaviours/Mutate/PrependValues.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Somnambulist\Components\Collection\Behaviours\Mutate;

use Somnambulist\Components\Collection\Contracts\Collection;
use Somnambulist\Components\Collection\Utils\Value;
use function array_unshift;

/**
Expand All @@ -22,6 +23,8 @@ trait PrependValues
*/
public function prepend(mixed ...$value): Collection|static
{
Value::assertAllOfType($value, $this->type);

array_unshift($this->items, ...$value);

return $this;
Expand Down
5 changes: 5 additions & 0 deletions src/Behaviours/Mutate/ReplaceValues.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Somnambulist\Components\Collection\Behaviours\Mutate;

use Somnambulist\Components\Collection\Contracts\Collection;
use Somnambulist\Components\Collection\Utils\Value;
use function array_replace;
use function array_replace_recursive;

Expand All @@ -21,6 +22,8 @@ trait ReplaceValues
*/
public function replace(array ...$items): Collection|static
{
Value::assertAllOfType($items, $this->type);

$this->items = array_replace($this->items, ...$items);

return $this;
Expand All @@ -35,6 +38,8 @@ public function replace(array ...$items): Collection|static
*/
public function replaceRecursively(array ...$items): Collection|static
{
Value::assertAllOfType($items, $this->type);

$this->items = array_replace_recursive($this->items, ...$items);

return $this;
Expand Down
2 changes: 2 additions & 0 deletions src/Behaviours/Mutate/UnionOnlyUniqueValues.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ public function union(mixed $items): Collection|static
{
$items = Value::toArray($items);

Value::assertAllOfType($items, $this->type);

foreach ($items as $key => $item) {
if ($this->contains($item)) {
throw DuplicateItemException::found($item, $this->keys($item)->first());
Expand Down
6 changes: 5 additions & 1 deletion src/Behaviours/Mutate/UnionValues.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@ trait UnionValues
*/
public function union(mixed $items): Collection|static
{
$this->items = $this->items + Value::toArray($items);
$items = Value::toArray($items);

Value::assertAllOfType($items, $this->type);

$this->items = $this->items + $items;

return $this;
}
Expand Down
39 changes: 39 additions & 0 deletions src/Exceptions/InvalidItemTypeException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php declare(strict_types=1);

namespace Somnambulist\Components\Collection\Exceptions;

use DomainException;
use function get_class;
use function get_debug_type;
use function gettype;
use function is_object;
use function sprintf;

class InvalidItemTypeException extends DomainException
{
private mixed $value;
private string $type;

public static function invalidItem($value, $type): InvalidItemTypeException
{
$e = new self(
sprintf('Values must be of type "%s", "%s" is not permitted',
$type, get_debug_type($value),
)
);
$e->value = $value;
$e->type = $type;

return $e;
}

public function getValue(): mixed
{
return $this->value;
}

public function getType(): string
{
return $this->type;
}
}
Loading

0 comments on commit 7249942

Please sign in to comment.