PHPackages                             slava-basko/functional-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. slava-basko/functional-php

ActiveLibrary

slava-basko/functional-php
==========================

Collection of php functions that allows you to write code in a declarative way.

1421PHPCI passing

Since Feb 8Pushed 5mo ago2 watchersCompare

[ Source](https://github.com/slava-basko/functional-php)[ Packagist](https://packagist.org/packages/slava-basko/functional-php)[ RSS](/packages/slava-basko-functional-php/feed)WikiDiscussions main Synced 1mo ago

READMEChangelogDependenciesVersions (3)Used By (0)

Functional PHP 🐘
================

[](#functional-php-)

Collection of PHP functions that allows you to write code in a declarative way.

General ⚙️
----------

[](#general-️)

#### Name convention

[](#name-convention)

The `snake_case` is used to be closer to a PHP native functions.

#### Zero dependencies

[](#zero-dependencies)

This library has no dependencies on any external libs.

#### PHP 5.5+

[](#php-55)

Supporting PHP versions since 5.5. Why? Because legacy projects also deserve a bit of functional approach.

#### "Data last" principe

[](#data-last-principe)

The data to be operated on is generally supplied last (last functions argument). Functions is more convenient for currying in this way.

#### Functions are curried by default

[](#functions-are-curried-by-default)

This allows us to be more efficient in building new functions from old ones simply by not supplying the final parameter.

---

The last two points together make it easy to build functions as sequences of simpler functions, each of which transforms the data and passes it along to the next.

Docs 📚
------

[](#docs-)

Here you can find available function.

[Functions list](docs/functions.md)

Other useful things.

- Identity / aka simple container
- Constant / container that holds value unchanged
- [Maybe / container that holds `value` or `Nothing`](docs/maybe.md)
- [Either / container that holds two values, `Left` aka error and `Right` aka success](docs/either.md)
- [Optional / container that holds `value` including NULL or `Nothing`](docs/optional.md)
- IO / container that holds impure function that returns value

OOP 🤝 FP
--------

[](#oop--fp)

The purpose of this library is not to replace imperative and OOP. They can be combined, and I believe they should be combined because any of these approaches is not a silver bullet.

I will omit the theory about functional programming because you can find a lot of information about it yourself. But I want to show you examples.

#### Collection example

[](#collection-example)

Let's imagine that you are using collection lib, and you want to upper all elements. You need to write things like this:

```
$collection = new Collection(['one']);
$collection->map(function ($value) {
    return strtoupper($value);
});
```

You can get an error like `ArgumentCountError : strtoupper() expects exactly 1 argument, X given`when you will write `$collection->map('strtoupper');`. Only user defined functions does not throw an exception when called with more arguments. But you can do this:

```
$collection = new Collection(['one']);
$collection->map(unary('strtoupper'));
```

Bam! You get less bloated code without `function`, `{`, `return`, `}`, `;`. Function `unary` is a higher-order function, it takes function with any arity and return new function that accept only one argument.

That's what I mean when I talk about combining imperative/OOP and functional code.

One more example with the collection. We need to filter users by `isActive` method for example.

```
$collection = new Collection([$user1, $user2, $user3]);

$collection->filter(function ($user) {
    return $user->isActive();
});

// VS

$collection->filter(invoker('isActive'));
```

#### Point-free example

[](#point-free-example)

Now let's consider the second example when we need to calculate qty of items in order.

```
$products = [
    [
        'description' => 't-shirt',
        'qty' => 2,
        'value' => 20
    ],
    [
        'description' => 'jeans ',
        'qty' => 1,
        'value' => 30
    ],
    [
        'description' => ' boots',
        'qty' => 1,
        'value' => 40
    ],
];

$imperativeTotalQty = 0;
foreach ($products as $product) {
    $imperativeTotalQty += $product['qty'];
}

// OR
$totalQty = compose(sum, pluck('qty'))($products);
```

You can read code `compose(sum, pluck('qty'))` like `sum of 'quantity' properties`. Ok, I understand that this could be a bit odd for you. You get used to writing code differently.

#### Pipe and partial application

[](#pipe-and-partial-application)

We have a `$products[]` and we need to create a common description from the `description` property of each one. So, here are the basic steps:

1. Fetch property 'description' from products.
2. Strip whitespace from the beginning and end of each value.
3. Remove empty elements.
4. Join elements with commas.
5. Cut generated description up to 34 characters.
6. Trim the comma at the end if present.

The imperative way could be:

```
$commonDescription = trim(substr(implode(', ', array_filter(array_map('trim', array_column($products, 'description')), 'strlen')), 0, 34), ', ');
// OR
$commonDescription = trim(
    substr(
        implode(
            ', ',
            array_filter(
                array_map(
                    'trim',
                    array_column($products, 'description')
                ),
                'strlen'
            )
        )
        , 0, 34
    ),
    ', '
);
```

Quite big cognitive load 🤯. Let's try to reorder it and make it more readable.

```
$descriptions = array_column($products, 'description');
$trimmedDescriptions = array_map('trim', $descriptions);
$nonEmptyDescriptions = array_filter($trimmedDescriptions, 'strlen');
$descriptionString = implode(', ', $nonEmptyDescriptions);
$shortDescription = substr($descriptionString, 0, 34);
$commonDescription = trim($shortDescription, ', ');
```

Now it's more readable, but we need to mess with states.

The functional code could be like this:

```
$commonDescription = pipe(
    pluck('description'),
    map(unary('trim')),
    select(unary('strlen')),
    join(', '),
    take(34),
    partial_r('trim', ', ')
)($products);
```

This is precisely what we need. It's in a natural order. No intermediate states.

One more example where we need to get user initials.

```
$initials = pipe(
    partial('explode', ' '),
    map(pipe(take(1), partial_r(concat, '.'))),
    join(' ')
)('Slava Basko');

// $initials = 'S. B.'
```

I know, maybe that line looks weired to you, but the idea of composing functions without having to stop at every step to consider the control flow structures and what the parameters are going to be named is pretty powerful.

#### What about some real-life example? 🤔

[](#what-about-some-real-life-example-)

No problem, this project has a doc auto-generation script. Written in an entirely point-free manner.

[Show me your doc\_generator.php](internal/doc_generator.php)

No variable were harmed during script development.

License ⚖️
----------

[](#license-️)

Use as you want. No liability or warranty from me. Can be considered as MIT.

###  Health Score

22

—

LowBetter than 22% of packages

Maintenance48

Moderate activity, may be stable

Popularity10

Limited adoption so far

Community11

Small or concentrated contributor base

Maturity19

Early-stage or recently created project

 Bus Factor1

Top contributor holds 85.8% 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.

### Community

Maintainers

![](https://www.gravatar.com/avatar/6a8e37b4c52b1cfe4f5d64f420d21875ebdeb53954d6859d6d663038b95ef3cc?d=identicon)[slava-basko](/maintainers/slava-basko)

---

Top Contributors

[![slava-basko](https://avatars.githubusercontent.com/u/4376953?v=4)](https://github.com/slava-basko "slava-basko (121 commits)")[![weirdan](https://avatars.githubusercontent.com/u/57403?v=4)](https://github.com/weirdan "weirdan (20 commits)")

---

Tags

declarativefunctionalfunctional-programmingimmutablephp

### Embed Badge

![Health badge](/badges/slava-basko-functional-php/health.svg)

```
[![Health](https://phpackages.com/badges/slava-basko-functional-php/health.svg)](https://phpackages.com/packages/slava-basko-functional-php)
```

PHPackages © 2026

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