PHPackages                             koshuang/expression - 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. [Search &amp; Filtering](/categories/search)
4. /
5. koshuang/expression

ActiveLibrary[Search &amp; Filtering](/categories/search)

koshuang/expression
===================

Implementation of the Specification pattern and logical expressions for PHP.

v2.0.0(3y ago)04MITPHPPHP &gt;=8.1

Since Sep 3Pushed 3y agoCompare

[ Source](https://github.com/koshuang/expression)[ Packagist](https://packagist.org/packages/koshuang/expression)[ RSS](/packages/koshuang-expression/feed)WikiDiscussions master Synced 1w ago

READMEChangelogDependencies (4)Versions (2)Used By (0)

Webmozart Expression
====================

[](#webmozart-expression)

[![Build Status](https://camo.githubusercontent.com/cf89875a0d1a36f4b19dc26e478b5078e8248ceadb13abb9fcabe8db6e4c8c7c/68747470733a2f2f646c2e636972636c6563692e636f6d2f7374617475732d62616467652f696d672f67682f6b6f736875616e672f65787072657373696f6e2f747265652f6d61737465722e7376673f7374796c653d737667)](https://dl.circleci.com/status-badge/redirect/gh/koshuang/expression/tree/master)[![Latest Stable Version](https://camo.githubusercontent.com/45d8cdb19ed3ae2a423d666a17d0bd4a55bf81edb3b8bff3ee00fc3a62a94157/68747470733a2f2f706f7365722e707567782e6f72672f6b6f736875616e672f65787072657373696f6e2f762f737461626c652e737667)](https://packagist.org/packages/koshuang/expression)[![Total Downloads](https://camo.githubusercontent.com/6bbddb79022573fc62c15ff7030c5869e5c0294c14a882f772b1c41b0a898fdc/68747470733a2f2f706f7365722e707567782e6f72672f6b6f736875616e672f65787072657373696f6e2f646f776e6c6f6164732e737667)](https://packagist.org/packages/koshuang/expression)

Latest release: [2.0.0](https://packagist.org/packages/koshuang/expression#2.0.0)

PHP &gt;= 8.1

This is a forked version from . It's upgraded for PHP 8.1.
---------------------------------------------------------------------------------------------------

[](#this-is-a-forked-version-from-httpsgithubcomwebmozartexpression-its-upgraded-for-php-81)

This library implements the [Specification Pattern](http://www.martinfowler.com/apsupp/spec.pdf) for PHP. You can use it to easily filter results of your domain services by evaluating logical expressions.

Conversely to [rulerz](https://github.com/K-Phoen/rulerz), this library focuses on providing a usable and efficient PHP API first. An expression language that converts string expressions into `Expression` instances can be built on top, but is not included in the current release.

Visitors can be implemented that convert `Expression` objects into Doctrine queries and similar objects.

Installation
------------

[](#installation)

Use [Composer](https://getcomposer.org) to install the package:

```
$ composer require koshuang/expression

```

Basic Usage
-----------

[](#basic-usage)

Use the [`Expression`](src/Expression.php) interface in finder methods of your service classes:

```
use Webmozart\Expression\Expression;

interface PersonRepository
{
    public function findPersons(Expression $expr);
}
```

When querying persons from the repository, you can create new expressions with the [`Expr`](src/Expr.php) factory class:

```
$expr = Expr::method('getFirstName', Expr::startsWith('Tho'))
    ->andMethod('getAge', Expr::greaterThan(35));

$persons = $repository->findPersons($expr);
```

The repository implementation can use the `evaluate()` method to match individual persons against the criteria:

```
class PersonRepositoryImpl implements PersonRepository
{
    private $persons = [];

    public function findPersons(Expression $expr)
    {
        return Expr::filter($this->persons, $expr);
    }
}
```

[Visitors](#expression-transformation) can be built to convert expressions into other types of specifications, such as Doctrine query builders.

Domain Expressions
------------------

[](#domain-expressions)

Extend existing expressions to build domain-specific expressions:

```
class IsPremium extends Method
{
    public function __construct()
    {
        parent::__construct('isPremium', [], Expr::same(true));
    }
}

class HasPreviousBookings extends Method
{
    public function __construct()
    {
        parent::__construct(
            'getBookings',
            [],
            Expr::count(Expr::greaterThan(0))
        );
    }
}

// Check if a customer is premium
if ((new IsPremium())->evaluate($customer)) {
    // ...
}

// Get premium customers with bookings
$customers = $repo->findCustomers(Expr::andX([
    new IsPremium(),
    new HasPreviousBookings(),
]));
```

The following sections describe the core expressions in detail.

Expressions
-----------

[](#expressions)

The [`Expr`](src/Expr.php) class is able to create the following expressions:

MethodDescription`null()`Check that a value is `null``notNull()`Check that a value is not `null``isEmpty()`Check that a value is empty (using `empty()`)`notEmpty()`Check that a value is not empty (using `empty()`)`isInstanceOf($className)`Check that a value is instance of a class (using `instanceof`)`equals($value)`Check that a value equals another value (using `==`)`notEquals($value)`Check that a value does not equal another value (using `!=`)`same($value)`Check that a value is identical to another value (using `===`)`notSame($value)`Check that a value does not equal another value (using `!==`)`greaterThan($value)`Check that a value is greater than another value`greaterThanEqual($value)`Check that a value is greater than or equal to another value`lessThan($value)`Check that a value is less than another value`lessThanEqual($value)`Check that a value is less than or equal to another value`startsWith($prefix)`Check that a value starts with a given string`endsWith($suffix)`Check that a value ends with a given string`contains($string)`Check that a value contains a given string`matches($regExp)`Check that a value matches a regular expression`in($values)`Check that a value occurs in a list of values`keyExists($key)`Check that a key exists in a value`keyNotExists($key)`Check that a key does not exist in a value`true()`Always `true` (tautology)`false()`Always `false` (contradiction)Selectors
---------

[](#selectors)

With composite values like arrays or objects, you often want to match only a part of that value (like an array key or the result of a getter) against an expression. You can select the evaluated parts with a *selector*.

When you evaluate arrays, use the `key()` selector to match the value of an array key:

```
$expr = Expr::key('age', Expr::greaterThan(10));

$expr->evaluate(['age' => 12]);
// => true
```

Each selector method accepts the expression as last argument that should be evaluated for the selected value.

When evaluating objects, use `property()` and `method()` to evaluate the values of properties and the results of method calls:

```
$expr = Expr::property('age', Expr::greaterThan(10));

$expr->evaluate(new Person(12));
// => true

$expr = Expr::method('getAge', Expr::greaterThan(10));

$expr->evaluate(new Person(12));
// => true
```

The `method()` selector also accepts arguments that will be passed to the method. Pass the arguments before the evaluated expression:

```
$expr = Expr::method('getParameter', 'age', Expr::greaterThan(10));

$expr->evaluate(new Person(12));
// => true
```

You can nest selectors to evaluate expressions for nested objects or arrays:

```
$expr = Expr::method('getBirthDate', Expr::method('format', 'Y', Expr::lessThan(2000)));

$expr->evaluate(new Person(12));
// => false
```

The following table lists all available selectors:

MethodDescription`key($key, $expr)`Evaluate an expression for a key of an array`method($name, $expr)`Evaluate an expression for the result of a method call`property($name, $expr)`Evaluate an expression for the value of a property`count($expr)`Evaluate an expression for the count of a collectionThe `count()` selector accepts arrays and `Countable` objects.

Quantors
--------

[](#quantors)

Quantors are applied to collections and test whether an expression matches for a certain number of elements. A famous one is the all-quantor:

```
$expr = Expr::all(Expr::method('getAge', Expr::greaterThan(10)));

$expr->evaluate([new Person(12), new Person(11)]);
// => true
```

Quantors accept both arrays and `Traversable` instances. The following table lists all available quantors:

MethodDescription`all($expr)`Check that an expression matches for all entries of a collection`atLeast($count, $expr)`Check that an expression matches for at least `$count` entries of a collection`atMost($count, $expr)`Check that an expression matches for at most `$count` entries of a collection`exactly($count, $expr)`Check that an expression matches for exactly `$count` entries of a collectionLogical Operators
-----------------

[](#logical-operators)

You can negate an expression with `not()`:

```
$expr = Expr::not(Expr::method('getFirstName', Expr::startsWith('Tho')));
```

You can connect multiple expressions with "and" using the `and*()` methods:

```
$expr = Expr::method('getFirstName', Expr::startsWith('Tho'))
    ->andMethod('getAge', Expr::greaterThan(35));
```

The same is possible for the "or" operator:

```
$expr = Expr::method('getFirstName', Expr::startsWith('Tho'))
    ->orMethod('getAge', Expr::greaterThan(35));
```

You can use and/or inside selectors:

```
$expr = Expr::method('getAge', Expr::greaterThan(35)->orLessThan(20));
```

If you want to mix and match "and" and "or" operators, use `andX()` and `orX()`to add embedded expressions:

```
$expr = Expr::method('getFirstName', Expr::startsWith('Tho'))
    ->andX(
        Expr::method('getAge', Expr::lessThan(14))
            ->orMethod('isReduced', Expr::same(true))
    );
```

Testing
-------

[](#testing)

To make sure that PHPUnit compares [`Expression`](src/Expression.php) objects correctly, you should register the [`ExpressionComparator`](src/PhpUnit/ExpressionComparator.php) with PHPUnit in your PHPUnit bootstrap file:

```
// tests/bootstrap.php
use SebastianBergmann\Comparator\Factory;
use Webmozart\Expression\PhpUnit\ExpressionComparator;

require_once __DIR__.'/../vendor/autoload.php';

Factory::getInstance()->register(new ExpressionComparator());
```

Make sure the file is registered correctly in `phpunit.xml.dist`:

```

```

The [`ExpressionComparator`](src/PhpUnit/ExpressionComparator.php) makes sure that PHPUnit compares different [`Expression`](src/Expression.php) instances by *logical equivalence* instead of by object equality. For example, the following [`Expression`](src/Expression.php) are logically equivalent, but not equal as objects:

```
// Logically equivalent
$c1 = Expr::notNull()->andSame(35);
$c2 = Expr::same(35)->andNotNull();

$c1 == $c2;
// => false

$c1->equivalentTo($c2);
// => true

// Also logically equivalent
$c1 = Expr::same(35);
$c2 = Expr::oneOf([35]);

$c1 == $c2;
// => false

$c1->equivalentTo($c2);
// => true
```

Expression Transformation
-------------------------

[](#expression-transformation)

In some cases, you will want to transform expressions to some other representation. A prime example is the transformation of an expression to a [Doctrine](http://www.doctrine-project.org/) query.

You can implement a custom [`ExpressionVisitor`](src/Traversal/ExpressionVisitor.php) to do the transformation. The visitor's methods `enterExpression()` and `leaveExpression()` are called for every node of the expression tree:

```
use Webmozart\Expression\Traversal\ExpressionVisitor;

class QueryBuilderVisitor implements ExpressionVisitor
{
    private $qb;

    public function __construct(QueryBuilder $qb)
    {
        $this->qb = $qb;
    }

    public function enterExpression(Expression $expr)
    {
        // configure the $qb...
    }

    public function leaveExpression(Expression $expr)
    {
        // configure the $qb...
    }
}
```

Use an [`ExpressionTraverser`](src/Traversal/ExpressionTraverser.php) to traverse an expression with your visitor:

```
public function expressionToQueryBuilder(Expression $expr)
{
    $qb = new QueryBuilder();

    $traverser = new ExpressionTraverser();
    $traverser->addVisitor(new QueryBuilderVisitor($qb));
    $traverser->traverse($expr);

    return $qb;
}
```

Authors
-------

[](#authors)

- [Bernhard Schussek](http://webmozarts.com) a.k.a. [@webmozart](https://twitter.com/webmozart)
- [The Community Contributors](https://github.com/webmozart/expression/graphs/contributors)

Contribute
----------

[](#contribute)

Contributions to the package are always welcome!

- Report any bugs or issues you find on the [issue tracker](https://github.com/webmozart/expression).
- You can grab the source code at the package's [Git repository](https://github.com/webmozart/expression).

Support
-------

[](#support)

If you are having problems, send a mail to  or shout out to [@webmozart](https://twitter.com/webmozart) on Twitter.

License
-------

[](#license)

All contents of this package are licensed under the [MIT license](LICENSE).

###  Health Score

24

—

LowBetter than 32% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity3

Limited adoption so far

Community12

Small or concentrated contributor base

Maturity55

Maturing project, gaining track record

 Bus Factor1

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

Unknown

Total

1

Last Release

1352d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/679bbb264c234b50ac1a75848e407d06036e3250d1d264e8bce25c975f5d30a9?d=identicon)[koshuang](/maintainers/koshuang)

---

Top Contributors

[![webmozart](https://avatars.githubusercontent.com/u/176399?v=4)](https://github.com/webmozart "webmozart (143 commits)")[![koshuang](https://avatars.githubusercontent.com/u/1978357?v=4)](https://github.com/koshuang "koshuang (12 commits)")[![harikt](https://avatars.githubusercontent.com/u/120454?v=4)](https://github.com/harikt "harikt (1 commits)")[![temp](https://avatars.githubusercontent.com/u/216128?v=4)](https://github.com/temp "temp (1 commits)")[![tgalopin](https://avatars.githubusercontent.com/u/1651494?v=4)](https://github.com/tgalopin "tgalopin (1 commits)")[![StyleCIBot](https://avatars.githubusercontent.com/u/11048387?v=4)](https://github.com/StyleCIBot "StyleCIBot (1 commits)")[![stof](https://avatars.githubusercontent.com/u/439401?v=4)](https://github.com/stof "stof (1 commits)")

---

Tags

specificationfilterexpressioncriterialogicformula

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

![Health badge](/badges/koshuang-expression/health.svg)

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

###  Alternatives

[webmozart/expression

Implementation of the Specification pattern and logical expressions for PHP.

214382.0k11](/packages/webmozart-expression)[clue/stream-filter

A simple and modern approach to stream filtering in PHP

1.7k261.7M7](/packages/clue-stream-filter)[laminas/laminas-filter

Programmatically filter and normalize data and files

9428.0M150](/packages/laminas-laminas-filter)[outl1ne/nova-input-filter

An input filter for Laravel Nova

24822.7k](/packages/outl1ne-nova-input-filter)[dialekt/dialekt

A boolean expression DSL.

173.7k](/packages/dialekt-dialekt)

PHPackages © 2026

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