PHPackages                             qaribou/immutable.php - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. qaribou/immutable.php

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

qaribou/immutable.php
=====================

Immutable, highly-performant collections, well-suited for functional programming and memory-intensive applications.

2.0.0(6y ago)344146.0k↓30.2%21[2 issues](https://github.com/jkoudys/immutable.php/issues)MITPHPPHP &gt;=7.1.0CI failing

Since Oct 21Pushed 6y ago13 watchersCompare

[ Source](https://github.com/jkoudys/immutable.php)[ Packagist](https://packagist.org/packages/qaribou/immutable.php)[ Docs](http://github.com/jkoudys/immutable.php)[ RSS](/packages/qaribou-immutablephp/feed)WikiDiscussions master Synced 1mo ago

READMEChangelog (1)Dependencies (1)Versions (6)Used By (0)

immutable.php
=============

[](#immutablephp)

Immutable collections, well-suited for functional programming and memory-intensive applications. Runs especially fast in PHP7.

Basic Usage
-----------

[](#basic-usage)

### Quickly load from a simple array

[](#quickly-load-from-a-simple-array)

```
use Qaribou\Collection\ImmArray;
$polite = ImmArray::fromArray(['set', 'once', 'don\'t', 'mutate']);
echo $polite->join(' ');
// => "set once don't mutate"
```

### Map with a callback

[](#map-with-a-callback)

```
$yelling = $polite->map(function($word) { return strtoupper($word); });

echo
// =>   A Wonderful List
// =>
// =>     SETONCEDON'TMUTATE
// =>
// =>
```

### Sort with a callback

[](#sort-with-a-callback)

```
echo 'Os in front: ' .
    $yelling
        ->sort(function($word) { return (strpos('O', $word) === false) ? 1 : -1; })
        ->join(' ');
// => "Os in front: ONCE DON'T MUTATE SET"
```

### Slice

[](#slice)

```
echo 'First 2 words only: ' . $polite->slice(0, 2)->join(' ');
// => "set once"
```

### Load big objects

[](#load-big-objects)

```
// Big memory footprint: $fruits is 30MB on PHP5.6
$fruits = array_merge(array_fill(0, 1000000, 'peach'), array_fill(0, 1000000, 'banana'));

// Small memory footprint: only 12MB
$fruitsImm = ImmArray::fromArray($fruits);

// Especially big savings for slices -- array_slice() gives a 31MB object
$range = range(0, 50000);
$sliceArray = array_slice($range, 0, 30000);

// But this is a 192 _byte_ iterator!
$immSlice = ImmArray::fromArray($range)->slice(0, 30000);
```

### Filter

[](#filter)

```
// Yes, we have no bananas
$noBananas = $fruitsImm->filter(function($fruit) { return $fruit !== 'banana'; });
```

### Concat (aka merge)

[](#concat-aka-merge)

```
$ia = ImmArray::fromArray([1,2,3,4]);
$ib = ImmArray::fromArray([5,6,7,8]);

// Like slice(), it's just a little iterator in-memory
$ic = $ia->concat($ib);
// => [1,2,3,4,5,6,7,8]
```

### Reduce

[](#reduce)

```
$fruits = ImmArray::fromArray(['peach', 'plum', 'orange']);

$fruits->reduce(function($last, $cur, $i) {
  return $last . '{"' . $i . '":' . $cur . '"},';
}, '"My Fruits: ');

// => My Fruits: {"0":"peach"},{"1":"plum"},{"2":"orange"},
```

### Find

[](#find)

```
$fruits = ImmArray::fromArray(['peach', 'plum', 'banana', 'orange']);

$fruitILike = $fruits->find(function ($fruit) {
  return $fruit === 'plum' || $fruit === 'orange';
});

// => 'plum'
```

### Array accessible

[](#array-accessible)

```
echo $fruits[1];
// => "plum"
```

### Countable

[](#countable)

```
count($fruits);
// => 3
```

### Iterable

[](#iterable)

```
foreach ($fruits as $fruit) {
    $fruitCart->sell($fruit);
}
```

### Load from any `Traversable` object

[](#load-from-any-traversable-object)

```
$vegetables = ImmArray::fromItems($vegetableIterator);
```

### Even serialize back as json!

[](#even-serialize-back-as-json)

```
echo json_encode(
    ['name' => 'The Peach Pit', 'type' => 'fruit stand', 'fruits' => $noBananas]
);
// => {"name": "The Peach Pit", "type": "fruit stand", "fruits": ["peach", "peach", .....
```

Install
-------

[](#install)

immutable.php is available on composer via packagist.

```
composer require qaribou/immutable.php
```

Why
---

[](#why)

This project was born out of my love for 3 other projects: Hack (), immutable.js (), and the Standard PHP Library (SPL) datastructures ().

- Both Hack and immutable.js show that it's both possible, and practical to work with immutable data structures, even in a very loosely-typed language
- The Hack language introduced many collections of its own, along with special syntax, which are unavailable in PHP.
- SPL has some technically excellent, optimized datastructures, which are often impractical in real world applications.

Why didn't I just use SplFixedArray directly?
---------------------------------------------

[](#why-didnt-i-just-use-splfixedarray-directly)

The SplFixedArray is very nicely implemented at the low-level, but is often somewhat painful to actually use. Its memory savings vs standard arrays (which are really just variable-sized hashmaps -- the most mutable datastructure I can think of) can be enormous, though perhaps not quite as big a savings as it will be once PHP7 gets here. By composing an object with the SplFixedArray, we can have a class which solves the usability issues, while maintaining excellent performance.

### Static-Factory Methods

[](#static-factory-methods)

The SPL datastructures are all very focused on an inheritance-approach, but I found the compositional approach taken in hacklang collections to be far nicer to work with. Indeed, the collections classes in hack are all `final`, implying that you must build your own datastructures composed of them, so I took the same approach with SPL. The big thing you miss out on with inheritance is the `fromArray` method, which is implemented in C and quite fast, however:

```
class FooFixed extends SplFixedArray {}
$foo = FooFixed::fromArray([1, 2, 3]);
echo get_class($foo);
// => "SplFixedArray"
```

So you can see that while the static class method `fromArray()` was called from a FooFixed class, our `$foo` is not a `FooFixed` at all, but an `SplFixedArray`.

ImmArray, however, uses a compositional approach so we can statically bind the factory methods:

```
class FooFixed extends ImmArray {}
$foo = FooFixed::fromArray([1, 2, 3]);
echo get_class($foo);
// => "FooFixed"
```

Now that dependency injection, and type-hinting in general, are all the rage, it's more important than ever that our datastructures can be built as objects for the class we want. It's doubly important, because implementing a similar `fromArray()` in PHP is many times slower than the C-optimized `fromArray()` we use here.

### De-facto standard array functions

[](#de-facto-standard-array-functions)

The good ol' PHP library has a pile of often useful, generally well-performing, but crufty array functions with inconsistent interfaces (e.g. `array_map($callback, $array)` vs `array_walk($array, $callback)`). Dealing with these can be considered one of PHP's quirky little charms. The real problem is, these functions all have one thing in common: your object *must* be an array. Not arraylike, not ArrayAccessible, not Iterable, not Traversable, etc., but an array. By building in functions so common in JavaScript and elsewhere, e.g. `map()`, `filter()`, and `join()`, one can easily build new immutable arrays by passing a callback to the old one.

```
$foo = ImmArray::fromArray([1, 2, 3, 4, 5]);
echo $foo->map(function($el) { return $el * 2; })->join(', ');
// => "2, 4, 6, 8, 10"
```

### Serialize as JSON

[](#serialize-as-json)

More and more, PHP is being used less for bloated, view-logic heavy applications, and more as a thin data layer that exists to provide business logic against a datasource, and be consumed by a client side or remote application. I've found most of what I write nowadays simply renders to JSON, which I'll load in a React.js or ember application in the browser. In the interest of being nice to JavaScript developers, it's important to send arrays as arrays, not "arraylike" objects which need to have a bunch of `Object.keys` magic used on them.e.g.

```
$foo = SplFixedArray::fromArray([1, 2, 3]);
echo json_encode($foo);
// => {"0":1,"1":2,"2":3}
```

The internal logic makese sense to a PHP dev here -- you're encoding properties, after all, but this format is undesirable when working in JS. Objects in js are unordered, so you need to loop through a separate counter, and lookup each string property-name by casting the counter back to string, doing a property lookup, and ending the loop once you've reached the length of the object keys. It's a silly PitA we often have to endure, when we'd much rather get back an array in the first place. e.g.

```
$foo = ImmArray::fromArray([1, 2, 3]);
echo json_encode($foo);
// => [1,2,3]
```

### Immutability

[](#immutability)

A special interface gives us an appropriate layer to enforce immutability. While the immutable.php datastructures implement `ArrayAccess`, attempts to push or set to them will fail.

```
$foo = new ImmArray();
$foo[1] = 'bar';
// => PHP Warning:  Uncaught exception 'RuntimeException' with message 'Attempt
to mutate immutable Qaribou\Collection\ImmArray object.' in
/project/src/Collection/ImmArray.php:169
```

### Alternative Iterators

[](#alternative-iterators)

PHP7
----

[](#php7)

It's well-known that callbacks are incredibly slow pre-PHPNG days, but once PHP7 becomes the standard the callback-heavy approach to functional programming needed by immutable.php will become far faster. For example, compare this basic test:

```
// Make 100,000 random strings
$bigSet = ImmArray::fromArray(array_map(function($el) { return md5($el); }, range(0, 100000)));

// Time the map function
$t = microtime(true);
$mapped = $bigSet->map(function($el) { return '{' . $el . '}'; });
echo 'map: ' . (microtime(true) - $t) . 's', PHP_EOL;

// Time the sort function
$t = microtime(true);
$bigSet->sort(function($a, $b) { return strcmp($a, $b); });
echo 'mergeSort: ' . (microtime(true) - $t) . 's', PHP_EOL;
```

On 5.6:

```
map: 0.30895709991455s
mergeSort: 6.610347032547s
```

On 7.0alpha2:

```
map: 0.01442813873291s
mergeSort: 0.58948588371277s
```

Holy moly! Running on my laptop, running the map function (which executes a callback) is 21x faster on PHP7. Running the stable mergesort algorithm is 11x faster on PHP7. Big maps and sorts will always be expensive, but PHP7 drops what may be a prohibitively expensive 300ms map, to a much more manageable 14ms.

###  Health Score

42

—

FairBetter than 90% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity50

Moderate usage in the ecosystem

Community21

Small or concentrated contributor base

Maturity62

Established project with proven stability

 Bus Factor1

Top contributor holds 83.6% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~482 days

Total

4

Last Release

2413d ago

Major Versions

1.1.0 → v2.x-dev2017-01-17

PHP version history (2 changes)1.0.0PHP &gt;=5.4.0

2.0.0PHP &gt;=7.1.0

### Community

Maintainers

![](https://www.gravatar.com/avatar/845413a62dc358463f16287fad4a6dbdb42735b59682f12a4230a20969fdb2a8?d=identicon)[jkoudys](/maintainers/jkoudys)

---

Top Contributors

[![jkoudys](https://avatars.githubusercontent.com/u/586985?v=4)](https://github.com/jkoudys "jkoudys (61 commits)")[![JarJak](https://avatars.githubusercontent.com/u/4981490?v=4)](https://github.com/JarJak "JarJak (7 commits)")[![localheinz](https://avatars.githubusercontent.com/u/605483?v=4)](https://github.com/localheinz "localheinz (2 commits)")[![royopa](https://avatars.githubusercontent.com/u/442991?v=4)](https://github.com/royopa "royopa (2 commits)")[![hansott](https://avatars.githubusercontent.com/u/3886384?v=4)](https://github.com/hansott "hansott (1 commits)")

---

Tags

collectionsfunctionalsplfixedarrayimmutablePHP7data structuresfp

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/qaribou-immutablephp/health.svg)

```
[![Health](https://phpackages.com/badges/qaribou-immutablephp/health.svg)](https://phpackages.com/packages/qaribou-immutablephp)
```

###  Alternatives

[doctrine/collections

PHP Doctrine Collections library that adds additional functionality on top of PHP arrays.

6.0k411.1M1.2k](/packages/doctrine-collections)[crell/fp

Functional utilities for PHP 8 and later

91429.8k11](/packages/crell-fp)[chemem/bingo-functional

A simple functional programming library.

716.9k3](/packages/chemem-bingo-functional)[boehm_s/fun

Functional programming utilities for PHP

301.8k](/packages/boehm-s-fun)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
