oceanmoon / collections
Type-safe PHP collections: Sequence (ordered list), Dictionary (any-type keys), Set (unique values), with runtime type validation.
Requires
- php: ^8.4
- oceanmoon/core: ^2.0
Requires (Dev)
- dealerdirect/phpcodesniffer-composer-installer: ^1.0
- oceanmoon/coding-standard: ^2.0
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^10.0 || ^11.0 || ^12.0
- squizlabs/php_codesniffer: ^4.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
Type-safe collection classes for PHP 8.4+.
License | Changelog | Documentation
Description
A type-safe collection library featuring runtime type validation, immutable operations, and unrestricted key types.
Core Classes:
- Sequence - Ordered lists with integer indexing
- Dictionary - Key-value pairs accepting any type as keys
- Set - Unique value collections with set operations
Development and Quality Assurance
This package was developed with Claude Code:
- The author developed the initial concept, designed the public API, and did most of the initial coding.
- Claude Code wrote the tests and most of the documentation.
- Claude Code also assisted with multiple code reviews, which led to bug fixes, performance improvements and other refinements.
The code is checked with:
- PHP_CodeSniffer, for PSR-12 compliance plus the Mossy2100 coding standard
- PHPStan, at level 9
- PHPUnit, with 100% code coverage
Why these collections?
PHP's native arrays are powerful but have limitations:
- Keys restricted to strings and integers - Can't use booleans, floats, arrays, or objects as keys.
- No type safety - Arrays accept any mix of types without validation.
- Type coercion issues - Keys like
1,'1',true, and1.0all become the same key. - Limited operations - Built-in array functions lack chaining, immutability, and advanced transformations. Errors and exceptions are inconsistent.
The collection classes in this package solve these problems with:
✅ Any type as keys - Use objects, arrays, booleans, floats, null as Dictionary keys.
✅ Runtime type validation - Optional type constraints with compile-time-like checking.
✅ Rich API - Fluent interfaces, method chaining, functional programming support.
✅ Immutable operations - Transformations return new collections without modifying originals.
✅ Type inference - Automatically detect types from your data.
✅ Mathematical correctness - Proper type safety for operations like sum() and product().
Alternatives
Before using this package, you may want to check out these PHP extensions:
These are official PHP extensions that provide efficient data structure implementations and will probably be well-supported going forward. However, if you need runtime type safety and generics-like behavior, or simply prefer a more functional style of programming, the collection classes in this package provides features that these extensions lack, including type constraints, type inference, and type-safe operations.
Features
Type Safety
// Restrict types at runtime $numbers = new Sequence('int'); $numbers->append(1, 2, 3); // ✅ Works $numbers->append('four'); // ❌ InvalidArgumentException // Union types $mixed = new Sequence('int|string'); $mixed->append(1, 'two', 3); // ✅ All work // Type inference $seq = new Sequence(source: [1, 2, 3]); // Automatically infers type as 'int'
Unrestricted Keys
// PHP arrays: keys must be string|int $array = []; $array[new DateTime()] = 'event'; // ❌ TypeError: Cannot access offset of type DateTime on array $array[[1, 2]] = 'coords'; // ❌ TypeError: Cannot access offset of type array on array // Dictionary: any type works $dict = new Dictionary(); $dict[new DateTime()] = 'event'; // ✅ Works $dict[[1, 2, 3]] = 'coordinates'; // ✅ Works $dict[fopen('file.txt', 'r')] = 'data'; // ✅ Works $dict[true] = 'yes'; // ✅ Works $dict[null] = 'empty'; // ✅ Works
Functional Programming
$numbers = new Sequence('int', source: [1, 2, 3, 4, 5]); // Method chaining $result = $numbers ->filter(fn($n) => $n % 2 === 0) // Keep evens ->map(fn($n) => $n * 2) // Double them ->reverse(); // Reverse order echo $result; // [8, 4] // Original unchanged (immutable operations) echo $numbers; // [1, 2, 3, 4, 5]
Set Operations
$set1 = new Set('int', [1, 2, 3, 4]); $set2 = new Set('int', [3, 4, 5, 6]); $union = $set1->union($set2); // {1, 2, 3, 4, 5, 6} $intersection = $set1->intersect($set2); // {3, 4} $difference = $set1->diff($set2); // {1, 2} // Subset checking $set1->subset($set2); // false $set1->disjoint($set2); // false
Installation
composer require mossy2100/collections
Requirements
- PHP ^8.4
- mossy2100/core
Quick Start
Sequence - Type-safe lists
use Mossy2100\Collections\Sequence; // Create with type inference $seq = new Sequence(source: [1, 2, 3, 4, 5]); // Add items $seq->append(6, 7, 8); $seq->prepend(0); // Remove items $item = $seq->removeByIndex(0); // 0 (the removed value) $count = $seq->removeByValue(8); // 1 (the number of items removed) // Access items echo $seq[0]; // 1 echo $seq->first(); // 1 echo $seq->last(); // 7 // Transformations $evens = $seq->filter(fn($n) => $n % 2 === 0); $doubled = $seq->map(fn($n) => $n * 2); $sorted = $seq->sort(); // Aggregations echo $seq->sum(); // 28 echo $seq->product(); // 5040 echo $seq->average(); // 4 echo $seq->min(); // 1 echo $seq->max(); // 7
Dictionary - Key-value pairs with any type
use Mossy2100\Collections\Dictionary; // Create without type constraints, so keys and values can be any type $dict = new Dictionary(null, null, ['a' => 1, 'b' => 2]); // Use objects as keys $dict[new DateTime('2024-01-01')] = 'New Year'; // Use arrays as keys $dict[[10, 20]] = 'coordinates'; // Type constraints $typed = new Dictionary('string', 'int'); $typed['count'] = 42; // ✅ Works $typed['count'] = 'text'; // ❌ InvalidArgumentException // Check and access if ($dict->keyExists('a')) { echo $dict['a']; // 1 } // Iteration foreach ($typed as $key => $value) { echo "$key => $value\n"; // count => 42 }
Set - Unique values
use Mossy2100\Collections\Set; // Duplicates automatically removed $set = new Set(source: [1, 2, 2, 3, 3, 3]); echo $set->count(); // 3 // Set operations $a = new Set('int', [1, 2, 3]); $b = new Set('int', [2, 3, 4]); $a->union($b); // {1, 2, 3, 4} $a->intersect($b); // {2, 3} $a->diff($b); // {1} // Membership testing var_dump($set->contains(2)); // true var_dump($set->contains(5)); // false
Classes
Core Collections
- Collection - Abstract base class providing shared functionality for all collection types
- Sequence - Ordered lists with integer indexing, similar to
List<T>in C# or Java - Dictionary - Key-value pairs accepting any PHP type for both keys and values
- Set - Unique value collections with mathematical set operations
Supporting Classes
- TypeSet - Runtime type validation and constraint management
- Pair - Immutable key-value pair container
Testing
The library includes comprehensive test coverage:
# Run all tests vendor/bin/phpunit # Run all tests for a specific collection type vendor/bin/phpunit tests/Dictionary # Run specific test class vendor/bin/phpunit tests/Sequence/SequenceTransformationTest.php # Run with coverage (generates HTML report and clover.xml) composer test
Test Coverage:
- 600+ tests across all classes
- 100% code coverage
- Edge cases, error conditions, and type safety
License
MIT License - see LICENSE for details
Support
- Issues: https://github.com/mossy2100/PHP-Collections/issues
- Documentation: See docs/ directory for detailed class documentation
- Examples: See test files for comprehensive usage examples
For questions or suggestions, please open an issue.
Changelog
See CHANGELOG.md for version history and changes.
