2017 © Pedro Peláez
 

library expression

Implementation of the Specification pattern and logical expressions for PHP.

image

webmozart/expression

Implementation of the Specification pattern and logical expressions for PHP.

  • Monday, August 15, 2016
  • by webmozart
  • Repository
  • 17 Watchers
  • 196 Stars
  • 71,589 Installations
  • PHP
  • 8 Dependents
  • 0 Suggesters
  • 8 Forks
  • 1 Open issues
  • 7 Versions
  • 5 % Grown

The README.md

Webmozart Expression

Build Status Build status Latest Stable Version Total Downloads Dependency Status, (*1)

Latest release: 1.0.0, (*2)

PHP >= 5.3.9, (*3)

This library implements the Specification Pattern for PHP. You can use it to easily filter results of your domain services by evaluating logical expressions., (*4)

Conversely to 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., (*5)

Visitors can be implemented that convert Expression objects into Doctrine queries and similar objects., (*6)

Installation

Use Composer to install the package:, (*7)

$ composer require webmozart/expression

Basic Usage

Use the [Expression] interface in finder methods of your service classes:, (*8)

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] factory class:, (*9)

$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:, (*10)

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

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

Visitors can be built to convert expressions into other types of specifications, such as Doctrine query builders., (*11)

Domain Expressions

Extend existing expressions to build domain-specific expressions:, (*12)

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., (*13)

Expressions

The [Expr] class is able to create the following expressions:, (*14)

Method Description
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

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., (*15)

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

$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., (*17)

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

$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:, (*19)

$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:, (*20)

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

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

The following table lists all available selectors:, (*21)

Method Description
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 collection

The count() selector accepts arrays and Countable objects., (*22)

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:, (*23)

$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:, (*24)

Method Description
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 collection

Logical Operators

You can negate an expression with not():, (*25)

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

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

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

The same is possible for the "or" operator:, (*27)

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

You can use and/or inside selectors:, (*28)

$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:, (*29)

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

Testing

To make sure that PHPUnit compares [Expression] objects correctly, you should register the [ExpressionComparator] with PHPUnit in your PHPUnit bootstrap file:, (*30)

// 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:, (*31)




<phpunit bootstrap="tests/bootstrap.php" colors="true">
    <!-- ... -->
</phpunit>

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

// 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

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 query., (*33)

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

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] to traverse an expression with your visitor:, (*35)

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

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

    return $qb;
}

Authors

Contribute

Contributions to the package are always welcome!, (*36)

Support

If you are having problems, send a mail to bschussek@gmail.com or shout out to @webmozart on Twitter., (*37)

License

All contents of this package are licensed under the MIT license., (*38)

The Versions

15/08 2016

dev-master

9999999-dev

Implementation of the Specification pattern and logical expressions for PHP.

  Sources   Download

MIT

The Requires

  • php ^5.3.9|^7.0

 

The Development Requires

by Bernhard Schussek

filter specification expression logic criteria formula

17/12 2015

1.0.0

1.0.0.0

Formulate expressions and search criteria using PHP objects.

  Sources   Download

MIT

The Requires

  • php >=5.3.9

 

The Development Requires

by Bernhard Schussek

filter expression criteria formula

02/10 2015

1.0.0-beta5

1.0.0.0-beta5

Formulate expressions and search criteria using PHP objects.

  Sources   Download

MIT

The Requires

  • php >=5.3.9

 

The Development Requires

by Bernhard Schussek

filter expression criteria formula

24/08 2015

1.0.0-beta4

1.0.0.0-beta4

Formulate expressions and search criteria using PHP objects.

  Sources   Download

MIT

The Requires

  • php >=5.3.9

 

The Development Requires

by Bernhard Schussek

filter expression criteria formula

28/05 2015

1.0.0-beta3

1.0.0.0-beta3

Formulate expressions and search criteria using PHP objects.

  Sources   Download

MIT

The Requires

  • php >=5.3.9

 

The Development Requires

by Bernhard Schussek

filter expression criteria formula

13/04 2015

1.0.0-beta2

1.0.0.0-beta2

Formulate expressions and search criteria using PHP objects.

  Sources   Download

MIT

The Requires

  • php >=5.3.9

 

The Development Requires

by Bernhard Schussek

filter expression criteria formula

19/03 2015

1.0.0-beta

1.0.0.0-beta

Formulate expressions and search criteria using PHP objects.

  Sources   Download

MIT

The Requires

  • php >=5.3.9

 

The Development Requires

by Bernhard Schussek

filter expression criteria formula