It's basically a syntax sugar over classic array structure, which allows you to use it as classic array, but adds some cool features.
- Installation
 - Requirements
 - Base Interfaces
 - Mutable
 - Immutable
 - Generic
 - Arrow Functions
 - Plans for next versions
 
composer require mf/collections-phpPHP ^8.2
Check out Documentation for more details.
- basic Interface for enumerable
 - extends 
IteratorAggregate,Countable - see Immutable tuple
 - see Mutable PrioritizedCollection
 
- basic Interface for Collections
 - extends 
IEnumerable - see Mutable collections
 - see Immutable collections
 
A list is an ordered (possibly immutable) series of elements of the same type.
- extends 
ICollection - see Mutable list
 - see Immutable list
 
A map is an ordered (possibly immutable) series of key values pairs.
- extends 
ICollection, ArrayAccess - see Mutable map
 - see Immutable map
 
A sequence is a logical series of elements all of one type.
- extends 
ICollection - see Immutable seq
 
A tuple is a grouping of unnamed but ordered values, possibly of different types.
- extends 
IEnumerable,ArrayAccess,Stringable - see Immutable tuple
 
Mutable\Generic\ICollection,Mutable\Generic\IList,Mutable\Generic\IMap
- implements 
Mutable\Generic\IList - is 
eageras possible 
- implements 
Mutable\Generic\IMap - is 
eageras possible 
- implements 
IEnumerable - holds items with 
generictype bypriority - is 
eageras possible 
For case when you want to apply only the first strategy which can do what you want.
You can add strategies dynamically and still apply them by priority later.
// initialization of strategies
/** @phpstan-var PrioritizedCollection<StrategyInterface> $strategies */
$strategies = new PrioritizedCollection();
$strategies->add(new DefaultStrategy(), 1);
// added later
$strategies->add(new SpecialStrategy(), 100);
// find and apply first suitable strategy
/** @var StrategyInterface $strategy */
foreach ($strategies as $strategy) {
    if ($strategy->supports($somethingStrategic)) {
        return $strategy->apply($somethingStrategic);
    }
}internal stateof Immutable\Collection instance willnever changefrom the outside (it isreadonly)
$list = new Immutable\ListCollection();
$listWith1 = $list->add(1);
// $list != $listWith1
echo $list->count();        // 0
echo $listWith1->count();   // 1$listis still an emptyImmutable\ListCollection$listWith1is new instance ofImmutable\ListCollectionwith value1
Immutable\Generic\ICollection,Immutable\Generic\IList,Immutable\Generic\IMap,Immutable\Generic\ISeq,Immutable\ITuple
- implements 
Immutable\Generic\IList - is 
eageras possible 
- implements 
Immutable\Generic\IMap - is 
eageras possible 
- implements 
Immutable\Generic\ISeq - is 
lazyas possible (even could beInfinite) 
$seq = Seq::infinite()                         // 1, 2, ...
    ->filter(fn ($i) => $i % 2 === 0)   // 2, 4, ...
    ->skip(2)                           // 6, 8, ...
    ->map(fn ($i) => $i * $i)           // 36, 64, ...
    ->takeWhile(fn ($i) => $i < 100)    // 36, 64
    ->reverse()                         // 64, 36
    ->take(1);                          // 64
// for now the Sequence is still lazy
// this will generate (evaluate) the values
$array = $seq->toArray();               // [64]- always has a 
Keyand theValue - key is restricted to 
int|stringso it may be used in theforeachas a key - can contain any values
 
- implements 
Immutable\ITuple - must have at least 2 values (otherwise it is just a single value)
 - is 
eageras possible - allows 
destructuring,matchingandparsing/formatting - can contain any scalar values and/or arrays
- in string representation of a 
Tuple, array values must be separated by;(not by,) 
 - in string representation of a 
 
Tuple::parse('(foo, bar)')->toArray();                  // ['foo', 'bar']
Tuple::parse('("foo, bar", boo)')->toArray();           // ['foo, bar', 'boo']
Tuple::parse('(1, "foo, bar", true)')->toArray();       // [1, 'foo, bar', true]
Tuple::parse('(1, [2; 3], [four; "Five"])')->toArray(); // [1, [2, 3], ['four', 'five']]Tuple::from([1, 1])->match('int', 'int');                      // true
Tuple::from([1, 2, 3])->isSame(Tuple::of(1, 2, 3));            // true
Tuple::of(10, 'Foo', null)->match('int', 'string', '?string'); // true
Tuple::of(10, [9, 8])->match('int', 'array');                  // trueTuple::parseMatch('(foo, bar)', 'string', 'string')->toArray();        // ['foo', 'bar']
Tuple::parseMatchTypes('(foo, bar)', ['string', 'string'])->toArray(); // ['foo', 'bar']
// invalid types
Tuple::parseMatch('(foo, bar, 1)', 'string', 'string'); // throws \InvalidArgumentException "Given tuple does NOT match expected types (string, string) - got (string, string, int)"Tuple::from([1, 'foo', null])->toString();          // '(1, "foo", null)'
// for URL
Tuple::from(['foo', 'bar'])->toStringForUrl();      // '(foo,bar)'
Tuple::from(['foo-bar', 'boo'])->toStringForUrl();  // '(foo-bar,bar)'
Tuple::from(['mail', 'a@b.com'])->toStringForUrl(); // '(mail,"a@b.com")'$tuple  = Tuple::of('first', 2, 3); // ('first', 2, 3)
$first  = $tuple->first();          // 'first'
$second = $tuple->second();         // 2
[$first, $second] = $tuple;         // $first = 'first'; $second = 2
[,, $third]       = $tuple;         // 3sprintf('Title: %s | Value: %s', ...Tuple::of('foo', 'bar')); // "Title: foo | Value: bar"- merging 
Tupleswill automatically flat them (see last example below) 
$base  = Tuple::of('one', 'two');                       // ('one', 'two')
$upTo3 = Tuple::merge($base, 'three');                  // ('one', 'two', 'three')
$upTo4 = Tuple::merge($base, '3', 'four');              // ('one', 'two', '3', 'four')
$upTo5 = Tuple::merge($base, ['3', '4'], '5');          // ('one', 'two', ['3', '4'], '5')
$upTo5 = Tuple::merge($base, Tuple::of('3', '4'), '5'); // ('one', 'two', '3', '4', '5')$base = Tuple::of('one', 'two');                                    // ('one', 'two')
$upTo3 = Tuple::mergeMatch(['string', 'string', 'int'], $base, 3);  // ('one', 'two', 3)
// invalid types
Tuple::mergeMatch(['string', 'string'], $base, 3); // throws \InvalidArgumentException "Merged tuple does NOT match expected types (string, string) - got (string, string, int)."- if your 
Sequenceget mapped and filtered many times (for readability), it is not a problemmap -> map -> filter -> map -> filter -> mapwill iterate the collection only once (for applying all modifiers at once)- this modification is done when another method is triggered, so adding new modifier is an atomic operation
 
 - all the values are generated on the fly, so it may end on out of memory exception
 
- use 
Symfony/Stopwatchin unit tests - even better documentation (current)