PHPackages                             athari/yalinqo - 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. athari/yalinqo

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

athari/yalinqo
==============

YaLinqo, a LINQ-to-objects library for PHP

v3.0.0(7mo ago)4561.2M—7.8%40[13 issues](https://github.com/Athari/YaLinqo/issues)5BSD-2-ClausePHPPHP &gt;=7.0CI passing

Since Feb 25Pushed 7mo ago20 watchersCompare

[ Source](https://github.com/Athari/YaLinqo)[ Packagist](https://packagist.org/packages/athari/yalinqo)[ Docs](http://athari.github.io/YaLinqo)[ RSS](/packages/athari-yalinqo/feed)WikiDiscussions master Synced 1mo ago

READMEChangelog (10)Dependencies (3)Versions (23)Used By (5)

*YaLinqo: Yet Another LINQ to Objects for PHP*
==============================================

[](#yalinqo-yet-another-linq-to-objects-for-php)

[![Coveralls Coverage](https://camo.githubusercontent.com/72148b38fa1b781de68d64a4787ca94832a7e9184c62da61eb4c0b3e24508230/68747470733a2f2f696d672e736869656c64732e696f2f636f766572616c6c732f4174686172692f59614c696e716f2f6d61737465722e737667)](https://coveralls.io/r/Athari/YaLinqo)[![Scrutinizer Code Quality](https://camo.githubusercontent.com/0610760c19827899f59bd46d2ffce5c75574afc24e0f3013d9ef3c8788e2c406/68747470733a2f2f696d672e736869656c64732e696f2f7363727574696e697a65722f672f4174686172692f59614c696e716f2e737667)](https://scrutinizer-ci.com/g/Athari/YaLinqo)[![Packagist Downloads](https://camo.githubusercontent.com/57c9ce38de9c64f0e1f0ccb7df82c660b5966be4e32871a00c0e610a9617ea89/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f6174686172692f79616c696e716f2e737667)](https://packagist.org/packages/athari/yalinqo)[![Packagist Version](https://camo.githubusercontent.com/04c8954a8a6307b494dbfab89c945663e930e8331039737e65fc87c431c783f7/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6174686172692f79616c696e716f2e737667)](https://packagist.org/packages/athari/yalinqo)[![GitHub License](https://camo.githubusercontent.com/516497dc683bbb49e34d802b0619bd5b1d2723c49b6aca64863c9cf45ed1d593/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f4174686172692f59614c696e716f2e737667)](license.md)

- [**Reference documentation**](http://athari.github.io/YaLinqo)
- [**GitHub repository**](https://github.com/Athari/YaLinqo)

Features
========

[](#features)

- The most complete port of .NET LINQ to PHP, with [many additional methods](#implemented-methods).
- Lazy evaluation, error messages and other behavior of original LINQ.
- [Detailed PHPDoc and online reference](http://athari.github.io/YaLinqo) based on PHPDoc for all methods. Articles are adapted from original LINQ documentation from MSDN.
- 100% unit test coverage.
- Best performance among full-featured LINQ ports (YaLinqo, Ginq, Pinq), at least 2x faster than the closest competitor, see [performance tests](https://github.com/Athari/YaLinqoPerf).
- Callback functions can be specified as arrow functions (`fn($v) => $v`), first-class callables (`strnatcmp(...)`) or any other [PHP callables](https://www.php.net/manual/language.types.callable.php).
- Keys are as important as values. Most callback functions receive both values and keys; transformations can be applied to both values and keys; keys are never lost during transformations, if possible.
- SPL interfaces `Iterator`, `IteratorAggregate` etc. are used throughout the code and can be used interchangeably with `Enumerable`.
- Redundant collection classes are avoided, native PHP arrays are used everywhere.
- Composer support ([package](https://packagist.org/packages/athari/yalinqo) on Packagist).
- No external dependencies.

Implemented methods
===================

[](#implemented-methods)

Some methods had to be renamed, because their names are reserved keywords. Original methods names are given in parentheses.

- **Generation**: cycle, emptyEnum (empty), from, generate, toInfinity, toNegativeInfinity, matches, returnEnum (return), range, rangeDown, rangeTo, repeat, split;
- **Projection and filtering**: cast, ofType, select, selectMany, where;
- **Ordering**: orderBy, orderByDescending, orderByDir, thenBy, thenByDescending, thenByDir;
- **Joining and grouping**: groupJoin, join, groupBy;
- **Aggregation**: aggregate, aggregateOrDefault, average, count, max, maxBy, min, minBy, sum;
- **Set**: all, any, append, concat, contains, distinct, except, intersect, prepend, union;
- **Pagination**: elementAt, elementAtOrDefault, first, firstOrDefault, firstOrFallback, last, lastOrDefault, lastOrFallback, single, singleOrDefault, singleOrFallback, indexOf, lastIndexOf, findIndex, findLastIndex, skip, skipWhile, take, takeWhile;
- **Conversion**: toArray, toArrayDeep, toList, toListDeep, toDictionary, toJSON, toLookup, toKeys, toValues, toObject, toString;
- **Actions**: call (do), each (forEach), write, writeLine.

In total, more than 80 methods.

Usage
=====

[](#usage)

Add to `composer.json`:

```
{
    "require": {
        "athari/yalinqo": "^3.0"
    }
}
```

Add to your PHP script:

```
require_once 'vendor/autoloader.php';
use \YaLinqo\Enumerable;

// 'from' can be called as a static method or via a global function shortcut
Enumerable::from([1, 2, 3]);
from([1, 2, 3]);
```

Example
=======

[](#example)

*Process sample data:*

```
// Data
$products = [
    [ 'name' => 'Keyboard',    'catId' => 'hw', 'quantity' =>  10, 'id' => 1 ],
    [ 'name' => 'Mouse',       'catId' => 'hw', 'quantity' =>  20, 'id' => 2 ],
    [ 'name' => 'Monitor',     'catId' => 'hw', 'quantity' =>   0, 'id' => 3 ],
    [ 'name' => 'Joystick',    'catId' => 'hw', 'quantity' =>  15, 'id' => 4 ],
    [ 'name' => 'CPU',         'catId' => 'hw', 'quantity' =>  15, 'id' => 5 ],
    [ 'name' => 'Motherboard', 'catId' => 'hw', 'quantity' =>  11, 'id' => 6 ],
    [ 'name' => 'Windows',     'catId' => 'os', 'quantity' => 666, 'id' => 7 ],
    [ 'name' => 'Linux',       'catId' => 'os', 'quantity' => 666, 'id' => 8 ],
    [ 'name' => 'Mac',         'catId' => 'os', 'quantity' => 666, 'id' => 9 ],
];
$categories = [
    [ 'name' => 'Hardware',          'id' => 'hw' ],
    [ 'name' => 'Operating systems', 'id' => 'os' ],
];

// Put products with non-zero quantity into matching categories;
// sort categories by name;
// sort products within categories by quantity descending, then by name.
$result = from($categories)
    ->orderBy(fn($cat) => $cat['name'])
    ->groupJoin(
        from($products)
            ->where(fn($prod) => $prod['quantity'] > 0)
            ->orderByDescending(fn($prod) => $prod['quantity'])
            ->thenBy(fn($prod) => $prod['name'], 'strnatcasecmp'),
        fn($cat) => $cat['id'],
        fn($prod) => $prod['catId'],
        fn($cat, $prods) => [
            'name' => $cat['name'],
            'products' => $prods
        ]
    );

// More verbose syntax with parameter names (PHP 8.0+)
// and first-class callables (PHP 8.1+):
$result = Enumerable::from($categories)
    ->orderBy(keySelector: fn($cat) => $cat['name'])
    ->groupJoin(
        inner: from($products)
            ->where(predicate: fn($prod) => $prod['quantity'] > 0)
            ->orderByDescending(keySelector: fn($prod) => $prod['quantity'])
            ->thenBy(keySelector: fn($prod) => $prod['name'], comparer: strnatcasecmp(...)),
        outerKeySelector: fn($cat) => $cat['id'],
        innerKeySelector: fn($prod) => $prod['catId'],
        resultSelectorValue: fn($cat, $prods) => [
            'name' => $cat['name'],
            'products' => $prods
        ]
    );

print_r($result->toArrayDeep());
```

*Output (compacted):*

```
Array (
    [hw] => Array (
        [name] => Hardware
        [products] => Array (
            [0] => Array ( [name] => Mouse       [catId] => hw [quantity] =>  20 [id] => 2 )
            [1] => Array ( [name] => CPU         [catId] => hw [quantity] =>  15 [id] => 5 )
            [2] => Array ( [name] => Joystick    [catId] => hw [quantity] =>  15 [id] => 4 )
            [3] => Array ( [name] => Motherboard [catId] => hw [quantity] =>  11 [id] => 6 )
            [4] => Array ( [name] => Keyboard    [catId] => hw [quantity] =>  10 [id] => 1 )
        )
    )
    [os] => Array (
        [name] => Operating systems
        [products] => Array (
            [0] => Array ( [name] => Linux       [catId] => os [quantity] => 666 [id] => 8 )
            [1] => Array ( [name] => Mac         [catId] => os [quantity] => 666 [id] => 9 )
            [2] => Array ( [name] => Windows     [catId] => os [quantity] => 666 [id] => 7 )
        )
    )
)
```

Versions
========

[](#versions)

VersionStatusPHPNotes### **1.x** (2012)

[](#1x-2012)

1.0−1.1 legacy 5.3−7.4 - Manually implemented iterators

### **2.x** (2014)

[](#2x-2014)

2.0−2.4 legacy 5.5−7.4 - Rewrite using PHP 5.5 generators
- Causes deprecation warnings in PHP 7.2+ due to use of `create_function`

2.5 maintenance 5.5+ - Switched from `create_function` to `eval` for string lambdas
- May cause security analysis warnings due to use of `eval`

### **3.x** (2018)

[](#3x-2018)

3.0 maintenance 7.0+ - Abandoned rewrite with performance improvements
- Released 7 years later with most of the performance-related changes dropped
- May cause security analysis warnings due to use of `eval`

### **4.x** (2025)

[](#4x-2025)

4.0 planned 8.0+(?) - Strong types everywhere, string lambdas nuked from existence

Breaking changes
================

[](#breaking-changes)

Version 1.x → 2.x
-----------------

[](#version-1x--2x)

- Minimum supported PHP version is 5.5.
- Collections `Dictionary` and `Lookup` were replaced with standard arrays.

Version 2.x → 3.x
-----------------

[](#version-2x--3x)

- Minimum supported PHP version is 7.0.
- Type hints were added to parameters of some functions (`ofType`, `range`, `rangeDown`, `rangeTo`, `toInfinity`, `toNegativeInfinity`, `matches`, `split`). There may be edge cases if you rely on passing incorrect types of arguments.

Legacy information
==================

[](#legacy-information)

Legacy features
---------------

[](#legacy-features)

- (**Versions 1.0−2.5**) Callback functions can be specified as “string lambdas” using various syntaxes:
    - `'"$k = $v"'` (implicit `$v` and `$k` arguments, implicit return)
    - `'$v ==> $v + 1'` (like a modern arrow function, but without `fn` and with a longer arrow)
    - `'($v, $k) ==> $v + $k'` (explicit arguments, implicit return)
    - `'($v, $k) ==> { return $v + $k; }'` (explicit arguments, explicit return within a block)

Note

Before arrow functions were added in PHP 7.4, the choice was between the ridiculously verbose anonymous function syntax (`function ($value) { return $value['key']; }`) and rolling your own lambda syntax (like `$v ==> $v["key"]`). This is why “string lambdas” were a necessity at the time.

Caution

**When using legacy versions of YaLinqo and PHP:**

1. You **MUST NOT**[1](#user-content-fn-1-b24c29121f46dd3eb4561e1c804f1ed0) use user-provided strings to construct string lambdas. This directly opens you to passing to user-provided strings to `eval`, which is literally the worst thing you can do security-wise.
2. You **SHOULD NOT**[1](#user-content-fn-1-b24c29121f46dd3eb4561e1c804f1ed0) dynamically construct string lambdas in general, even if it seems convenient. Passing incorrect code to `eval` throws a `ParseError`. An exception to this rule may be constructing a *trivial* lambda from an array of *predefined* values.
3. You **SHOULD**[1](#user-content-fn-1-b24c29121f46dd3eb4561e1c804f1ed0) use full closure syntax instead of string lambdas when you need access to variables in scope.

When all your string lambdas are *single-quoted string constants*, there’s no security risk in using them. If you’re still paranoid about `eval`, just never use string lambdas.

Links
=====

[](#links)

### Documentation

[](#documentation)

- [Reference documentation](http://athari.github.io/YaLinqo) — generated from source. Includes MSDN-tier explanation of what every method does.
- [How to use Linq in PHP](https://web.archive.org/web/2019/http://tutewall.com/how-to-use-linq-in-php-part-01/) (archived) by *Mr. X* — a series of posts covering basic usage of YaLinqo.

Tip

If you’re new to LINQ, you should read the series of articles by Mr. X, as they’re very beginner-friendly.

### Articles

[](#articles)

- **CodeProject** *(English):*

    - [LINQ for PHP comparison: YaLinqo, Ginq, Pinq](http://www.codeproject.com/Articles/997238/LINQ-for-PHP-comparison-YaLinqo-Ginq-Pinq) — performance comparison of full-featured LINQ ports, with some additional information.
- **Habrahabr** *(Russian):*

    - [Comparison of old LINQ libraries](http://habrahabr.ru/post/147612/) — comparison of *LINQ for PHP*, *Phinq*, *PHPLinq* and *Plinq*, also *Underscore.php*.
    - [YaLinqo 1.0 with updated comparison](http://habrahabr.ru/post/147848/) — explanation of architecture and design decisions.
    - [YaLinqo 2.0](http://habrahabr.ru/post/229763/) — switch to PHP 5.5 with generators support and related changes.
    - [LINQ for PHP: speed matters](http://habrahabr.ru/post/259155/) — performance comparison of full-featured LINQ ports (YaLinqo, Ginq, Pinq).

### Alternatives

[](#alternatives)

Realistically, there’re none. This is the only PHP library in existence which implements lazy evaluation, deals with keys in iterators properly, has documentation and actually works (until yet another breaking change in PHP), with everything else failing in 2+ ways. However, some alternatives are worth mentioning.

- [**Laravel LazyCollection**](https://laravel.com/docs/collections#lazy-collections) (Laravel 6.0+) — The closest you can get to LINQ-to-objects in PHP without YaLinqo. Includes SQL-isms like `where('balance', '>', '100')`, Ruby-isms like `pluck('my.hair')`, random non-pure methods like `forget('name')` and other footguns, but largely functional. Note that lazy evaluation is opt-in: you need to call either `LazyCollection::make($iterable)` or `collect($array)->lazy()`.
- [**RxPHP**](https://github.com/ReactiveX/RxPHP) — reactive (push) counterpart of the active (pull) LINQ, port of Rx.NET. A faithful implementation of Rx in PHP by people who actually use it. Highly recommended if you need complex transformations over asynchronous operations.

### Related projects

[](#related-projects)

- [**linq.js**](https://github.com/mihaifm/linq) — LINQ for JavaScript. The one and only complete port of .NET LINQ to JavaScript. Supports TypeScript, ESM, CJS, browsers.
- [**Underscore.js**](https://underscorejs.org/) — library for functional programming in JavaScript. Similar to LINQ, but different method names and no lazy evaluation.
- [**YaLinqoPerf**](https://github.com/Athari/YaLinqoPerf) — collection of performance tests comparing raw PHP, array functions, YaLinqo, YaLinqo with string lambdas, Ginq, Ginq with property accessors, Pinq.

### PHP

[](#php)

If you want to contribute to the project without writing any code, consider annoying the developers of PHP on GitHub and their mailing list whenever they decline yet another useful feature.

If you’re successful and actually get them to implement PFA + Pipe v4 (?), then non-lazy LINQ ports will lose 80% of their users, as PHP array functions will become usable by themselves without turning the code into unreadable spaghetti.

And if devs of PHP implement [pipes for iterables](https://wiki.php.net/rfc/pipe-operator-v3#iterable_api), then YaLinqo itself will need a complete rewrite for 20% of cases and become obsolete for 80% of them. I wouldn’t hold my breath though, as that thing has been in discussion for like 10 years already.

- Graveyard of PHP RFCs:

    - [Partial function application](https://wiki.php.net/rfc/partial_function_application) — imagine pipe operator being actually useful… nah, not happening.
    - [Short Closures 2.0](https://wiki.php.net/rfc/auto-capture-closure) — support for multi-statement arrow functions declined, 2 votes short.
    - [Add `array_group` function](https://wiki.php.net/rfc/array_column_results_grouping) — grouping won’t be optimized by using a built-in function. Not a huge loss, but still a pity.
- Too little too late:

    - [Pipe operator v3](https://wiki.php.net/rfc/pipe-operator-v3) ([v2](https://wiki.php.net/rfc/pipe-operator-v2), [v1](https://wiki.php.net/rfc/pipe-operator)) — took 3 RFCs and 10 years, but we’ve finally arrived at… the least useful and the most verbose pipe syntax on the planet… yay?
    - [Arrow functions v2](https://wiki.php.net/rfc/arrow_functions_v2) ([v1](https://wiki.php.net/rfc/arrow_functions), [v0](https://wiki.php.net/rfc/short_closures)) — took 3 RFCs and just 5 years. A notable exception of actually being in a good state. However, *zero* plans from the “future scope” were implemented in the following years.

License
=======

[](#license)

[**Simplified BSD License**](license.md)
Copyright © 2012–2025, Alexander Prokhorov

History
=======

[](#history)

[    ![Star history chart](https://camo.githubusercontent.com/13120da8cf5b8674fa93e77d5a6e228018d3410426ff80a50030b6427992ecac/68747470733a2f2f6170692e737461722d686973746f72792e636f6d2f7376673f7265706f733d4174686172692f59614c696e716f26747970653d64617465266c6567656e643d626f74746f6d2d7269676874 "Star history chart") ](https://www.star-history.com/#Athari/YaLinqo&type=date&legend=bottom-right)[![Contributors](https://camo.githubusercontent.com/035aa322da7d33bf2e97dab5de9816e42e2ea819a13c0903c76f0b6bacf73ce5/68747470733a2f2f636f6e747269622e726f636b732f696d6167653f7265706f3d4174686172692f59614c696e716f26636f6c756d6e733d3132266d61783d323426616e6f6e3d30 "Contributors")](https://github.com/Athari/YaLinqo/graphs/contributors)

Footnotes
---------

1. The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in [RFC 2119](https://datatracker.ietf.org/doc/html/rfc2119). [↩](#user-content-fnref-1-b24c29121f46dd3eb4561e1c804f1ed0) [↩2](#user-content-fnref-1-2-b24c29121f46dd3eb4561e1c804f1ed0) [↩3](#user-content-fnref-1-3-b24c29121f46dd3eb4561e1c804f1ed0)

###  Health Score

58

—

FairBetter than 98% of packages

Maintenance64

Regular maintenance activity

Popularity59

Moderate usage in the ecosystem

Community29

Small or concentrated contributor base

Maturity68

Established project with proven stability

 Bus Factor1

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

###  Release Activity

Cadence

Every ~219 days

Recently: every ~1 days

Total

22

Last Release

219d ago

Major Versions

v1.0.1.1 → v2.0.0.02014-07-12

v1.1.0.0 → v2.3.0.02015-06-07

v1.1.1 → v2.4.12018-02-25

v1.0.x-dev → v2.4.22019-01-10

v2.5.2 → v3.0.x-dev2025-10-11

PHP version history (3 changes)v1.0.0.0PHP &gt;=5.3

v2.0.0.0PHP &gt;=5.5

v3.0.x-devPHP &gt;=7.0

### Community

Maintainers

![](https://www.gravatar.com/avatar/b2817a3b659afdca818a1ff6df89472ca242d991b2914afae330ea849961ca5e?d=identicon)[Athari](/maintainers/Athari)

---

Top Contributors

[![Athari](https://avatars.githubusercontent.com/u/1715107?v=4)](https://github.com/Athari "Athari (176 commits)")[![sanmai](https://avatars.githubusercontent.com/u/139488?v=4)](https://github.com/sanmai "sanmai (26 commits)")[![zquintana](https://avatars.githubusercontent.com/u/130444?v=4)](https://github.com/zquintana "zquintana (3 commits)")[![jorrit](https://avatars.githubusercontent.com/u/521449?v=4)](https://github.com/jorrit "jorrit (2 commits)")[![umpirsky](https://avatars.githubusercontent.com/u/208957?v=4)](https://github.com/umpirsky "umpirsky (2 commits)")[![lucasjsoliveira](https://avatars.githubusercontent.com/u/9120016?v=4)](https://github.com/lucasjsoliveira "lucasjsoliveira (1 commits)")

---

Tags

collectionfunctionalfunctional-programminggeneratorgenerator-functioniteratoriterator-librarylibrarylinqlinq-to-objectsphpquerystatisticsunderscorearraygeneratorqueryiteratorcollectionyieldlinq

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/athari-yalinqo/health.svg)

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

###  Alternatives

[loophp/collection

A (memory) friendly, easy, lazy and modular collection class.

745663.8k13](/packages/loophp-collection)[nikic/iter

Iteration primitives using generators

1.1k5.9M38](/packages/nikic-iter)[aimeos/map

Easy and elegant handling of PHP arrays as array-like collection objects similar to jQuery and Laravel Collections

4.2k412.9k11](/packages/aimeos-map)[ginq/ginq

LINQ to Object inspired DSL for PHP

192257.5k3](/packages/ginq-ginq)[nahid/qarray

QArray is a PHP abstraction for querying array

108403.2k2](/packages/nahid-qarray)[chdemko/sorted-collections

Sorted Collections for PHP &gt;= 8.2

222.5M3](/packages/chdemko-sorted-collections)

PHPackages © 2026

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