adachsoft / collection
A collection library providing flexible and immutable collections.
Requires
- php: ^8.3
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^12.3
This package is not auto-updated.
Last update: 2025-09-19 04:03:32 UTC
README
A simple, focused PHP library for building strongly-typed collections with both mutable and immutable variants. It promotes clear, predictable APIs and removes the pitfalls of loosely-typed arrays.
- PHP 8.3+
- No external runtime dependencies
- PSR-4 autoloading
Key Features
- Mutable and immutable base classes to extend
- Strong runtime type validation for items (and for maps: for keys and values)
- Array-like access
[]
:- Mutable collections support read/write via
[]
- Immutable collections support read-only via
[]
(write operations throw)
- Mutable collections support read/write via
- Clean iteration with
IteratorAggregate
- Utility type system for maps (scalar and class types)
- NEW: Advanced collection operations - filter, map, reduce, unique, reverse, chunk, take, skip
Installation
composer require adachsoft/collection
Quick Start
1) Your domain object
<?php
final class User
{
public function __construct(
public readonly int $id,
public readonly string $name,
) {}
}
2) Mutable collection
Extend AdachSoft\Collection\AbstractCollection
and set the expected item type. The simplest way is to predefine the $type
property in the subclass.
<?php
use AdachSoft\Collection\AbstractCollection;
final class UserCollection extends AbstractCollection
{
/** @var class-string<User>|null */
protected ?string $type = User::class;
}
Usage:
$user1 = new User(1, 'Alice');
$user2 = new User(2, 'Bob');
$users = new UserCollection([$user1]);
$users->add($user2); // OK
$users[] = new User(3, 'C'); // also OK via ArrayAccess (write)
foreach ($users as $user) {
echo $user->name . "\n"; // Alice, Bob, C
}
// Type safety: adding wrong type will throw InvalidArgumentException
$users->add('not a user'); // throws InvalidArgumentException
3) Immutable collection (read-only, array-like access for reads)
Extend AdachSoft\Collection\AbstractImmutableCollection
. No mutator methods exist; read access via []
is allowed, write attempts via []
throw.
<?php
use AdachSoft\Collection\AbstractImmutableCollection;
final class ImmutableUserCollection extends AbstractImmutableCollection
{
/** @var class-string<User>|null */
protected ?string $type = User::class;
}
Usage:
$users = new ImmutableUserCollection([
new User(1, 'Alice'),
new User(2, 'Bob'),
]);
// Read operations
echo $users[0]->name; // Alice
var_dump(isset($users[1])); // true
// Write operations are not allowed (immutable)
$users[0] = new User(3, 'Charlie'); // throws BadMethodCallException
unset($users[0]); // throws BadMethodCallException
// Functional helpers return the same collection class (static)
$names = $users->map(fn (User $u) => $u->name); // ImmutableUserCollection
4) Advanced Collection Operations (NEW)
All collections (both mutable and immutable) now support advanced functional operations:
$numbers = new ImmutableUserCollection([1, 2, 3, 4, 5, 5, 6]);
// Reduce - aggregate values
$sum = $numbers->reduce(fn ($carry, $item) => $carry + $item, 0); // 21
// Unique - remove duplicates
$unique = $numbers->unique(); // [1, 2, 3, 4, 5, 6]
// Custom unique comparator for objects
$users = new UserCollection([
new User(1, 'Alice'),
new User(2, 'Bob'),
new User(1, 'Alice2'), // Same ID, different name
]);
$uniqueUsers = $users->unique(fn ($a, $b) => $a->id === $b->id);
// Reverse - reverse order
$reversed = $numbers->reverse(); // [6, 5, 5, 4, 3, 2, 1]
// Chunk - split into smaller collections
$chunks = $numbers->chunk(3); // [[1,2,3], [4,5,5], [6]]
// Take - get first N elements
$firstThree = $numbers->take(3); // [1, 2, 3]
// Skip - skip first N elements
$withoutFirst = $numbers->skip(2); // [3, 4, 5, 5, 6]
All operations return new collection instances (immutable operations), preserving the original collection type and maintaining key associations where applicable.
5) Immutable, typed map (keys and values)
Use AdachSoft\Collection\AbstractImmutableMap
for key/value collections with strict, explicit types for keys and values.
<?php
use AdachSoft\Collection\AbstractImmutableMap;
use AdachSoft\Collection\Type\TypeDefinitionInterface;
use AdachSoft\Collection\Type\ScalarTypeEnum;
use AdachSoft\Collection\Type\ClassType;
final class UserMap extends AbstractImmutableMap
{
protected function getKeyType(): TypeDefinitionInterface
{
return ScalarTypeEnum::STRING; // keys must be strings
}
protected function getValueType(): TypeDefinitionInterface
{
return new ClassType(User::class); // values must be User instances
}
}
Usage:
$map = new UserMap([
'owner' => new User(1, 'Alice'),
'guest' => new User(2, 'Bob'),
]);
$owner = $map->get('owner'); // returns User
var_dump($map->hasKey('guest')); // true
foreach ($map as $key => $user) {
echo $key . ': ' . $user->name . "\n";
}
$allKeys = iterator_to_array($map->keys()); // ['owner', 'guest']
$allVals = iterator_to_array($map->values()); // [User(...), User(...)]
// Invalid types at construction time will throw:
// - AdachSoft\Collection\Exception\InvalidKeyTypeException for wrong key type
// - AdachSoft\Collection\Exception\InvalidItemTypeException for wrong value type
6) Mutable, typed map
Use AdachSoft\Collection\AbstractMap
to allow in-place mutation of key/value pairs while keeping strict types.
<?php
use AdachSoft\Collection\AbstractMap;
use AdachSoft\Collection\Type\TypeDefinitionInterface;
use AdachSoft\Collection\Type\ScalarTypeEnum;
use AdachSoft\Collection\Type\ClassType;
final class MutableUserMap extends AbstractMap
{
protected function getKeyType(): TypeDefinitionInterface
{
return ScalarTypeEnum::STRING; // keys must be strings
}
protected function getValueType(): TypeDefinitionInterface
{
return new ClassType(User::class); // values must be User instances
}
}
Usage:
$map = new MutableUserMap();
$map->set('x', new User(1, 'Alice'));
$map->set('x', new User(2, 'Bob')); // updates existing key
$map->removeKey('x');
$map->clear();
Contracts
Prefer the contracts from AdachSoft\Collection\Contract
:
ReadableCollectionInterface
for read-only collectionsMutableCollectionInterface
for mutable collectionsKeyedCollectionInterface
for immutable mapsMutableKeyedCollectionInterface
for mutable maps
Tooling
- PHPUnit tests:
composer test
- Coding style:
composer csfix
- Static analysis:
composer phpstan
Changelog
See CHANGELOG.md for release notes.
License
MIT