https://github.com/mortalflesh/mfcollectionsphp
Collections for PHP - It's basically a syntax sugar over classic array structure, which allows you to use it as classic array, but adds some cool features.
https://github.com/mortalflesh/mfcollectionsphp
collection collections generic generic-collections generic-interface immutable immutable-collections mutable-collections php syntax-sugar
Last synced: about 2 months ago
JSON representation
Collections for PHP - It's basically a syntax sugar over classic array structure, which allows you to use it as classic array, but adds some cool features.
- Host: GitHub
- URL: https://github.com/mortalflesh/mfcollectionsphp
- Owner: MortalFlesh
- License: mit
- Created: 2016-06-04T09:50:22.000Z (almost 9 years ago)
- Default Branch: master
- Last Pushed: 2024-09-27T07:55:05.000Z (8 months ago)
- Last Synced: 2025-03-21T13:45:23.687Z (2 months ago)
- Topics: collection, collections, generic, generic-collections, generic-interface, immutable, immutable-collections, mutable-collections, php, syntax-sugar
- Language: PHP
- Homepage: https://github.com/MortalFlesh/MFCollectionsPHP/wiki
- Size: 623 KB
- Stars: 10
- Watchers: 3
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
MFCollections for PHP
=====================[](https://packagist.org/packages/mf/collections-php)
[](https://packagist.org/packages/mf/collections-php)
[](https://packagist.org/packages/mf/collections-php)
[](https://github.com/MortalFlesh/MFCollectionsPHP/actions/workflows/tests.yaml)
[](https://coveralls.io/github/MortalFlesh/MFCollectionsPHP?branch=master)It's basically a syntax sugar over classic array structure, which allows you to use it as classic array, but adds some cool features.
## Table of Contents
- [Installation](#installation)
- [Requirements](#requirements)
- [Base Interfaces](#base-interfaces)
- [IEnumerable](#enumerable-interface)
- [ICollection](#collection-interface)
- [IList](#list-interface)
- [IMap](#map-interface)
- [ISeq](#seq-interface)
- [ITuple](#tuple-interface)
- [Mutable](#mutable-collections)
- [Immutable](#immutable-collections)
- [Generic](#generic-collections)
- [Arrow Functions](#arrow-functions)
- [Plans for next versions](#plans-for-next-versions)## Installation
```bash
composer require mf/collections-php
```## Requirements
- `PHP ^8.2`## Base Interfaces
Check out [Documentation](https://github.com/MortalFlesh/MFCollectionsPHP/wiki) for more details.
### IEnumerable
- basic Interface for enumerable
- extends `IteratorAggregate`, `Countable`
- [see Immutable tuple](#immutable-tuple)
- [see Mutable PrioritizedCollection](#mutable-prioritized-collection)### ICollection
- basic Interface for Collections
- extends `IEnumerable`
- [see Mutable collections](#mutable-collections)
- [see Immutable collections](#immutable-collections)### IList
A _list_ is an ordered (_possibly immutable_) series of elements of the same type.
- extends `ICollection`
- [see Mutable list](#mutable-list)
- [see Immutable list](#immutable-list)### IMap
A _map_ is an ordered (_possibly immutable_) series of key values pairs.
- extends `ICollection, ArrayAccess`
- [see Mutable map](#mutable-map)
- [see Immutable map](#immutable-map)### ISeq
A _sequence_ is a logical series of elements all of one type.
- extends `ICollection`
- [see Immutable seq](#immutable-seq)### ITuple
A _tuple_ is a grouping of unnamed but ordered values, possibly of different types.
- extends `IEnumerable`, `ArrayAccess`, `Stringable`
- [see Immutable tuple](#immutable-tuple)### Interfaces
- `Mutable\Generic\ICollection`, `Mutable\Generic\IList`, `Mutable\Generic\IMap`### Mutable\Generic\ListCollection
- implements `Mutable\Generic\IList`
- is `eager` as possible### Mutable\Generic\Map
- implements `Mutable\Generic\IMap`
- is `eager` as possible### Mutable\Generic\PrioritizedCollection
- implements `IEnumerable`
- holds items with `generic` type by `priority`
- is `eager` as possible#### Example of strategies by priority
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.
```php
// initialization of strategies
/** @phpstan-var PrioritizedCollection $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);
}
}
```## Immutable Collections
- `internal state` of Immutable\Collection instance will `never change` from the outside (it is `readonly`)
```php
$list = new Immutable\ListCollection();
$listWith1 = $list->add(1);// $list != $listWith1
echo $list->count(); // 0
echo $listWith1->count(); // 1
```
- `$list` is still an empty `Immutable\ListCollection`
- `$listWith1` is new instance of `Immutable\ListCollection` with value `1`### Interfaces
- `Immutable\Generic\ICollection`, `Immutable\Generic\IList`, `Immutable\Generic\IMap`, `Immutable\Generic\ISeq`, `Immutable\ITuple`### Immutable\Generic\ListCollection
- implements `Immutable\Generic\IList`
- is `eager` as possible### Immutable\Generic\Map
- implements `Immutable\Generic\IMap`
- is `eager` as possible### Immutable\Seq
- implements `Immutable\Generic\ISeq`
- is `lazy` as possible (_even could be `Infinite`_)
```php
$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]
```### Immutable\Generic\KVPair
- always has a `Key` and the `Value`
- key is restricted to `int|string` so it may be used in the `foreach` as a key
- can contain any values### Immutable\Tuple
- implements `Immutable\ITuple`
- must have at least 2 values (_otherwise it is just a single value_)
- is `eager` as possible
- allows `destructuring`, `matching` and `parsing`/`formatting`
- can contain any scalar values and/or arrays
- in string representation of a `Tuple`, array values must be separated by `;` (_not by `,`_)#### Parsing
```php
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']]
```#### Matching and comparing
```php
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'); // true
```#### Parsing and matching
```php
Tuple::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)"
```#### Formatting
```php
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', '[email protected]'])->toStringForUrl(); // '(mail,"[email protected]")'
```#### Destructuring
```php
$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; // 3
```#### Unpacking
```php
sprintf('Title: %s | Value: %s', ...Tuple::of('foo', 'bar')); // "Title: foo | Value: bar"
```#### Merging
- merging `Tuples` will automatically flat them (_see last example below_)
```php
$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')
```#### Merging and matching
```php
$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)."
```## Sequences and lazy mapping
- if your `Sequence` get mapped and filtered many times (_for readability_), it is not a problem
- `map -> map -> filter -> map -> filter -> map` will 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## Plans for next versions
- use `Symfony/Stopwatch` in unit tests
- _even better_ documentation ([current](https://github.com/MortalFlesh/MFCollectionsPHP/wiki))