PHPackages                             maxonfjvipon/elegant-elephant - 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. maxonfjvipon/elegant-elephant

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

maxonfjvipon/elegant-elephant
=============================

Small library of PHP primitives in EO style

4.12.0(2y ago)124041[8 issues](https://github.com/maxonfjvipon/elegant-elephant/issues)1MITPHPPHP &gt;=8.0

Since Jan 30Pushed 2y ago2 watchersCompare

[ Source](https://github.com/maxonfjvipon/elegant-elephant)[ Packagist](https://packagist.org/packages/maxonfjvipon/elegant-elephant)[ RSS](/packages/maxonfjvipon-elegant-elephant/feed)WikiDiscussions master Synced 2d ago

READMEChangelog (10)Dependencies (5)Versions (39)Used By (1)

[![logo](https://github.com/maxonfjvipon/elegant-elephant/raw/fa2664c6e9c30c30ca255b0608615faacc8b6556/logo/logo.jpg)](https://github.com/maxonfjvipon/elegant-elephant/blob/fa2664c6e9c30c30ca255b0608615faacc8b6556/logo/logo.jpg)

ElegantElephant
===============

[](#elegantelephant)

ElegantElephant - small library of PHP primitives in EO style. Inspired by [Cactoos](https://github.com/yegor256/cactoos) from [@yegor256](https://github.com/yegor256).

[![EO principles respected here](https://camo.githubusercontent.com/5c7a27b75a8da4be52680df944abec4ccc4d0151df036e0793b8c9b16cbe3d1e/68747470733a2f2f7777772e656c6567616e746f626a656374732e6f72672f62616467652e737667)](https://www.elegantobjects.org)[![DevOps By Rultor.com](https://camo.githubusercontent.com/14899b1d90e7334dde7d856e9b9bdb64e54891f1cd06b528a386a9e555183b16/687474703a2f2f7777772e72756c746f722e636f6d2f622f6d61786f6e666a7669706f6e2f656c6567616e742d656c657068616e74)](http://www.rultor.com/p/maxonfjvipon/elegant-elephant)

[![Composer](https://github.com/maxonfjvipon/elegant-elephant/actions/workflows/composer.yml/badge.svg)](https://github.com/maxonfjvipon/elegant-elephant/actions/workflows/composer.yml)[![codecov](https://camo.githubusercontent.com/0cd32d46d3f08e9529c62f33dc42c687ed1dee723f7a10dfd8e40f4d899bb4bd/68747470733a2f2f636f6465636f762e696f2f6769746875622f6d61786f6e666a7669706f6e2f656c6567616e742d656c657068616e742f6272616e63682f6d61737465722f67726170682f62616467652e7376673f746f6b656e3d5630554c314659475857)](https://codecov.io/github/maxonfjvipon/elegant-elephant)[![Hits-of-Code](https://camo.githubusercontent.com/870195037e1a6b9e6ea4943b36c36ee0d2ee2c16b8ab71cd1554083705cac168/68747470733a2f2f686974736f66636f64652e636f6d2f6769746875622f6d61786f6e666a7669706f6e2f656c6567616e742d656c657068616e743f6272616e63683d6d6173746572)](https://hitsofcode.com/github/maxonfjvipon/elegant-elephant/view?branch=master)[![License](https://camo.githubusercontent.com/8bb50fd2278f18fc326bf71f6e88ca8f884f72f179d3e555e20ed30157190d0d/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d677265656e2e737667)](https://github.com/maxonfjvipon/elegant-elephant/blob/master/LICENSE)[![Tag](https://camo.githubusercontent.com/19ec1432dbc655f39efc8fcd8dc52861831d07079f88edd00e84aa9090e52bb5/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f7461672f6d61786f6e666a7669706f6e2f656c6567616e742d656c657068616e742e737667)](https://github.com/maxonfjvipon/elegant-elephant/releases)[![Total lines](https://camo.githubusercontent.com/8f0405b223e27dbb104b162bc27499a53b34254e4cdd51cdbf1f485f9f55e48c/68747470733a2f2f736c6f632e78797a2f6769746875622f6d61786f6e666a7669706f6e2f656c6567616e742d656c657068616e74)](https://sloc.xyz/github/maxonfjvipon/elegant-elephant)

**Motivation**
--------------

[](#motivation)

PHP was designed and created like a procedural language. Then PHP started support OOP paradigm, but it's not really pure OOP. We got classes but we still use them in a procedural way. This library enforces you to write real objects in a real OOP.

Principles
----------

[](#principles)

PrincipleYes/NoNo null➖No code in constructors✔️No getters and setters✔️No mutable objects✔️No static methods, not even private ones➖No instanceof, type casting, or reflection➖No public methods without a contract✔️No statements in test methods except assertThat✔️Getting started:
----------------

[](#getting-started)

### Requirements

[](#requirements)

- PHP &gt;= 8.0

### Installation

[](#installation)

```
composer require maxonfjvipon/elegant-elephant

```

Snippets
--------

[](#snippets)

To get flatten array:

```
// The result will be [0, 1, 2, 2, 3, 3, 4, 5]
(new ArrFlatten(
  [0, [1], [[2, 2], [3, 3]], [4, 5]],
  deep: 2
))->asArray();
```

To merge arrays:

```
/*
 * If user is admin, result array will contain permissions field
 *
 * [
 *    'name' => ...,
 *    'age' => ...,
 *    'permissions' => [...],
 *    'projects' => [
 *       ['name' => ..., 'created_at' => ...],
 *       [...],
 *       ...
 *    ]
 * ]
 */
(new ArrMerged(
  [
    'name' => $user->name,
    'age' => $user->age,
  ],
  new ArrIf(
    $user->isAdmin(),
    ArrOf::func(fn () => [
      'permissions' => [...]
    ])
  ),
  new ArrObject(
    'projects',
    new ArrMapped(
      $projects,
      fn (Project $project) => [
        'name' => $project->name,
        'created_at' => $project->created_at
      ]
    )
  )
))-asArray();
```

To manipulate with a text:

```
// To lower case
(new TxtLowered(
  TxtOf::str("Hello")
))->asString();

// To upper case
(new TxtUpper("Hello"))->asString();

// Join texts, if $isAdmin === TRUE the result will be "Hello Admin" else "Hello username, what'up"
(new TxtJoined([
  "Hello ",
  // Conditional text, behaves like first text if condition is TRUE, like second otherwise
  new TxtCond(
    $isAdmin,
    "Admin"
    TxtOf::func(fn () => $user->name())
  ),
  // Conditional text, behaves like first text if condition is TRUE, like empty string otherwise
  new TxtIf(
    !$isAdmin,
    ", what's up"
  )
]))->asString();
```

To get first `x` from array of `x y` points:

```
$points = [['x' => 1, 'y' => 1], ['x' => 2, 'y' => 2], [...], ...];

NumOf::any(
  new AtKey(
    'x',
    ArrOf::any(
      new FirstOf(
        $points
      )
    )
  )
)->asNumber(); // 1
```

To get length of filtered array:

```
(new LengthOf(
  new ArrFiltered(
    [1, 2, 3, 4, 5, 6],
    fn (int $num) => $num > 3
  )
))->asNum(); // 3
```

Complicated example from commercial project with no visible algorithm:

```
(new AtKey(                                                   // 12. Get element by key "pump" from given array
  'pump',
  ArrOf::any(                                                 // 11. Try to cast given element to array
    new FirstOf(                                              // 10. Get first element of given array
      new ArrCond(                                            // 9.1. If given sorted jockey pumps are not empty - take them
        new Not(
          new IsEmpty(
            $jockeyPumps = new ArrSticky(                     // 8. Cache given sorted jockey pumps
              new ArrSorted(                                  // 7. Sort given mapped jockey pumps by "cost" key
                new ArrMapped(                                // 6. Map given jockey pumps
                  new ArrCond(                                // 5.1. If optimized jockey pumps are not empty - take them
                    new Not(
                      new IsEmpty(
                        $optimized = new ArrSticky(           // 4. Cached optimized jockey pumps
                          new ArrFiltered(                    // 3. Filter cached jockey pumps with optimization
                            $dbPumps = new ArrSticky(         // 2. Cache jockey pumps
                              new ArrPumpsForJockeySelecting( // 1. Take jockey pumps from somewhere
                                $request->jockey_series_ids,
                                $request->accounting_rests
                              )
                            ),
                            $filterPump(threeFifths: true)
                          )
                        )
                      )
                    ),
                    $optimized,
                    new ArrFiltered(                           // 5.2. If opmtimized jockey pumps are empty - filter jockey pumps without optimization
                      $dbPumps,
                      $filterPump(threeFifths: false)
                    )
                  ),
                  fn (Pump $pump) => [
                    'pump' => $pump,
                    'cost' => $pump->priceByRates($rates),
                  ]
                ),
                'cost'
              )
            )
          )
        ),
        $jockeyPumps,
        [['pump' => null]]                                      // 9.2. or take [['pump' => null]] otherwise
      ),
    )
  ),
))->value();
```

Static methods
--------------

[](#static-methods)

Since PHP does not allow you to have more than one constructor in the class, some classes in the library have public static methods that return an instance of the class and replace secondary constructors. Some classes have private primary constructor to prevent user to use them in a wrong way.

For example take a look at the class [`ArrOf`](https://github.com/maxonfjvipon/elegant-elephant/blob/master/src/Arr/ArrOf.php) that has private constructor but let you create an instance of [`Arr`](#arr) object in a several ways via static methods:

```
use Maxonfjvipon\ElegantElephant\Arr\ArrOf;
use Maxonfjvipon\ElegantElephant\Any\AnyOf;

// From items
ArrOf::items($item1, $item2, $item3)->asArray(); // [$item1, $item2, $item3]

// From array
ArrOf::array([1, 2, 3])->asArray(); // [1, 2, 3]

// From Maxonfjvipon\ElegantElephant\Any
ArrOf::any(AnyOf::arr(["Hello", "Jeff"]))->asArray(); // ["Hello", "Jeff"]

// From callback
ArrOf::func(fn () => ["Hello", "World"])->asArray(); // ["Hello", "World"]
```

If you write code and see that you can't create an object via `new` keyword - definitely it's the class with private constructor but public static methods that replace constructors, so try them.

Union arguments
---------------

[](#union-arguments)

Almost every classes allows you to pass union-typed arguments to the constructor.

For example, class [`TxtJoined`](https://github.com/maxonfjvipon/elegant-elephant/blob/master/src/Txt/TxtJoined.php) behaves like a joined string and accepts an `array` or an instance of `Arr` that both may contains strings, instances of `Txt` or both of them.

```
use Maxonfjvipon\ElegantElephant\Txt\TxtJoined;
use Maxonfjvipon\ElegantElephant\Txt\TxtOf;
use Maxonfjvipon\ElegantElephant\Arr\ArrOf;
use Maxonfjvipon\ElegantElephant\Any\AnyOf;

$joined1 = new TxtJoined([
  TxtOf::str("Hello"),
  " ",
  TxtOf::any(AnyOf::str("Jeff"))
]);

$joined2 = new TxtJoined(
  ArrOf::func(fn () => [
    new TxtJoined(["Hello", " "]),
    "Jeff"
  ])
);

$joined1->asString() === $joined2->asString(); // "Hello Jeff" === "Hello Jeff" => TRUE
```

So when you write code you may not care about conversion an argument to the desired type, for example conversion `string` to `Maxonfjvipon\ElegantElephant\Txt`. Just pass what you have to the object, it knows how to deal with it.

Any
---

[](#any)

Something that has value of any (mixed) type. `Any` interface has only one public method `value` that must return something of `mixed` type.

### Any objects

[](#any-objects)

ClassDescriptionAnyForkBehaves like first value if condition is TRUE, like second otherwiseAnyCondAlias of AnyForkAnyOfAllow you to create Any from array, Arr, string, Txt, number, Num or functionAnyStickyAny with caching mechanismAnyWrapEnvelope for Any classesAtKeyGet element from array or Arr by keyAtValueGet key from array or Arr by valueEnsureAnyHelper trait to cast mixedFirstOfGet the first element from array, Arr, string or TxtLastOfGet the last element from array, Arr, string or Txt### Tests

[](#tests)

See [Any unit tests](https://github.com/maxonfjvipon/elegant-elephant/tree/master/tests/Any) for better undestanding.

Arr
---

[](#arr)

Elegant arrays. `Arr` interface has only one public method `asArray()` that must return an `array`.

### Spreading

[](#spreading)

There is one more interface that you can use in your own classes - [`IterableArr`](https://github.com/maxonfjvipon/elegant-elephant/blob/master/src/Arr/IterableArr.php) that extends `Arr` and [`\IteratorAggregate`](https://www.php.net/manual/en/class.iteratoraggregate.php).

`\IteratorAggregate` allows you to apply spread operator `...` to your class. If you want to use spread operator with your class without actual calling `asArray()` method you should make your class implement `IterableArr` and use [`HasArrIterator`](https://github.com/maxonfjvipon/elegant-elephant/blob/master/src/Arr/HasArrIterator.php) trait. The trait is the default implementation of spreading `Arr` classes. And now when you use `...` with your class trait calls `asArray()` behind the scene.

And here is the example for better understanding:

```
use Maxonfjvipon\ElegantElephant\Arr;
use Maxonfjvipon\ElegantElephant\Arr\IterableArr;
use Maxonfjvipon\ElegantElephant\Arr\HasArrIterator;

class MyArr implements Arr { /** code */ }

class MyIterableArr implements IterableArr {
  use HasArrIterator;
  /** code */
}

$arr = [...(new MyArr())->asArray()];         // good
$arr = [...new MyIterableArr()];              // good, no actual calling asArray()
$arr = [...(new MyIterableArr())->asArray()]; // good, but verbose
$arr = [...new MyArr()];                      // wrong
```

All `Arr` classes in the library can be spead.

### Arr classes

[](#arr-classes)

ClassDescriptionPHPArrCastCast all elements in given array-ArrCombinedCombine two arrays into signe onearray\_combineArrForkBehaves like first array if condition is TRUE, like second otherwise-ArrCondAlias Of ArrFork-ArrEmptyEmpty array\[\]ArrExplodedElements of exploded by separator stringexplodeArrFilteredFilter elements of given array by given callbackarray\_filterArrFlattenArray flatten with given deep-ArrIfBehaves like given array if condition is TRUE, like empty otherwise-ArrKeysGet keys of given arrayarray\_keysArrMappedMap elements of given array by given callbackarray\_mapArrMergedMerge given arraysarray\_mergeArrObjectAlias of ArrSingle\[key =&gt; value\]ArrOfAllows to create Arr from array, Any or function-ArrPluckedPluck an array of values from an array-ArrRangeArray of elements in given rangerangeArrReversedReverse given arrayarray\_reverseArrSingleArray with single key =&gt; value element\[key =&gt; value\]ArrSortedSort array by given key or callbacksort, usortArrSplitAlias or ArrExplodedexplode, splitArrStickyArray with cache mechanism-ArrUniqueArray with unique elementsarray\_uniqueArrValuesArray of just values of given array (ignore keys)array\_valuesArrWithGiven array + new given element\[...\] + \[$item\]ArrWithoutGiven array - element by keyunset($arr\[$key\])ArrWrapEnvelope for Arr classes-CountArrDefault implementation of counting Arr if your Arr impelement Countablecount($array)EnsureArrHelper trait for casting array or Arr to array-HasArrIteratorDefault implementation of spreading IterableArr...$array### Tests

[](#tests-1)

See [Arr unit test](https://github.com/maxonfjvipon/ElegantElephant/tree/master/tests/Arr) for better undestanding.

Logic
-----

[](#logic)

Elegant boolean. `Logic` interface has only one method `asBool()` that must return `bool`.

### Logic classes

[](#logic-classes)

ClassDescriptionPHPConjunctionConjunction, logical ANDand, &amp;&amp;ConjAlias of Conjunctionand, &amp;&amp;ContainsInCheck if something contains in string, Txt, array or Arr-DisjunctionDisjunction, logical ORor, ||DisjAlias of Disjunctionor, ||EnsureLogicHelper trait to cast bool or Logic to bool-GreaterOrEqualCheck if first given number is greater than or equal to the second one&gt;=GreaterThanCheck if first given number is greater than the second one&gt;InArrayCheck if something contains in array or Arrin\_arrayInTextCheck if string or Txt contains in other string or ArrstrcontainsIsEmptyCheck if string, Txt, array or Arr is emptyempty()IsEqualCheck if one mixed element is equal to another===IsNotEmptyChekc if string, Txt, array or Arr is not empty!empty()IsNotEqualCheck if one mixed element is not equal to another!==IsNullCheck if given element is null=== nullIsNotNullCheck if given element is not null!== nullKeyExistsCheck if key exists in array or Arrarray\_key\_existsLessOrEqualCheck if first given number is less than or equal to the second one&lt;=LessThanCheck if first given number is less than the second one&lt;LogicCondAlias of LogicFork-LogicForkBehaves like first given logic if given condition is TRUE, like second otherwise-LogicOfAllows you to create Logic from bool or function-LogicStickyLogic with caching mechanism-LogicWrapEnvelope for Logic classes-NotLogical Not!PregMatchCheck if string or Txt is match to regular expressionpreg\_match### Tests

[](#tests-2)

See [Logic unit tests](https://github.com/maxonfjvipon/elegant-elephant/tree/master/tests/Logic) for better undestanding.

Num
---

[](#num)

Elegant numbers. `Num` interface has only one method `asNumber()` that must return `float` or `int`.

### Num classes

[](#num-classes)

ClassDescriptionPHPArraySumAlias of SumOfarray\_sumDividedDivisiona / bEnsureNumHelper trait for casting number or Num to float or int-LengthOfLength of string, Txt, array or Arrstrlen, countMaxOfMax numbermaxMinOfMin numberminMultipliedMultiplicationa \* bNumForkBehaves like first number if condition is TRUE, like second otherwise-NumCondAlias of NumFork-NumIfBehaves like given number if condition is TRUE, like 0 otherwise-NumOfAllows to create Num from float, int, Any or function-NumStickyNum with caching mechanism-NumWrapEnvelope for Num classes-RoundedRounded numberroundSumOfSum of given numbers or Numsarray\_sum### Tests

[](#tests-3)

See [Num unit tests](https://github.com/maxonfjvipon/elegant-elephant/tree/master/tests/Num) for better undestanding.

Txt
---

[](#txt)

Elegant strings. `Txt` interface has only one method `asString()` that must return `string`.

### Txt-&gt;\_\_toString()

[](#txt-__tostring)

There is one more interface that you can use in your own classes - [`StringableTxt`](https://github.com/maxonfjvipon/ElegantElephant/blob/master/src/Txt/StringableTxt.php) that extends `Txt` and [`\Stringable`](https://www.php.net/manual/en/class.stringable.php).

`\Stringable` allows you to cast your class to string via calling `__toString()` method. So instead of `Txt` you can use `StringableTxt` interface and [`TxtToString`](https://github.com/maxonfjvipon/ElegantElephant/blob/master/src/Txt/TxtToString.php) helper trait that calls `asString()` behind the scene.

Here's an example for better undestanding:

```
use Maxonfjvipon\ElegantElephant\Txt\StringableTxt;
use Maxonfjvipon\ElegantElephant\Txt\TxtToString;
use Maxonfjvipon\ElegantElephant\Txt;

class MyTxt implements Txt { /** code */ }

class MyStringableTxt implements StringableTxt {
  use TxtToString;
  /** code */
}

$txt = new MyTxt();
$stringableTxt = new MyStringableTxt();

echo $txt->asString();           // good
echo $stringableTxt;             // good, no actual calling asString()
echo $stringableTxt->asString(); // good, but verbose
echo $txt;                       // wrong
```

All `Txt` classes in the library implements `StringableTxt`.

### Txt classes

[](#txt-classes)

ClassDescriptionPHPEnsureTxtHelper trait for casting string or Txt to string-TxtBlankEmpty text.""TxtForkBehaves like first text if condition is TRUE, like second otherwise-TxtCondAlias of TxtFork-TxtIfBehaves like given text if condition is TRUE, likey empty otherwise-TxtImplodedImploded text by separatorimplodeTxtJoinedJoined textjoin("", \[...\])TxtJsonEncodedObject encoded to JSON asjson\_encodedTxtLoweredText in a lower casestrtolowerTxtLtrimmedText trimmed from leftltrimTxtOfAllows to create Txt from string, number, float, int, Any of function.-TxtPregReplacedText with replacements by regexpreg\_replaceTxtReplacedText with replacementsstr\_replaceTxtRtimmedText trimmed from rightrtrimTxtStickyText with caching mechanism-TxtSubstrSub-textsubstrTxtToStringDefault implementation converting Txt to string via \_\_toString()-TxtTrimmedText trimmedtrimTxtUpperText in a upper casestrtoupperTxtWrapEnvelope for Txt classes-### Tests

[](#tests-4)

See [Txt unit test](https://github.com/maxonfjvipon/ElegantElephant/tree/master/tests/Txt) for better undestanding.

Contribution
------------

[](#contribution)

Fork repository, make changes, send a pull request. To avoid frustration, before sending your pull request please run:

```
$ ./pre-push.sh
```

And make sure you have no errors.

###  Health Score

30

—

LowBetter than 64% of packages

Maintenance7

Infrequent updates — may be unmaintained

Popularity20

Limited adoption so far

Community14

Small or concentrated contributor base

Maturity66

Established project with proven stability

 Bus Factor1

Top contributor holds 94.4% 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 ~15 days

Recently: every ~33 days

Total

31

Last Release

1088d ago

Major Versions

0.3.1 → 1.0.02022-10-13

1.0.0 → 2.0.02022-10-15

2.1.0 → 3.0.02022-10-19

3.1.0 → 4.0.02022-10-26

PHP version history (2 changes)0.0.3PHP &gt;=8.0

1.0.0PHP &gt;=7.4

### Community

Maintainers

![](https://www.gravatar.com/avatar/553059dc46ea6e67ba5b9e1de4b9056af3c1892ab4f4576cab0687143e00b6f9?d=identicon)[maxonfjvipon](/maintainers/maxonfjvipon)

---

Top Contributors

[![maxonfjvipon](https://avatars.githubusercontent.com/u/37025995?v=4)](https://github.com/maxonfjvipon "maxonfjvipon (101 commits)")[![rultor](https://avatars.githubusercontent.com/u/8086956?v=4)](https://github.com/rultor "rultor (4 commits)")[![R0bari](https://avatars.githubusercontent.com/u/56538398?v=4)](https://github.com/R0bari "R0bari (2 commits)")

---

Tags

elegantobjectsphp

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/maxonfjvipon-elegant-elephant/health.svg)

```
[![Health](https://phpackages.com/badges/maxonfjvipon-elegant-elephant/health.svg)](https://phpackages.com/packages/maxonfjvipon-elegant-elephant)
```

PHPackages © 2026

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