PHPackages                             crell/fp - 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. crell/fp

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

crell/fp
========

Functional utilities for PHP 8 and later

1.0.0(2y ago)91429.8k—6%311LGPL-3.0-or-laterPHPPHP ~8.1CI passing

Since Jul 23Pushed 7mo ago4 watchersCompare

[ Source](https://github.com/Crell/fp)[ Packagist](https://packagist.org/packages/crell/fp)[ Docs](https://github.com/Crell/fp)[ GitHub Sponsors](https://github.com/Crell)[ RSS](/packages/crell-fp/feed)WikiDiscussions master Synced 1mo ago

READMEChangelogDependencies (3)Versions (24)Used By (11)

Functional utilities
====================

[](#functional-utilities)

[![Latest Version on Packagist](https://camo.githubusercontent.com/ab3d85eeb511b74a981bd154be4ee21df5c01d5a0364464ff09deab03dad4d93/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6372656c6c2f66702e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/crell/fp)[![Software License](https://camo.githubusercontent.com/bf1c19b4a07c841715e713542bd6e9c2aaf75ffa2ee2aa3987814d99311f799b/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4c47504c76332d677265656e2e7376673f7374796c653d666c61742d737175617265)](LICENSE.md)[![Total Downloads](https://camo.githubusercontent.com/c69251f28568d358c92f8f594cd5eef49e247b58c9a41dcad7b23841e3c94ed2/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f6372656c6c2f66702e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/crell/fp)

This library contains functional utilities intended for use with PHP 8.1 and later. Its primary tool is the `pipe()` function, which takes a starting argument and then a series of callables to "pipe" that argument through. Most other functions are utilities that produce a closure that takes the return from a previous `pipe()` step as its only argument.

That provides a reasonably good experience for building multi-step functional pipelines and composition, at least until PHP itself gets a proper pipe operator. :-) It also provides a convenient point-free style experience.

Install
-------

[](#install)

Via Composer

```
$ composer require crell/fp
```

Usage
-----

[](#usage)

### Pipes and composition

[](#pipes-and-composition)

The most important function in this library is `pipe()`. It takes an arbitrary number of arguments. The first is whatever starting value you want to send through a sequence of functions. The rest are any unary callable (single-argument callable) that returns a value. `pipe()` will pass the first value to the first callable, then pass the result of that to the second callable, then pass the result of that to the third callable, and so on until the pipe ends. The final result will then be returned.

For a trivial example:

```
use function Crell\fp\pipe;

$result = pipe(5,
  static fn ($in) => $in ** 4,     // Returns 625
  static fn ($in) => $in / 4,     // Returns 156.25
  static fn ($in) => (string)$in,  // Coerces the number to a string
  strlen(...),                    // Returns the length of the string
);
// $result is now 6, because "156.25" has 6 characters in it.
```

There is also a similar method `compose()`, which takes only an arbitrary number of callables and produces a function that will take one argument and pass it through all of them the same way. The difference is that `compose()` returns the resulting callable, while `pipe()` executes immediately. Technically it is trivial to implement either one in terms of the other, but for performance reasons they are separate.

### Pipeable functions

[](#pipeable-functions)

As stated, `pipe()` works only with unary functions. PHP has numerous functions that are not unary, however, including many of the most useful array and string functions. For that reason, this library provides alternate, pipe-friendly versions of most common operations. All of them will take some number of arguments and return a Closure that has those arguments partially applied; that is, the provided arguments get "saved" and used when the returned function is invoked. Normally that would be within a `pipe()` chain, but they may be directly invoked as well if desired.

For example, the `explode()` function (which is namespaced to not conflict with the global function), takes a single argument, the delimiter. Its return value is a callable that will, when called with a string, call the built-in `\explode()` function with the provided string and the saved delimiter.

```
use function Crell\fp\explode;

$result = pipe("Hello world",
  explode(' '),  // Produces ['Hello', 'world']
  count(...),    // Returns tne number of array elements, which is 2
);
// $result is now 2

// or

$words = explode(' ')("Hello World");
// $words is now ['Hello', 'world']
```

The upshot of this approach is that *nearly all needle/haystack questions go away*, as either the value to operate on is subsumed into the pipe itself or very clearly provided as a secondary argument list.

Most functions will simply wrap and fall back to standard-lib functions where possible.

### String functions

[](#string-functions)

All functions below are in the `Crell\fp` namespace.

`explode(string $delimiter)` - Explode a piped string using `$delimiter`.

`implode(string $glue)` - Implode a piped array using `$glue`.

`replace(array|string $find, array|string $replace)` - Does a find/replace in a piped string, using [`str_replace()`](https://www.php.net/str_replace)

### Object functions

[](#object-functions)

All functions below are in the `Crell\fp` namespace.

`prop(string $prop)` - Returns the `$prop` public property of a piped object.

`method(string $method, ...$args)` - Invokes `$method` on a piped object using `$args` as arguments. (Both positional and named arguments are supported.)

`typeIs(string $type)` - Returns `true` if a piped value is of the specified type, `false` otherwise. Legal types are `int`, `string`, `float`, `bool`, `array`, `resource`, or a class/interface name. This will usually be the last function in a pipe.

### Array functions

[](#array-functions)

All functions below are in the `Crell\fp` namespace.

In many cases below, there are multiple versions of a function. There are two axes on which they vary: Whether they return an array or an iterable, and whether they operate on array keys as well.

PHP's built-in array functions do not accept iterables; Nearly all the functions below do. Those that begin with `a` will return an array, even if what was passed in is an iterator. Those that begin with `it` will return a Generator, which will produce values lazily.

Unless otherwise specified, functions will operate only on array values. Array keys will be explicitly ignored and not passed to the provided callback, but preserved. If a function has the suffix `withKeys`, then the key will be made available to the provided callback.

These must be separated due to the combination of three ill-interacting PHP features.

1. All arrays are associative, but some are short-circuited to have list-like keys, rather than having lists and maps be two separate constructs.
2. PHP supports optional arguments, which means some functions will misbehave if passed an array key as an optional second argument.
3. PHP user-space functions will silently ignore excess arguments but bundled functions will fail if called with excess arguments.

The upshot of these design choices is that it is not possible to reliably build a function that applies a callable to an array without knowing if the keys are important. That distinction must be made by the developer. The non-key versions require a callback with a single argument only (the array value), while the `withKeys` version will pass the value and key as two separate arguments to the callback.

Deciding whether to use the greedy (array) or lazy (iterable) versions of functions depends on the tradeoffs appropriate to your use case. As a general rule, the greedy version will be faster but may use more memory, while the lazy version will use less memory but may be slower. How much the difference is will vary widely with the specific use case.

#### Mapping

[](#mapping)

Applies a provided callable to each entry in an iterable, producing a new iterable with the same keys as the source, but with the values replaced with the corresponding callback result.

- `amap(callable $c)`
- `amapWithKeys(callable $c)`
- `itmap(callable $c)`
- `itmapWithKeys(callable $c)`

### Filtering

[](#filtering)

Produces a new array containing only those array entries where the callable returns true. Array keys are preserved. If no callback is provided, a default of "is truthy" is used, just like PHP's native [`array_filter()`](https://www.php.net/array_filter).

- `afilter(?callable $c = null)`
- `afilterWithKeys(?callable $c = null)`
- `itfilter(?callable $c = null)`
- `itfilterWithKeys(?callable $c = null)`

### Collecting

[](#collecting)

The `collect()` function will accept a piped iterable or array, and produce an array. It's really just a wrapper around `iterator_to_array()` that guards against passing it an array, which is not supported in PHP 8.1. In PHP 8.2 and later, this function is equivalent to just using `iterator_to_array(...)` directly in a pipe, as it now accepts arrays as well.

### Reducing

[](#reducing)

Reducing, also known as `fold` or `foldl` in some languages, involves iteratively applying an operation across an array to produce a single final result. See [`array_reduce()`](https://www.php.net/array_reduce) for more details.

- `reduce(mixed $init, callable $c)` - Starting with `$init`, `$c` will be called with `$init` and each element in a piped iterable, and the result used as `$init` for the next entry. The callable signature is `($runningValue, $valueFromTheArray)`. The return from the last callable invocation is returned.
- `reduceWithKeys(mixed $init, callable $c)` - Same as `reduce()`, but the callback signature is `($runningValue, $valueFromTheArray, $keyFromTheArray)`.
- `reduceUntil(mixed $init, callable $c, callable $stop)` - Same as `reduce()`, but after each iteration `$stop($runningValue)` is called. If that returns true, the process stops early and whatever the current running value is will be returned.

### First, conditionally

[](#first-conditionally)

Several functions provide a way to obtain the first value in a sequence that meets some criteria. In all cases, they return null if nothing does.

- `first(callable $c)` - Returns the first value in a piped iterable for which `$c` returns `true`.
- `firstWithKeys(callable $c)` - Same as `first()`, but the callback is passed the value and key of each entry rather than just the value.
- `firstValue(callable $c)` - Invokes the provided callable on each item in a piped iterable, and returns the first result that is truthy, according to PHP.
- `firstValueWithKeys(callable $c)` - Same as `firstValue()`, but the callback is passed the value and key of each entry rather than just the value.

### Miscellaneous functions

[](#miscellaneous-functions)

- `indexBy(callable $keyMaker)` - Takes a piped array and returns a new array with the same values, but the key for each value is the result of calling `$keyMaker` with the value.
- `keyedMap(callable $values, ?callable $keys = null)` - Produces a new array from a piped array, in which the keys are the result of calling `$keys($key, $value)` and the values are the result of calling `$values($key, $value)`. If no `$keys` callback is specified, a default is provided that just indexes the entries numerically.
- `any(callable $c)` - Returns true if `$c` returns `true` for any value in a piped iterable. It may not be invoked on all items.
- `anyWithKeys(callable $c)` - Same as `any()`, but the callback is passed the value and key of each entry rather than just the value.
- `all(callable $c)` - Returns true if `$c` returns `true` for all value in a piped iterable. It may not be invoked on all items.
- `allWithKeys(callable $c)` - Same as `all()`, but the callback is passed the value and key of each entry rather than just the value.
- `flatten(array $arr)` - Accepts a multidimensional piped array and returns all the same values, but flattened into a single-dimensional sequential array.
- `append(mixed $value, mixed $key = null)` - Returns a piped array, but with the provided value added. If `$key` is provided, the value is assigned to that key regardless of whether it already exists. If not, the value is appended with `[]` and PHP's normal array handling applies.
- `atake(int $count)` - Accepts a piped iterable and returns an array, consisting of the first `$count` items from the iterable/array, or all the items if there are fewer than `$count`.
- `ittake(int $count)` - Accepts a piped iterable and returns an iterable, consisting of the first `$count` items from the iterable/array, or all the items if there are fewer than `$count`.
- `headtail(mixed $init, callable $first, callable $rest)` - Similar to `reduce()`, but uses a different reducing function for the first item.

### Utility functions

[](#utility-functions)

The following functions are not designed to be used with `pipe()`, but are more "traditional" functions. That said, they may be referenced as a first-class-closure.

- `iterate(mixed $init, callable $mapper)` - Produces an infinite list Generator. The first item is `$init`, the second is the result of calling `$mapper($init)`, the third is the result of calling `$mapper` on that result, etc. Note: This generator produces an infinite list! Make sure you have some termination check when calling it to avoid iterating forever.
- `nth(int $count, mixed $init, callable $mapper)` - Similar to `iterate()`, but returns the `$count`th item from the sequence and then stops.
- `head(array $a)` - Returns the first item from an array, or `null` if the aray is empty.
- `tail(array $a)` - Returns all but the first item from an array.

### Utility traits

[](#utility-traits)

`Crell/fp` also provides two traits to aid usage of objects in a functional environment.

#### Evolvable

[](#evolvable)

The `Evolvable` trait provides a single method, `with()`, which accepts a variadic list of named arguments. It will produce a new copy of the same object, but with the provided values assigned to properties of the same name. Note that because it runs in private scope, visibility is ignored. This may be undesireable in some cases.

This trait is most useful with `readonly` classes:

```
use Crell\fp\Evolvable;

readonly class Person
{
    use Evolvable;

    public function __construct(
        public string $name,
        public int $age,
        public string $jobTitle,
    ) {}
}

$p = new Person("Larry");
$p2 = $p->with(age: 18, jobTitle: "Developer");
```

#### Newable

[](#newable)

Constructor calls in PHP are screwy, and cannot be easily chained or piped. The `Newable` trait provides a simple standard static method that wraps the constructor, making it possible to chain and pipe. Its arguments are variadic and passed to the constructor verbatim.

```
use Crell\fp\Newable;

readonly class Person
{
    use Newable;

    public function __construct(
        public string $name,
        public int $age,
        public string $jobTitle,
    ) {}
}

$p = Person::new("Larry", 18, "Developer");
```

Examples
--------

[](#examples)

It may not be entirely obvious how all of the above fit together. To help make it clear, here's some examples of pipes and their functions in action.

The following example will take an input file name `$inputFile`, load its content into memory, trim whitespace, split it by lines into an array, call a custom `pairUp()` function on that array, filter the resulting array, and then count the remaining values. All of that in one simple statment.

```
$result = pipe($inputFile,
    file_get_contents(...),
    trim(...),
    explode(PHP_EOL),
    pairUp(...),
    afilter(static fn($v): bool => $v[0] > $v[1]),
    count(...),
);
```

This example uses a custom function to read lines from a file lazily. That produces a lazy generator, which is then passed to `itmap()` which will apply `parse()` to each item in the generator, but is itself a generator so is also lazy. `parse()` produces a `Step` object from each line of input. `reduce()` therefore receives an iterable of `Step` objects, and applies `move()` to each one in turn, from a starting point. Each time, `move()` returns a new `Position`.

In other words, these few lines of code constitute a complete script parsing engine, albeit a simple one. Because each step is lazy, only one `Step` is loaded into memory at once.

```
function lines(string $file): iterable
{
    $fp = fopen($file, 'rb');

    while ($line = fgets($fp)) {
        yield trim($line);
    }

    fclose($fp);
}

function parse(string $line): Step
{
    [$cmd, $size] = \explode(' ', $line);
    return new Step(cmd: Command::from($cmd), size: (int)$size);
}

$end = pipe($inputFile,
    lines(...),
    itmap(parse(...)),
    reduce(new Position(0, 0), move(...)),
);
```

The alternate version below uses the greedy `amap()` instead. That will produce an already-computed array of `Step` objects rather than a generator that produces `Step` objects.

```
$end = pipe($inputFile,
    lines(...),
    amap(parse(...)),
    reduce(new Position(0, 0), move(...)),
);
```

For more detailed examples, see the following articles that solved 10 days of Advent of Code 2021 using `Crell/fp`.

- [Day 1](https://peakd.com/php/@crell/aoc2021-day1): Function composition, pipes, and partial application.
- [Day 2](https://peakd.com/php/@crell/aoc2021-day2): Map, reduce, generators, immutable objects, and with-er methods.
- [Day 3](https://peakd.com/php/@crell/aoc2012-day3): Recursion, memoization, and bits.
- [Day 4](https://peakd.com/php/@crell/aoc2021-day4): First, head, array flattening, and handling state.
- [Day 5](https://peakd.com/php/@crell/aoc2021-day5): Zip and nested pipes.
- [Day 6](https://peakd.com/php/@crell/aoc2021-day6): Efficiency and dealing with infinite streams.
- [Day 7](https://peakd.com/php/@crell/aoc2021-day7): Functional means and medians
- [Day 8](https://peakd.com/php/@crell/aoc2021-day8): Encoding, decoding, and the value of first-class function thinking.
- [Day 9](https://peakd.com/php/@crell/aoc2021-day9): More fun with recursion.
- [Day 10](https://peakd.com/php/@crell/aoc2021-day10): Reduction and recursion, and how to swap between them.

Change log
----------

[](#change-log)

Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.

Contributing
------------

[](#contributing)

Please see [CONTRIBUTING](CONTRIBUTING.md) and [CODE\_OF\_CONDUCT](CODE_OF_CONDUCT.md) for details.

Security
--------

[](#security)

If you discover any security related issues, please use the [GitHub security reporting form](https://github.com/Crell/fp/security) rather than the issue queue.

Credits
-------

[](#credits)

- [Larry Garfield](https://github.com/Crell)
- [All Contributors](../../contributors)

License
-------

[](#license)

The Lesser GPL version 3 or later. Please see [License File](LICENSE.md) for more information.

###  Health Score

51

—

FairBetter than 96% of packages

Maintenance45

Moderate activity, may be stable

Popularity50

Moderate usage in the ecosystem

Community22

Small or concentrated contributor base

Maturity70

Established project with proven stability

 Bus Factor1

Top contributor holds 98.9% 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 ~37 days

Recently: every ~165 days

Total

23

Last Release

933d ago

Major Versions

0.4.0 → 1.0.02023-10-28

PHP version history (2 changes)0.1.0PHP ~8.0

0.4.0PHP ~8.1

### Community

Maintainers

![](https://www.gravatar.com/avatar/12e28c223b88445f07d697c8805bd856066c947f70b535f6a7e00d2cb311c3c2?d=identicon)[Crell](/maintainers/Crell)

---

Top Contributors

[![Crell](https://avatars.githubusercontent.com/u/254863?v=4)](https://github.com/Crell "Crell (88 commits)")[![stof](https://avatars.githubusercontent.com/u/439401?v=4)](https://github.com/stof "stof (1 commits)")

---

Tags

arrayfunctionalfp

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

![Health badge](/badges/crell-fp/health.svg)

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

###  Alternatives

[doctrine/collections

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

6.0k411.1M1.2k](/packages/doctrine-collections)[symfony/property-access

Provides functions to read and write from/to an object or array using a simple string notation

2.8k295.3M2.5k](/packages/symfony-property-access)[nette/utils

🛠 Nette Utils: lightweight utilities for string &amp; array manipulation, image handling, safe JSON encoding/decoding, validation, slug or strong password generating etc.

2.1k394.3M1.5k](/packages/nette-utils)[league/config

Define configuration arrays with strict schemas and access values with dot notation

564302.2M24](/packages/league-config)[cuyz/valinor

Dependency free PHP library that helps to map any input into a strongly-typed structure.

1.5k9.2M108](/packages/cuyz-valinor)[openlss/lib-array2xml

Array2XML conversion library credit to lalit.org

31052.5M47](/packages/openlss-lib-array2xml)

PHPackages © 2026

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