mathsgod / php-linq
A provider-based LINQ-style query library for PHP
Requires
- php: >=8.2
- ext-dom: *
Requires (Dev)
- phpunit/phpunit: ^11.5
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-09-22 05:01:00 UTC
README
An experimental LINQ-style query library for PHP 8.2+. Unlike callback-only collection wrappers, a query is represented by an expression tree and executed by a query provider.
LINQ to Objects
Enumerable provides lazy, generator-based processing with ordinary PHP
callbacks:
use PhpLinq\Enumerable; $names = Enumerable::from($users) ->where(fn (User $user): bool => $user->active) ->orderBy(fn (User $user): int => $user->age) ->select(fn (User $user): string => $user->name) ->take(10) ->toArray();
Streaming operators include where, select, selectMany, skip, take,
skipWhile, takeWhile, distinct, append, prepend, concat, and
chunk. Operators which require a complete view of the sequence, such as
orderBy, reverse, and takeLast, buffer their input when enumerated.
The API also includes the .NET-style aggregate and set operators min, max,
minBy, maxBy, except, intersect, union, and shuffle. Relational
join combines matching outer and inner elements by key:
$summaries = Enumerable::from($users)->join( $orders, fn (array $user): int => $user['id'], fn (array $order): int => $order['userId'], fn (array $user, array $order): array => [ 'name' => $user['name'], 'total' => $order['total'], ], );
Call asEnumerable() on an IQueryable to execute the provider-backed part
first and continue with callback-based, in-memory operators.
Generic collections
Mutable collections modelled after System.Collections.Generic are available
under PhpLinq\\Collections\\Generic:
use PhpLinq\Collections\Generic\GenericList; use PhpLinq\Collections\Generic\Queue; use PhpLinq\Collections\Generic\Stack; $list = new GenericList([1, 2]); $list->add(3); $list->insert(0, 0); $list->insertRange(1, [10, 20]); $slice = $list->getRange(1, 2); $list->removeRange(1, 2); $stack = new Stack(); $stack->push('job'); $job = $stack->pop(); $queue = new Queue(); $queue->enqueue('job'); $job = $queue->dequeue();
PHP reserves the keyword list, so the .NET List<T> equivalent is named
GenericList<T>. All three collections implement IteratorAggregate and can
be passed directly to Enumerable::from().
The generic collection contracts are organised as follows:
IReadOnlyCollection<T>
├── ICollection<T>
│ ├── IList<T> → GenericList<T>
│ └── ISet<T> → HashSet<T>
├── Stack<T>
└── Queue<T>
IReadOnlyCollection<KeyValuePair<TKey, TValue>>
└── IDictionary<TKey, TValue> → Dictionary<TKey, TValue>
HashSet<T> supports custom EqualityComparer<T> implementations and mutable
set operations including union, intersection, difference, symmetric
difference, subset/superset checks, overlap checks, and set equality.
LINQ to XML
The PhpLinq\\Xml namespace provides a DOMDocument-backed XML object model:
use PhpLinq\Xml\XDocument; use PhpLinq\Xml\XElement; $document = XDocument::load('users.xml'); $names = $document ->descendants('user') ->where(fn (XElement $user): bool => $user->attribute('active')?->value() === 'true' ) ->select(fn (XElement $user): ?string => $user->element('name')?->value() ) ->toArray();
XDocument supports parsing, file loading, root access, descendant queries,
and XML serialization. XElement provides direct-child and descendant element
queries plus attribute access. Expanded names such as {urn:people}user are
supported through XName. All XML sequence methods return lazy IEnumerable
instances.
use PhpLinq\Expr; use PhpLinq\InMemoryQueryProvider; use PhpLinq\Queryable; $provider = new InMemoryQueryProvider([ 'users' => [ ['name' => 'Ada', 'active' => true, 'age' => 36], ['name' => 'Bob', 'active' => false, 'age' => 22], ], ]); $names = Queryable::from($provider, 'users') ->where(Expr::eq(Expr::field('active'), true)) ->orderBy(Expr::field('name')) ->select(Expr::field('name')) ->toArray();
Select multiple fields with a projection. fields() keeps the field names,
while projection() allows aliases and computed expressions:
$summaries = $users->select(Expr::fields('id', 'name'))->toArray(); $summaries = $users->select(Expr::projection([ 'userId' => Expr::field('id'), 'displayName' => Expr::field('name'), 'isAdult' => Expr::gte(Expr::field('age'), 18), ]))->toArray();
select(Expr::field('name')) returns a scalar sequence. fields() and
projection() always return associative rows keyed by their aliases, even when
they contain only one member.
The expression tree is provider-independent. InMemoryQueryProvider executes
it against arrays or other iterables; a future SQL provider can translate the
same nodes into parameterized SQL.
Supported query operators: where, select, orderBy, orderByDescending,
skip, take, count, any, and first.
Generic dictionary
Collections\\Generic\\Dictionary<TKey, TValue> is a hash-table collection
with object-key support and pluggable equality semantics:
use PhpLinq\Collections\Generic\Dictionary; /** @var Dictionary<string, int> $scores */ $scores = new Dictionary(); $scores->add('Ada', 10); // throws when the key already exists $scores['Ada'] = 15; // indexer-style insert or replace $scores->tryGetValue('Ada', $score);
It provides add, set, get, tryAdd, tryGetValue, containsKey,
containsValue, remove, clear, keys, and values. Iteration returns
KeyValuePair<TKey, TValue> objects so keys are not restricted to PHP's
integer and string iterator-key types.
SQL providers
The same expression tree can be compiled and executed through PDO:
use PhpLinq\Queryable; use PhpLinq\Sql\MySqlDialect; use PhpLinq\SqlQueryProvider; $users = Queryable::from( new SqlQueryProvider($pdo, new MySqlDialect()), 'users', );
The included dialects are MySqlDialect, SqlServerDialect,
PostgreSqlDialect, and SqliteDialect. They generate their native identifier
quoting and pagination syntax, including SQL Server TOP / OFFSET ... FETCH
and MySQL, PostgreSQL, and SQLite LIMIT variants. Query values are always
emitted as bound parameters.
Install the development dependencies and run the PHPUnit test suite with:
composer install
composer test