Full Code of webmozart/expression for AI

master 49ef2cfe8b04 cached
89 files
217.1 KB
57.7k tokens
571 symbols
1 requests
Download .txt
Showing preview only (238K chars total). Download the full file or copy to clipboard to get everything.
Repository: webmozart/expression
Branch: master
Commit: 49ef2cfe8b04
Files: 89
Total size: 217.1 KB

Directory structure:
gitextract_uy2uw1lp/

├── .composer-auth.json
├── .gitignore
├── .styleci.yml
├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── appveyor.yml
├── composer.json
├── phpunit.xml.dist
├── src/
│   ├── Constraint/
│   │   ├── Contains.php
│   │   ├── EndsWith.php
│   │   ├── Equals.php
│   │   ├── GreaterThan.php
│   │   ├── GreaterThanEqual.php
│   │   ├── In.php
│   │   ├── IsEmpty.php
│   │   ├── IsInstanceOf.php
│   │   ├── KeyExists.php
│   │   ├── KeyNotExists.php
│   │   ├── LessThan.php
│   │   ├── LessThanEqual.php
│   │   ├── Matches.php
│   │   ├── NotEquals.php
│   │   ├── NotSame.php
│   │   ├── Same.php
│   │   └── StartsWith.php
│   ├── Expr.php
│   ├── Expression.php
│   ├── Logic/
│   │   ├── AlwaysFalse.php
│   │   ├── AlwaysTrue.php
│   │   ├── AndX.php
│   │   ├── Literal.php
│   │   ├── Not.php
│   │   └── OrX.php
│   ├── PhpUnit/
│   │   └── ExpressionComparator.php
│   ├── Selector/
│   │   ├── All.php
│   │   ├── AtLeast.php
│   │   ├── AtMost.php
│   │   ├── Count.php
│   │   ├── Exactly.php
│   │   ├── Key.php
│   │   ├── Method.php
│   │   ├── Property.php
│   │   └── Selector.php
│   ├── Traversal/
│   │   ├── ExpressionTraverser.php
│   │   └── ExpressionVisitor.php
│   └── Util/
│       └── StringUtil.php
└── tests/
    ├── Comparison/
    │   ├── ContainsTest.php
    │   ├── EndsWithTest.php
    │   ├── EqualsTest.php
    │   ├── GreaterThanEqualTest.php
    │   ├── GreaterThanTest.php
    │   ├── InTest.php
    │   ├── IsEmptyTest.php
    │   ├── IsInstanceOfTest.php
    │   ├── KeyExistsTest.php
    │   ├── KeyNotExistsTest.php
    │   ├── LessThanEqualTest.php
    │   ├── LessThanTest.php
    │   ├── MatchesTest.php
    │   ├── NotEqualsTest.php
    │   ├── NotSameTest.php
    │   ├── SameTest.php
    │   └── StartsWithTest.php
    ├── DomainExpressionsTest.php
    ├── EquivalenceTest.php
    ├── ExprTest.php
    ├── Fixtures/
    │   ├── Customer.php
    │   ├── HasPreviousBookings.php
    │   └── IsPremium.php
    ├── Logic/
    │   ├── AlwaysFalseTest.php
    │   ├── AlwaysTrueTest.php
    │   ├── ConjunctionTest.php
    │   ├── DisjunctionTest.php
    │   ├── Fixtures/
    │   │   └── TestLiteral.php
    │   ├── LiteralTest.php
    │   └── NotTest.php
    ├── Selector/
    │   ├── AllTest.php
    │   ├── AtLeastTest.php
    │   ├── AtMostTest.php
    │   ├── CountTest.php
    │   ├── ExactlyTest.php
    │   ├── Fixtures/
    │   │   └── TestSelector.php
    │   ├── KeyTest.php
    │   ├── MethodTest.php
    │   ├── PropertyTest.php
    │   └── SelectorTest.php
    └── Traversal/
        └── ExpressionTraverserTest.php

================================================
FILE CONTENTS
================================================

================================================
FILE: .composer-auth.json
================================================
{
    "github-oauth": {
        "github.com": "PLEASE DO NOT USE THIS TOKEN IN YOUR OWN PROJECTS/FORKS",
        "github.com": "This token is reserved for testing the webmozart/* repositories",
        "github.com": "a9debbffdd953ee9b3b82dbc3b807cde2086bb86"
    }
}


================================================
FILE: .gitignore
================================================
/vendor/
composer.lock


================================================
FILE: .styleci.yml
================================================
preset: symfony

enabled:
    - ordered_use

disabled:
    - empty_return
    - phpdoc_annotation_without_dot # This is still buggy: https://github.com/symfony/symfony/pull/19198


================================================
FILE: .travis.yml
================================================
language: php

sudo: false

branches:
  only:
    - master

cache:
  directories:
    - $HOME/.composer/cache/files

matrix:
  include:
    - php: 5.3
    - php: 5.4
    - php: 5.5
    - php: 5.6
    - php: hhvm
    - php: nightly
    - php: 7.0
      env: COVERAGE=yes
    - php: 7.0
      env: COMPOSER_FLAGS='--prefer-lowest --prefer-stable'
  allow_failures:
    - php: hhvm
    - php: nightly
  fast_finish: true

before_install:
  - if [[ $TRAVIS_PHP_VERSION != hhvm && $COVERAGE != yes ]]; then phpenv config-rm xdebug.ini; fi;
  - if [[ $TRAVIS_REPO_SLUG = webmozart/expression ]]; then cp .composer-auth.json ~/.composer/auth.json; fi;
  - composer self-update

install: composer update $COMPOSER_FLAGS --prefer-dist --no-interaction

script: if [[ $COVERAGE = yes ]]; then vendor/bin/phpunit --verbose --coverage-clover=coverage.clover; else vendor/bin/phpunit --verbose; fi

after_script: if [[ $COVERAGE = yes ]]; then wget https://scrutinizer-ci.com/ocular.phar && php ocular.phar code-coverage:upload --format=php-clover coverage.clover; fi


================================================
FILE: CHANGELOG.md
================================================
Changelog
=========

* 1.0.0 (2015-12-17)

 * added `Expr::filter()` to filter collections
 * removed `final` keyword of expression classes to facilitate building
   domain expressions
 * renamed `Conjunction` to `AndX`
 * renamed `Disjunction` to `OrX`
 * added `Expr::andX()` and `Expr::orX()`

* 1.0.0-beta5 (2015-10-02)

 * added `method()` selector
 * added `property()` selector
 * removed optional `$key` arguments. Use the `key()`/`method()`/`property()`
   selectors instead
 * added `isInstanceOf()`
 * removed class `NotEmpty` and used `Not` with `IsEmpty` instead
 * renamed `Webmozart\Expression\Comparison` namespace to `Webmozart\Expression\Constraint`
 * fixed type juggling in `equivalentTo()`

* 1.0.0-beta4 (2015-08-24)

 * fixed return types in `Expr::true()` and `Expr::false()`
 * fixed minimum versions in composer.json

* 1.0.0-beta3 (2015-05-28)

 * optimized `Valid::andX()` and `Valid::orX()`
 * optimized `Invalid::andX()` and `Invalid::orX()`
 * removed `true()` and `false()`. Use `same()` instead
 * renamed `valid()` to `true()` and `invalid()` to `false()`
 * added `contains()`
 * added brackets around string output of nested conjunctions/disjunctions

* 1.0.0-beta2 (2015-04-13)

 * added `Selector`
 * removed `key*()` methods
 * renamed argument `$field` to `$key`, moved it to end of the method arguments
   and made it optional for all test methods
 * removed argument `$field` from `key()`
 * removed argument `$strict` from `true()`, `false()` and `oneOf()`
 * added `atLeast()` selector
 * added `exactly()` selector
 * added `all()` selector
 * added `atMost()` selector
 * added `count()` selector
 * renamed `oneOf()` to `in()`
 * added `valid()`
 * added `invalid()`
 * optimized `andValid()`, `andInvalid()`, `orValid()` and `orInvalid()`

* 1.0.0-beta (2015-03-19)

 * first beta release


================================================
FILE: LICENSE
================================================
The MIT License (MIT)

Copyright (c) 2014 Bernhard Schussek

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


================================================
FILE: README.md
================================================
Webmozart Expression
====================

[![Build Status](https://travis-ci.org/webmozart/expression.svg?branch=master)](https://travis-ci.org/webmozart/expression)
[![Build status](https://ci.appveyor.com/api/projects/status/6dstc380h5pr5rk3/branch/master?svg=true)](https://ci.appveyor.com/project/webmozart/expression/branch/master)
[![Latest Stable Version](https://poser.pugx.org/webmozart/expression/v/stable.svg)](https://packagist.org/packages/webmozart/expression)
[![Total Downloads](https://poser.pugx.org/webmozart/expression/downloads.svg)](https://packagist.org/packages/webmozart/expression)
[![Dependency Status](https://www.versioneye.com/php/webmozart:expression/1.0.0/badge.svg)](https://www.versioneye.com/php/webmozart:expression/1.0.0)

Latest release: [1.0.0](https://packagist.org/packages/webmozart/expression#1.0.0)

PHP >= 5.3.9

This library implements the [Specification Pattern] for PHP. You can use it to
easily filter results of your domain services by evaluating logical expressions.

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.

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

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

Use [Composer] to install the package:

```
$ composer require webmozart/expression
```

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

Use the [`Expression`] interface in finder methods of your service classes:

```php
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:

```php
$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:

```php
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
------------------

Extend existing expressions to build domain-specific expressions:

```php
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
-----------

The [`Expr`] class is able to create the following expressions:

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*.

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

```php
$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:

```php
$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:

```php
$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:

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

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

The following table lists all available selectors:

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.

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:

```php
$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:

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()`:

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

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

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

The same is possible for the "or" operator:

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

You can use and/or inside selectors:

```php
$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:

```php
$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:

```php
// 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`:

```xml
<!-- phpunit.xml.dist -->
<?xml version="1.0" encoding="UTF-8"?>

<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:
 
```php
// 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.

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:

```php
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:

```php
public function expressionToQueryBuilder(Expression $expr)
{
    $qb = new QueryBuilder();
    
    $traverser = new ExpressionTraverser();
    $traverser->addVisitor(new QueryBuilderVisitor($qb));
    $traverser->traverse($expr);
    
    return $qb;
}
```

Authors
-------

* [Bernhard Schussek] a.k.a. [@webmozart]
* [The Community Contributors]

Contribute
----------

Contributions to the package are always welcome!

* Report any bugs or issues you find on the [issue tracker].
* You can grab the source code at the package's [Git repository].

Support
-------

If you are having problems, send a mail to bschussek@gmail.com or shout out to
[@webmozart] on Twitter.

License
-------

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

[Composer]: https://getcomposer.org
[Doctrine]: http://www.doctrine-project.org/
[Bernhard Schussek]: http://webmozarts.com
[The Community Contributors]: https://github.com/webmozart/expression/graphs/contributors
[issue tracker]: https://github.com/webmozart/expression
[Git repository]: https://github.com/webmozart/expression
[@webmozart]: https://twitter.com/webmozart
[MIT license]: LICENSE
[`Expression`]: src/Expression.php
[`Expr`]: src/Expr.php
[`ExpressionComparator`]: src/PhpUnit/ExpressionComparator.php
[`ExpressionTraverser`]: src/Traversal/ExpressionTraverser.php
[`ExpressionVisitor`]: src/Traversal/ExpressionVisitor.php
[Specification Pattern]: http://www.martinfowler.com/apsupp/spec.pdf
[rulerz]: https://github.com/K-Phoen/rulerz


================================================
FILE: appveyor.yml
================================================
build: false
platform: x86
clone_folder: c:\projects\webmozart\expression

branches:
  only:
    - master

cache:
  - c:\php -> appveyor.yml

init:
  - SET PATH=c:\php;%PATH%
  - SET COMPOSER_NO_INTERACTION=1
  - SET PHP=1

install:
  - IF EXIST c:\php (SET PHP=0) ELSE (mkdir c:\php)
  - cd c:\php
  - IF %PHP%==1 appveyor DownloadFile http://windows.php.net/downloads/releases/archives/php-7.0.0-nts-Win32-VC14-x86.zip
  - IF %PHP%==1 7z x php-7.0.0-nts-Win32-VC14-x86.zip -y >nul
  - IF %PHP%==1 del /Q *.zip
  - IF %PHP%==1 echo @php %%~dp0composer.phar %%* > composer.bat
  - IF %PHP%==1 copy /Y php.ini-development php.ini
  - IF %PHP%==1 echo max_execution_time=1200 >> php.ini
  - IF %PHP%==1 echo date.timezone="UTC" >> php.ini
  - IF %PHP%==1 echo extension_dir=ext >> php.ini
  - IF %PHP%==1 echo extension=php_curl.dll >> php.ini
  - IF %PHP%==1 echo extension=php_openssl.dll >> php.ini
  - IF %PHP%==1 echo extension=php_mbstring.dll >> php.ini
  - IF %PHP%==1 echo extension=php_fileinfo.dll >> php.ini
  - appveyor DownloadFile https://getcomposer.org/composer.phar
  - cd c:\projects\webmozart\expression
  - mkdir %APPDATA%\Composer
  - IF %APPVEYOR_REPO_NAME%==webmozart/expression copy /Y .composer-auth.json %APPDATA%\Composer\auth.json
  - composer update --prefer-dist --no-progress --ansi

test_script:
  - cd c:\projects\webmozart\expression
  - vendor\bin\phpunit.bat --verbose


================================================
FILE: composer.json
================================================
{
    "name": "webmozart/expression",
    "description": "Implementation of the Specification pattern and logical expressions for PHP.",
    "keywords": ["specification", "criteria", "formula", "expression", "filter", "logic"],
    "license": "MIT",
    "authors": [
        {
            "name": "Bernhard Schussek",
            "email": "bschussek@gmail.com"
        }
    ],
    "require": {
        "php": "^5.3.9|^7.0"
    },
    "require-dev": {
        "phpunit/phpunit": "^4.6",
        "sebastian/version": "^1.0.1"
    },
    "autoload": {
        "psr-4": {
            "Webmozart\\Expression\\": "src/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Webmozart\\Expression\\Tests\\": "tests/"
        }
    },
    "extra": {
        "branch-alias": {
            "dev-master": "1.0-dev"
        }
    }
}


================================================
FILE: phpunit.xml.dist
================================================
<?xml version="1.0" encoding="UTF-8"?>

<phpunit bootstrap="vendor/autoload.php" colors="true">
    <testsuites>
        <testsuite name="Webmozart Expression Test Suite">
            <directory suffix="Test.php">./tests/</directory>
        </testsuite>
    </testsuites>

    <!-- Whitelist for code coverage -->
    <filter>
        <whitelist>
            <directory suffix=".php">./src/</directory>
        </whitelist>
    </filter>
</phpunit>


================================================
FILE: src/Constraint/Contains.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Constraint;

use Webmozart\Expression\Expression;
use Webmozart\Expression\Logic\Literal;
use Webmozart\Expression\Util\StringUtil;

/**
 * Checks that a value contains another value.
 *
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 * @author Stephan Wentz <stephan@wentz.it>
 */
class Contains extends Literal
{
    /**
     * @var string
     */
    private $comparedValue;

    /**
     * Creates the expression.
     *
     * @param string $comparedValue The compared value.
     */
    public function __construct($comparedValue)
    {
        $this->comparedValue = (string) $comparedValue;
    }

    /**
     * Returns the accepted suffix.
     *
     * @return string The accepted suffix.
     */
    public function getComparedValue()
    {
        return $this->comparedValue;
    }

    /**
     * {@inheritdoc}
     */
    public function evaluate($value)
    {
        return false !== strpos($value, $this->comparedValue);
    }

    /**
     * {@inheritdoc}
     */
    public function equivalentTo(Expression $other)
    {
        // Since this class is final, we can check with instanceof
        return $other instanceof $this && $this->comparedValue === $other->comparedValue;
    }

    /**
     * {@inheritdoc}
     */
    public function toString()
    {
        return 'contains('.StringUtil::formatValue($this->comparedValue).')';
    }
}


================================================
FILE: src/Constraint/EndsWith.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Constraint;

use Webmozart\Expression\Expression;
use Webmozart\Expression\Logic\Literal;
use Webmozart\Expression\Util\StringUtil;

/**
 * Checks that a value has a given suffix.
 *
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class EndsWith extends Literal
{
    /**
     * @var string
     */
    private $acceptedSuffix;

    /**
     * Creates the expression.
     *
     * @param string $acceptedSuffix The accepted suffix.
     */
    public function __construct($acceptedSuffix)
    {
        $this->acceptedSuffix = (string) $acceptedSuffix;
    }

    /**
     * Returns the accepted suffix.
     *
     * @return string The accepted suffix.
     */
    public function getAcceptedSuffix()
    {
        return $this->acceptedSuffix;
    }

    /**
     * {@inheritdoc}
     */
    public function evaluate($value)
    {
        return $this->acceptedSuffix === substr($value, -strlen($this->acceptedSuffix));
    }

    /**
     * {@inheritdoc}
     */
    public function equivalentTo(Expression $other)
    {
        // Since this class is final, we can check with instanceof
        return $other instanceof $this && $this->acceptedSuffix === $other->acceptedSuffix;
    }

    /**
     * {@inheritdoc}
     */
    public function toString()
    {
        return 'endsWith('.StringUtil::formatValue($this->acceptedSuffix).')';
    }
}


================================================
FILE: src/Constraint/Equals.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Constraint;

use Webmozart\Expression\Expression;
use Webmozart\Expression\Logic\Literal;
use Webmozart\Expression\Util\StringUtil;

/**
 * Checks that a value equals another value.
 *
 * The comparison is done using PHP's "==" equality operator.
 *
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class Equals extends Literal
{
    /**
     * @var string
     */
    private $comparedValue;

    /**
     * Creates the expression.
     *
     * @param mixed $comparedValue The compared value.
     */
    public function __construct($comparedValue)
    {
        $this->comparedValue = $comparedValue;
    }

    /**
     * Returns the compared value.
     *
     * @return mixed The compared value.
     */
    public function getComparedValue()
    {
        return $this->comparedValue;
    }

    /**
     * {@inheritdoc}
     */
    public function evaluate($value)
    {
        return $this->comparedValue == $value;
    }

    /**
     * {@inheritdoc}
     */
    public function equivalentTo(Expression $other)
    {
        if ($other instanceof In && !$other->isStrict()) {
            return array($this->comparedValue) == $other->getAcceptedValues();
        }

        // Since this class is final, we can check with instanceof
        return $other instanceof $this && $this->comparedValue == $other->comparedValue;
    }

    /**
     * {@inheritdoc}
     */
    public function toString()
    {
        return '=='.StringUtil::formatValue($this->comparedValue);
    }
}


================================================
FILE: src/Constraint/GreaterThan.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Constraint;

use Webmozart\Expression\Expression;
use Webmozart\Expression\Logic\Literal;
use Webmozart\Expression\Util\StringUtil;

/**
 * Checks that a value is greater than a given value.
 *
 * The comparison is done using PHP's ">" comparison operator.
 *
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class GreaterThan extends Literal
{
    /**
     * @var mixed
     */
    private $comparedValue;

    /**
     * Creates the expression.
     *
     * @param mixed $comparedValue The compared value.
     */
    public function __construct($comparedValue)
    {
        $this->comparedValue = $comparedValue;
    }

    /**
     * Returns the compared value.
     *
     * @return mixed The compared value.
     */
    public function getComparedValue()
    {
        return $this->comparedValue;
    }

    /**
     * {@inheritdoc}
     */
    public function evaluate($value)
    {
        return $value > $this->comparedValue;
    }

    /**
     * {@inheritdoc}
     */
    public function equivalentTo(Expression $other)
    {
        // Since this class is final, we can check with instanceof
        return $other instanceof $this && $this->comparedValue == $other->comparedValue;
    }

    /**
     * {@inheritdoc}
     */
    public function toString()
    {
        return '>'.StringUtil::formatValue($this->comparedValue);
    }
}


================================================
FILE: src/Constraint/GreaterThanEqual.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Constraint;

use Webmozart\Expression\Expression;
use Webmozart\Expression\Logic\Literal;
use Webmozart\Expression\Util\StringUtil;

/**
 * Checks that a value is greater than or equal to a given value.
 *
 * The comparison is done using PHP's ">=" comparison operator.
 *
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class GreaterThanEqual extends Literal
{
    /**
     * @var mixed
     */
    private $comparedValue;

    /**
     * Creates the expression.
     *
     * @param mixed $comparedValue The compared value.
     */
    public function __construct($comparedValue)
    {
        $this->comparedValue = $comparedValue;
    }

    /**
     * Returns the compared value.
     *
     * @return mixed The compared value.
     */
    public function getComparedValue()
    {
        return $this->comparedValue;
    }

    /**
     * {@inheritdoc}
     */
    public function evaluate($value)
    {
        return $value >= $this->comparedValue;
    }

    /**
     * {@inheritdoc}
     */
    public function equivalentTo(Expression $other)
    {
        // Since this class is final, we can check with instanceof
        return $other instanceof $this && $this->comparedValue == $other->comparedValue;
    }

    /**
     * {@inheritdoc}
     */
    public function toString()
    {
        return '>='.StringUtil::formatValue($this->comparedValue);
    }
}


================================================
FILE: src/Constraint/In.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Constraint;

use Webmozart\Expression\Expression;
use Webmozart\Expression\Logic\Literal;
use Webmozart\Expression\Util\StringUtil;

/**
 * Checks that a value is one of a list of values.
 *
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class In extends Literal
{
    /**
     * @var array
     */
    private $acceptedValues;

    /**
     * @var bool
     */
    private $strict;

    /**
     * Creates the expression.
     *
     * @param array $acceptedValues The accepted values.
     * @param bool  $strict         Whether to do strict comparison.
     */
    public function __construct(array $acceptedValues, $strict = true)
    {
        $this->acceptedValues = $acceptedValues;
        $this->strict = $strict;
    }

    /**
     * Returns the accepted values.
     *
     * @return array The accepted values.
     */
    public function getAcceptedValues()
    {
        return $this->acceptedValues;
    }

    /**
     * Returns whether the value is compared strictly.
     *
     * @return bool Returns `true` if using strict comparison (`===`) and
     *              `false` if using weak comparison (`==`).
     */
    public function isStrict()
    {
        return $this->strict;
    }

    /**
     * {@inheritdoc}
     */
    public function evaluate($value)
    {
        return in_array($value, $this->acceptedValues, $this->strict);
    }

    /**
     * {@inheritdoc}
     */
    public function equivalentTo(Expression $other)
    {
        if (1 === count($this->acceptedValues)) {
            // In is logically equivalent to Same if strict and only one value
            if ($this->strict && $other instanceof Same) {
                return reset($this->acceptedValues) === $other->getComparedValue();
            }

            // In is logically equivalent to Equals if not strict and only one value
            if (!$this->strict && $other instanceof Equals) {
                return reset($this->acceptedValues) == $other->getComparedValue();
            }
        }

        // Since this class is final, we can check with instanceof
        if (!$other instanceof $this) {
            return false;
        }

        if ($this->strict !== $other->strict) {
            return false;
        }

        if (!$this->strict) {
            return $this->acceptedValues == $other->acceptedValues;
        }

        $acceptedValuesLeft = $this->acceptedValues;
        $acceptedValuesRight = $other->acceptedValues;

        // Ignore order
        sort($acceptedValuesLeft);
        sort($acceptedValuesRight);

        return $acceptedValuesLeft === $acceptedValuesRight;
    }

    /**
     * {@inheritdoc}
     */
    public function toString()
    {
        $values = array_map(function ($value) {
            return StringUtil::formatValue($value);
        }, $this->acceptedValues);

        return 'in('.implode(', ', $values).')';
    }
}


================================================
FILE: src/Constraint/IsEmpty.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Constraint;

use Webmozart\Expression\Expression;
use Webmozart\Expression\Logic\Literal;

/**
 * Checks that a value is empty.
 *
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class IsEmpty extends Literal
{
    /**
     * {@inheritdoc}
     */
    public function evaluate($value)
    {
        return empty($value);
    }

    /**
     * {@inheritdoc}
     */
    public function equivalentTo(Expression $other)
    {
        // Since this class is final, we can check with instanceof
        return $other instanceof $this;
    }

    /**
     * {@inheritdoc}
     */
    public function toString()
    {
        return 'empty()';
    }
}


================================================
FILE: src/Constraint/IsInstanceOf.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Constraint;

use Webmozart\Expression\Expression;
use Webmozart\Expression\Logic\Literal;

/**
 * Checks that a value is an instance of a given class name.
 *
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class IsInstanceOf extends Literal
{
    /**
     * @var array
     */
    private $className;

    /**
     * Creates the expression.
     *
     * @param string $className The accepted class name.
     */
    public function __construct($className)
    {
        $this->className = $className;
    }

    /**
     * Returns the accepted class name.
     *
     * @return array The accepted class name.
     */
    public function getClassName()
    {
        return $this->className;
    }

    /**
     * {@inheritdoc}
     */
    public function evaluate($value)
    {
        return $value instanceof $this->className;
    }

    /**
     * {@inheritdoc}
     */
    public function equivalentTo(Expression $other)
    {
        // Since this class is final, we can check with instanceof
        if (!$other instanceof $this) {
            return false;
        }

        return $this->className === $other->className;
    }

    /**
     * {@inheritdoc}
     */
    public function toString()
    {
        return 'instanceOf('.$this->className.')';
    }
}


================================================
FILE: src/Constraint/KeyExists.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Constraint;

use Webmozart\Expression\Expression;
use Webmozart\Expression\Logic\Literal;
use Webmozart\Expression\Util\StringUtil;

/**
 * Checks that an array key exists.
 *
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class KeyExists extends Literal
{
    /**
     * @var string
     */
    private $key;

    /**
     * Creates the expression.
     *
     * @param string $key The array key.
     */
    public function __construct($key)
    {
        $this->key = (string) $key;
    }

    /**
     * Returns the array key.
     *
     * @return string The array key.
     */
    public function getKey()
    {
        return $this->key;
    }

    /**
     * {@inheritdoc}
     */
    public function evaluate($value)
    {
        if (!is_array($value)) {
            return false;
        }

        return array_key_exists($this->key, $value);
    }

    /**
     * {@inheritdoc}
     */
    public function equivalentTo(Expression $other)
    {
        // Since this class is final, we can check with instanceof
        return $other instanceof $this && $this->key === $other->key;
    }

    /**
     * {@inheritdoc}
     */
    public function toString()
    {
        return 'keyExists('.StringUtil::formatValue($this->key).')';
    }
}


================================================
FILE: src/Constraint/KeyNotExists.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Constraint;

use Webmozart\Expression\Expression;
use Webmozart\Expression\Logic\Literal;
use Webmozart\Expression\Util\StringUtil;

/**
 * Checks that an array key does not exist.
 *
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class KeyNotExists extends Literal
{
    /**
     * @var string
     */
    private $key;

    /**
     * Creates the expression.
     *
     * @param string $key The array key.
     */
    public function __construct($key)
    {
        $this->key = (string) $key;
    }

    /**
     * Returns the array key.
     *
     * @return string The array key.
     */
    public function getKey()
    {
        return $this->key;
    }

    /**
     * {@inheritdoc}
     */
    public function evaluate($value)
    {
        if (!is_array($value)) {
            return false;
        }

        return !array_key_exists($this->key, $value);
    }

    /**
     * {@inheritdoc}
     */
    public function equivalentTo(Expression $other)
    {
        // Since this class is final, we can check with instanceof
        return $other instanceof $this && $this->key === $other->key;
    }

    /**
     * {@inheritdoc}
     */
    public function toString()
    {
        return 'keyNotExists('.StringUtil::formatValue($this->key).')';
    }
}


================================================
FILE: src/Constraint/LessThan.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Constraint;

use Webmozart\Expression\Expression;
use Webmozart\Expression\Logic\Literal;
use Webmozart\Expression\Util\StringUtil;

/**
 * Checks that a value is less than a given value.
 *
 * The comparison is done using PHP's "<" comparison operator.
 *
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class LessThan extends Literal
{
    /**
     * @var mixed
     */
    private $comparedValue;

    /**
     * Creates the expression.
     *
     * @param mixed $comparedValue The compared value.
     */
    public function __construct($comparedValue)
    {
        $this->comparedValue = $comparedValue;
    }

    /**
     * Returns the compared value.
     *
     * @return mixed The compared value.
     */
    public function getComparedValue()
    {
        return $this->comparedValue;
    }

    /**
     * {@inheritdoc}
     */
    public function evaluate($value)
    {
        return $value < $this->comparedValue;
    }

    /**
     * {@inheritdoc}
     */
    public function equivalentTo(Expression $other)
    {
        // Since this class is final, we can check with instanceof
        return $other instanceof $this && $this->comparedValue == $other->comparedValue;
    }

    /**
     * {@inheritdoc}
     */
    public function toString()
    {
        return '<'.StringUtil::formatValue($this->comparedValue);
    }
}


================================================
FILE: src/Constraint/LessThanEqual.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Constraint;

use Webmozart\Expression\Expression;
use Webmozart\Expression\Logic\Literal;
use Webmozart\Expression\Util\StringUtil;

/**
 * Checks that a value is less than or equal to a given value.
 *
 * The comparison is done using PHP's "<=" comparison operator.
 *
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class LessThanEqual extends Literal
{
    /**
     * @var mixed
     */
    private $comparedValue;

    /**
     * Creates the expression.
     *
     * @param mixed $comparedValue The compared value.
     */
    public function __construct($comparedValue)
    {
        $this->comparedValue = $comparedValue;
    }

    /**
     * Returns the compared value.
     *
     * @return mixed The compared value.
     */
    public function getComparedValue()
    {
        return $this->comparedValue;
    }

    /**
     * {@inheritdoc}
     */
    public function evaluate($value)
    {
        return $value <= $this->comparedValue;
    }

    /**
     * {@inheritdoc}
     */
    public function equivalentTo(Expression $other)
    {
        // Since this class is final, we can check with instanceof
        return $other instanceof $this && $this->comparedValue == $other->comparedValue;
    }

    /**
     * {@inheritdoc}
     */
    public function toString()
    {
        return '<='.StringUtil::formatValue($this->comparedValue);
    }
}


================================================
FILE: src/Constraint/Matches.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Constraint;

use Webmozart\Expression\Expression;
use Webmozart\Expression\Logic\Literal;
use Webmozart\Expression\Util\StringUtil;

/**
 * Checks that a value matches a given regular expression.
 *
 * The comparison is done using PHP's `preg_match()` function.
 *
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class Matches extends Literal
{
    /**
     * @var string
     */
    private $regExp;

    /**
     * Creates the expression.
     *
     * @param string $regExp The regular expression.
     */
    public function __construct($regExp)
    {
        $this->regExp = $regExp;
    }

    /**
     * Returns the regular expression.
     *
     * @return mixed The regular expression.
     */
    public function getRegularExpression()
    {
        return $this->regExp;
    }

    /**
     * {@inheritdoc}
     */
    public function evaluate($value)
    {
        return (bool) preg_match($this->regExp, $value);
    }

    /**
     * {@inheritdoc}
     */
    public function equivalentTo(Expression $other)
    {
        // Since this class is final, we can check with instanceof
        return $other instanceof $this && $this->regExp === $other->regExp;
    }

    /**
     * {@inheritdoc}
     */
    public function toString()
    {
        return 'matches('.StringUtil::formatValue($this->regExp).')';
    }
}


================================================
FILE: src/Constraint/NotEquals.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Constraint;

use Webmozart\Expression\Expression;
use Webmozart\Expression\Logic\Literal;
use Webmozart\Expression\Util\StringUtil;

/**
 * Checks that a value does not equal another value.
 *
 * The comparison is done using PHP's "!=" equality operator.
 *
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class NotEquals extends Literal
{
    /**
     * @var mixed
     */
    private $comparedValue;

    /**
     * Creates the expression.
     *
     * @param mixed $comparedValue The compared value.
     */
    public function __construct($comparedValue)
    {
        $this->comparedValue = $comparedValue;
    }

    /**
     * Returns the compared value.
     *
     * @return mixed The compared value.
     */
    public function getComparedValue()
    {
        return $this->comparedValue;
    }

    /**
     * {@inheritdoc}
     */
    public function evaluate($value)
    {
        return $this->comparedValue != $value;
    }

    /**
     * {@inheritdoc}
     */
    public function equivalentTo(Expression $other)
    {
        // Since this class is final, we can check with instanceof
        return $other instanceof $this && $this->comparedValue == $other->comparedValue;
    }

    /**
     * {@inheritdoc}
     */
    public function toString()
    {
        return '!='.StringUtil::formatValue($this->comparedValue);
    }
}


================================================
FILE: src/Constraint/NotSame.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Constraint;

use Webmozart\Expression\Expression;
use Webmozart\Expression\Logic\Literal;
use Webmozart\Expression\Util\StringUtil;

/**
 * Checks that a value is not identical to another value.
 *
 * The comparison is done using PHP's "!==" equality operator.
 *
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class NotSame extends Literal
{
    /**
     * @var mixed
     */
    private $comparedValue;

    /**
     * Creates the expression.
     *
     * @param mixed $comparedValue The compared value.
     */
    public function __construct($comparedValue)
    {
        $this->comparedValue = $comparedValue;
    }

    /**
     * Returns the compared value.
     *
     * @return mixed The compared value.
     */
    public function getComparedValue()
    {
        return $this->comparedValue;
    }

    /**
     * {@inheritdoc}
     */
    public function evaluate($value)
    {
        return $this->comparedValue !== $value;
    }

    /**
     * {@inheritdoc}
     */
    public function equivalentTo(Expression $other)
    {
        // Since this class is final, we can check with instanceof
        return $other instanceof $this && $this->comparedValue === $other->comparedValue;
    }

    /**
     * {@inheritdoc}
     */
    public function toString()
    {
        return '!=='.StringUtil::formatValue($this->comparedValue);
    }
}


================================================
FILE: src/Constraint/Same.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Constraint;

use Webmozart\Expression\Expression;
use Webmozart\Expression\Logic\Literal;
use Webmozart\Expression\Util\StringUtil;

/**
 * Checks that a value is identical to another value.
 *
 * The comparison is done using PHP's "===" equality operator.
 *
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class Same extends Literal
{
    /**
     * @var mixed
     */
    private $comparedValue;

    /**
     * Creates the expression.
     *
     * @param mixed $comparedValue The compared value.
     */
    public function __construct($comparedValue)
    {
        $this->comparedValue = $comparedValue;
    }

    /**
     * Returns the compared value.
     *
     * @return mixed The compared value.
     */
    public function getComparedValue()
    {
        return $this->comparedValue;
    }

    /**
     * {@inheritdoc}
     */
    public function evaluate($value)
    {
        return $this->comparedValue === $value;
    }

    /**
     * {@inheritdoc}
     */
    public function equivalentTo(Expression $other)
    {
        if ($other instanceof In && $other->isStrict()) {
            return array($this->comparedValue) === $other->getAcceptedValues();
        }

        // Since this class is final, we can check with instanceof
        return $other instanceof $this && $this->comparedValue === $other->comparedValue;
    }

    /**
     * {@inheritdoc}
     */
    public function toString()
    {
        return '==='.StringUtil::formatValue($this->comparedValue);
    }
}


================================================
FILE: src/Constraint/StartsWith.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Constraint;

use Webmozart\Expression\Expression;
use Webmozart\Expression\Logic\Literal;
use Webmozart\Expression\Util\StringUtil;

/**
 * Checks that a value has a given prefix.
 *
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class StartsWith extends Literal
{
    /**
     * @var string
     */
    private $acceptedPrefix;

    /**
     * Creates the expression.
     *
     * @param string $acceptedPrefix The accepted prefix.
     */
    public function __construct($acceptedPrefix)
    {
        $this->acceptedPrefix = (string) $acceptedPrefix;
    }

    /**
     * Returns the accepted prefix.
     *
     * @return string The accepted prefix.
     */
    public function getAcceptedPrefix()
    {
        return $this->acceptedPrefix;
    }

    /**
     * {@inheritdoc}
     */
    public function evaluate($value)
    {
        return 0 === strpos($value, $this->acceptedPrefix);
    }

    /**
     * {@inheritdoc}
     */
    public function equivalentTo(Expression $other)
    {
        // Since this class is final, we can check with instanceof
        return $other instanceof $this && $this->acceptedPrefix === $other->acceptedPrefix;
    }

    /**
     * {@inheritdoc}
     */
    public function toString()
    {
        return 'startsWith('.StringUtil::formatValue($this->acceptedPrefix).')';
    }
}


================================================
FILE: src/Expr.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression;

use ArrayAccess;
use InvalidArgumentException;
use Traversable;
use Webmozart\Expression\Constraint\Contains;
use Webmozart\Expression\Constraint\EndsWith;
use Webmozart\Expression\Constraint\Equals;
use Webmozart\Expression\Constraint\GreaterThan;
use Webmozart\Expression\Constraint\GreaterThanEqual;
use Webmozart\Expression\Constraint\In;
use Webmozart\Expression\Constraint\IsEmpty;
use Webmozart\Expression\Constraint\IsInstanceOf;
use Webmozart\Expression\Constraint\KeyExists;
use Webmozart\Expression\Constraint\KeyNotExists;
use Webmozart\Expression\Constraint\LessThan;
use Webmozart\Expression\Constraint\LessThanEqual;
use Webmozart\Expression\Constraint\Matches;
use Webmozart\Expression\Constraint\NotEquals;
use Webmozart\Expression\Constraint\NotSame;
use Webmozart\Expression\Constraint\Same;
use Webmozart\Expression\Constraint\StartsWith;
use Webmozart\Expression\Logic\AlwaysFalse;
use Webmozart\Expression\Logic\AlwaysTrue;
use Webmozart\Expression\Logic\AndX;
use Webmozart\Expression\Logic\Not;
use Webmozart\Expression\Logic\OrX;
use Webmozart\Expression\Selector\All;
use Webmozart\Expression\Selector\AtLeast;
use Webmozart\Expression\Selector\AtMost;
use Webmozart\Expression\Selector\Count;
use Webmozart\Expression\Selector\Exactly;
use Webmozart\Expression\Selector\Key;
use Webmozart\Expression\Selector\Method;
use Webmozart\Expression\Selector\Property;

/**
 * Factory for {@link Expression} instances.
 *
 * Use this class to build expressions:ons:
 *
 * ```php
 * $expr = Expr::greaterThan(20)->orLessThan(10);
 * ```
 *
 * You can evaluate the expression with another value {@link Expression::evaluate()}:
 *
 * ```php
 * if ($expr->evaluate($value)) {
 *     // do something...
 * }
 * ```
 *
 * You can also evaluate expressions by arrays by passing the array keys as
 * second argument:
 *
 * ```php
 * $expr = Expr::greaterThan(20, 'age')
 *     ->andStartsWith('Thomas', 'name');
 *
 * $values = array(
 *     'age' => 35,
 *     'name' => 'Thomas Edison',
 * );
 *
 * if ($expr->evaluate($values)) {
 *     // do something...
 * }
 * ```
 *
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class Expr
{
    /**
     * Filter a collection for entries matching the expression.
     *
     * @param array|ArrayAccess|Traversable $collection An array or an object
     *                                                  implementing Traversable
     *                                                  and ArrayAccess.
     * @param Expression                    $expr       The expression to
     *                                                  evaluate for each entry.
     *
     * @return array|ArrayAccess|Traversable The filtered collection.
     */
    public static function filter($collection, Expression $expr)
    {
        if (is_array($collection)) {
            return array_filter($collection, array($expr, 'evaluate'));
        }

        if (!($collection instanceof Traversable && $collection instanceof ArrayAccess)) {
            throw new InvalidArgumentException(sprintf(
                'Expected an array or an instance of Traversable and ArrayAccess. Got: %s',
                is_object($collection) ? get_class($collection) : gettype($collection)
            ));
        }

        $clone = clone $collection;

        foreach ($collection as $key => $value) {
            if (!$expr->evaluate($value)) {
                unset($clone[$key]);
            }
        }

        return $clone;
    }

    /**
     * Returns the expression.
     *
     * Facilitates usage of expressions on PHP < 7.
     *
     * @param Expression $expr An expression.
     *
     * @return Expression The expression.
     */
    public static function expr(Expression $expr)
    {
        return $expr;
    }

    /**
     * Negate an expression.
     *
     * @param Expression $expr The negated expression.
     *
     * @return Not The created negation.
     */
    public static function not(Expression $expr)
    {
        return new Not($expr);
    }

    /**
     * Create a conjunction.
     *
     * @param Expression[] $conjuncts The conjuncts.
     *
     * @return AndX The created conjunction.
     */
    public static function andX(array $conjuncts)
    {
        return new AndX($conjuncts);
    }

    /**
     * Create a disjunction.
     *
     * @param Expression[] $disjuncts The disjuncts.
     *
     * @return OrX The created disjunction.
     */
    public static function orX(array $disjuncts)
    {
        return new OrX($disjuncts);
    }

    /**
     * Always true (tautology).
     *
     * @return AlwaysTrue The created expression.
     */
    public static function true()
    {
        return new AlwaysTrue();
    }

    /**
     * Always false (contradiction).
     *
     * @return AlwaysFalse The created expression.
     */
    public static function false()
    {
        return new AlwaysFalse();
    }

    /**
     * Check that the value of an array key matches an expression.
     *
     * @param string|int $key  The array key.
     * @param Expression $expr The evaluated expression.
     *
     * @return Key The created expression.
     */
    public static function key($key, Expression $expr)
    {
        return new Key($key, $expr);
    }

    /**
     * Check that the result of a method call matches an expression.
     *
     * @param string     $methodName The name of the method to call.
     * @param mixed      $args...    The method arguments.
     * @param Expression $expr       The evaluated expression.
     *
     * @return Method The created expression.
     */
    public static function method($methodName, $args)
    {
        $args = func_get_args();
        $methodName = array_shift($args);
        $expr = array_pop($args);

        return new Method($methodName, $args, $expr);
    }

    /**
     * Check that the value of a property matches an expression.
     *
     * @param string     $propertyName The name of the property.
     * @param Expression $expr         The evaluated expression.
     *
     * @return Method The created expression.
     */
    public static function property($propertyName, Expression $expr)
    {
        return new Property($propertyName, $expr);
    }

    /**
     * Check that at least N entries of a traversable value match an expression.
     *
     * @param int        $count The minimum number of entries that need to match.
     * @param Expression $expr  The evaluated expression.
     *
     * @return AtLeast The created expression.
     */
    public static function atLeast($count, Expression $expr)
    {
        return new AtLeast($count, $expr);
    }

    /**
     * Check that at most N entries of a traversable value match an expression.
     *
     * @param int        $count The maximum number of entries that need to match.
     * @param Expression $expr  The evaluated expression.
     *
     * @return AtMost The created expression.
     */
    public static function atMost($count, Expression $expr)
    {
        return new AtMost($count, $expr);
    }

    /**
     * Check that exactly N entries of a traversable value match an expression.
     *
     * @param int        $count The number of entries that need to match.
     * @param Expression $expr  The evaluated expression.
     *
     * @return Exactly The created expression.
     */
    public static function exactly($count, Expression $expr)
    {
        return new Exactly($count, $expr);
    }

    /**
     * Check that all entries of a traversable value match an expression.
     *
     * @param Expression $expr The evaluated expression.
     *
     * @return All The created expression.
     */
    public static function all(Expression $expr)
    {
        return new All($expr);
    }

    /**
     * Check that the count of a collection matches an expression.
     *
     * @param Expression $expr The evaluated expression.
     *
     * @return Count The created expression.
     */
    public static function count(Expression $expr)
    {
        return new Count($expr);
    }

    /**
     * Check that a value is null.
     *
     * @return Same The created expression.
     */
    public static function null()
    {
        return new Same(null);
    }

    /**
     * Check that a value is not null.
     *
     * @return NotSame The created expression.
     */
    public static function notNull()
    {
        return new NotSame(null);
    }

    /**
     * Check that a value is empty.
     *
     * @return IsEmpty The created expression.
     */
    public static function isEmpty()
    {
        return new IsEmpty();
    }

    /**
     * Check that a value is not empty.
     *
     * @return Not The created expression.
     */
    public static function notEmpty()
    {
        return new Not(new IsEmpty());
    }

    /**
     * Check that a value is an instance of a given class.
     *
     * @param string $className The class name.
     *
     * @return IsEmpty The created expression.
     */
    public static function isInstanceOf($className)
    {
        return new IsInstanceOf($className);
    }

    /**
     * Check that a value equals another value.
     *
     * @param mixed $value The compared value.
     *
     * @return Equals The created expression.
     */
    public static function equals($value)
    {
        return new Equals($value);
    }

    /**
     * Check that a value does not equal another value.
     *
     * @param mixed $value The compared value.
     *
     * @return NotEquals The created expression.
     */
    public static function notEquals($value)
    {
        return new NotEquals($value);
    }

    /**
     * Check that a value is identical to another value.
     *
     * @param mixed $value The compared value.
     *
     * @return Same The created expression.
     */
    public static function same($value)
    {
        return new Same($value);
    }

    /**
     * Check that a value is not identical to another value.
     *
     * @param mixed $value The compared value.
     *
     * @return NotSame The created expression.
     */
    public static function notSame($value)
    {
        return new NotSame($value);
    }

    /**
     * Check that a value is greater than another value.
     *
     * @param mixed $value The compared value.
     *
     * @return GreaterThan The created expression.
     */
    public static function greaterThan($value)
    {
        return new GreaterThan($value);
    }

    /**
     * Check that a value is greater than or equal to another value.
     *
     * @param mixed $value The compared value.
     *
     * @return GreaterThanEqual The created expression.
     */
    public static function greaterThanEqual($value)
    {
        return new GreaterThanEqual($value);
    }

    /**
     * Check that a value is less than another value.
     *
     * @param mixed $value The compared value.
     *
     * @return LessThan The created expression.
     */
    public static function lessThan($value)
    {
        return new LessThan($value);
    }

    /**
     * Check that a value is less than or equal to another value.
     *
     * @param mixed $value The compared value.
     *
     * @return LessThanEqual The created expression.
     */
    public static function lessThanEqual($value)
    {
        return new LessThanEqual($value);
    }

    /**
     * Check that a value occurs in a list of values.
     *
     * @param array $values The compared values.
     *
     * @return In The created expression.
     */
    public static function in(array $values)
    {
        return new In($values);
    }

    /**
     * Check that a value matches a regular expression.
     *
     * @param string $regExp The regular expression.
     *
     * @return Matches The created expression.
     */
    public static function matches($regExp)
    {
        return new Matches($regExp);
    }

    /**
     * Check that a value starts with a given string.
     *
     * @param string $prefix The prefix string.
     *
     * @return StartsWith The created expression.
     */
    public static function startsWith($prefix)
    {
        return new StartsWith($prefix);
    }

    /**
     * Check that a value ends with a given string.
     *
     * @param string $suffix The suffix string.
     *
     * @return EndsWith The created expression.
     */
    public static function endsWith($suffix)
    {
        return new EndsWith($suffix);
    }

    /**
     * Check that a value contains a given string.
     *
     * @param string $string The sub-string.
     *
     * @return Contains The created expression.
     */
    public static function contains($string)
    {
        return new Contains($string);
    }

    /**
     * Check that a value key exists.
     *
     * @param string $keyName The key name.
     *
     * @return KeyExists The created expression.
     */
    public static function keyExists($keyName)
    {
        return new KeyExists($keyName);
    }

    /**
     * Check that a value key does not exist.
     *
     * @param string $keyName The key name.
     *
     * @return KeyNotExists The created expression.
     */
    public static function keyNotExists($keyName)
    {
        return new KeyNotExists($keyName);
    }

    /**
     * This class cannot be instantiated.
     */
    private function __construct()
    {
    }
}


================================================
FILE: src/Expression.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression;

/**
 * A logical expression.
 *
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
interface Expression
{
    /**
     * Evaluates the expression with the given value.
     *
     * @param mixed $value A value.
     *
     * @return bool Returns `true` if the value satisfies the expression and
     *              `false` otherwise.
     */
    public function evaluate($value);

    /**
     * Returns whether this expression is logically equivalent to another expression.
     *
     * @param Expression $other Some expression.
     *
     * @return bool Returns `true` if the expressions are logically equivalent
     *              and `false` otherwise.
     */
    public function equivalentTo(Expression $other);

    /**
     * Returns a string representation of the expression.
     *
     * @return string The expression as string.
     */
    public function toString();
}


================================================
FILE: src/Logic/AlwaysFalse.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Logic;

use Webmozart\Expression\Expression;

/**
 * Always false (contradiction).
 *
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class AlwaysFalse extends Literal
{
    /**
     * {@inheritdoc}
     */
    public function evaluate($value)
    {
        return false;
    }

    /**
     * {@inheritdoc}
     */
    public function equivalentTo(Expression $other)
    {
        // Since this class is final, we can check with instanceof
        return $other instanceof $this;
    }

    /**
     * {@inheritdoc}
     */
    public function toString()
    {
        return 'false';
    }

    /**
     * {@inheritdoc}
     */
    public function andX(Expression $expr)
    {
        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function orX(Expression $expr)
    {
        return $expr;
    }
}


================================================
FILE: src/Logic/AlwaysTrue.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Logic;

use Webmozart\Expression\Expression;

/**
 * Always true (tautology).
 *
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class AlwaysTrue extends Literal
{
    /**
     * {@inheritdoc}
     */
    public function evaluate($value)
    {
        return true;
    }

    /**
     * {@inheritdoc}
     */
    public function equivalentTo(Expression $other)
    {
        // Since this class is final, we can check with instanceof
        return $other instanceof $this;
    }

    /**
     * {@inheritdoc}
     */
    public function toString()
    {
        return 'true';
    }

    /**
     * {@inheritdoc}
     */
    public function andX(Expression $expr)
    {
        return $expr;
    }

    /**
     * {@inheritdoc}
     */
    public function orX(Expression $expr)
    {
        return $this;
    }
}


================================================
FILE: src/Logic/AndX.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Logic;

use Webmozart\Expression\Expr;
use Webmozart\Expression\Expression;

/**
 * A disjunction of expressions.
 *
 * A disjunction is a set of {@link Expression} instances connected by logical
 * "and" operators.
 *
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class AndX implements Expression
{
    /**
     * @var Expression[]
     */
    private $conjuncts = array();

    /**
     * Creates a conjunction of the given expressions.
     *
     * @param Expression[] $conjuncts The conjuncts.
     */
    public function __construct(array $conjuncts = array())
    {
        foreach ($conjuncts as $conjunct) {
            if ($conjunct instanceof self) {
                foreach ($conjunct->conjuncts as $expr) {
                    // $conjunct is guaranteed not to contain Conjunctions
                    $this->conjuncts[] = $expr;
                }
            } else {
                $this->conjuncts[] = $conjunct;
            }
        }
    }

    /**
     * Returns the conjuncts of the conjunction.
     *
     * @return Expression[] The conjuncts.
     */
    public function getConjuncts()
    {
        return $this->conjuncts;
    }

    public function andX(Expression $expr)
    {
        if ($expr instanceof AlwaysTrue) {
            return $this;
        } elseif ($expr instanceof AlwaysFalse) {
            return $expr;
        }

        foreach ($this->conjuncts as $conjunct) {
            if ($conjunct->equivalentTo($expr)) {
                return $this;
            }
        }

        $conjuncts = $this->conjuncts;

        if ($expr instanceof self) {
            $conjuncts = array_merge($conjuncts, $expr->conjuncts);
        } else {
            $conjuncts[] = $expr;
        }

        return new self($conjuncts);
    }

    public function andNot(Expression $expr)
    {
        return $this->andX(Expr::not($expr));
    }

    public function andTrue()
    {
        return $this;
    }

    public function andFalse()
    {
        return Expr::false();
    }

    public function andKey($keyName, Expression $expr)
    {
        return $this->andX(Expr::key($keyName, $expr));
    }

    public function andMethod($methodName, $args)
    {
        return $this->andX(call_user_func_array(array('Webmozart\Expression\Expr', 'method'), func_get_args()));
    }

    public function andProperty($propertyName, Expression $expr)
    {
        return $this->andX(Expr::property($propertyName, $expr));
    }

    public function andAtLeast($count, Expression $expr)
    {
        return $this->andX(Expr::atLeast($count, $expr));
    }

    public function andAtMost($count, Expression $expr)
    {
        return $this->andX(Expr::atMost($count, $expr));
    }

    public function andExactly($count, Expression $expr)
    {
        return $this->andX(Expr::exactly($count, $expr));
    }

    public function andAll(Expression $expr)
    {
        return $this->andX(Expr::all($expr));
    }

    public function andCount(Expression $expr)
    {
        return $this->andX(Expr::count($expr));
    }

    public function andNull()
    {
        return $this->andX(Expr::null());
    }

    public function andNotNull()
    {
        return $this->andX(Expr::notNull());
    }

    public function andEmpty()
    {
        return $this->andX(Expr::isEmpty());
    }

    public function andNotEmpty()
    {
        return $this->andX(Expr::notEmpty());
    }

    public function andInstanceOf($className)
    {
        return $this->andX(Expr::isInstanceOf($className));
    }

    public function andEquals($value)
    {
        return $this->andX(Expr::equals($value));
    }

    public function andNotEquals($value)
    {
        return $this->andX(Expr::notEquals($value));
    }

    public function andSame($value)
    {
        return $this->andX(Expr::same($value));
    }

    public function andNotSame($value)
    {
        return $this->andX(Expr::notSame($value));
    }

    public function andGreaterThan($value)
    {
        return $this->andX(Expr::greaterThan($value));
    }

    public function andGreaterThanEqual($value)
    {
        return $this->andX(Expr::greaterThanEqual($value));
    }

    public function andLessThan($value)
    {
        return $this->andX(Expr::lessThan($value));
    }

    public function andLessThanEqual($value)
    {
        return $this->andX(Expr::lessThanEqual($value));
    }

    public function andIn(array $values)
    {
        return $this->andX(Expr::in($values));
    }

    public function andMatches($regExp)
    {
        return $this->andX(Expr::matches($regExp));
    }

    public function andStartsWith($prefix)
    {
        return $this->andX(Expr::startsWith($prefix));
    }

    public function andEndsWith($suffix)
    {
        return $this->andX(Expr::endsWith($suffix));
    }

    public function andContains($string)
    {
        return $this->andX(Expr::contains($string));
    }

    public function andKeyExists($keyName)
    {
        return $this->andX(Expr::keyExists($keyName));
    }

    public function andKeyNotExists($keyName)
    {
        return $this->andX(Expr::keyNotExists($keyName));
    }

    /**
     * {@inheritdoc}
     */
    public function evaluate($values)
    {
        foreach ($this->conjuncts as $expr) {
            if (!$expr->evaluate($values)) {
                return false;
            }
        }

        return true;
    }

    /**
     * {@inheritdoc}
     */
    public function equivalentTo(Expression $other)
    {
        if (get_class($this) !== get_class($other)) {
            return false;
        }

        /* @var static $other */
        $leftConjuncts = $this->conjuncts;
        $rightConjuncts = $other->conjuncts;

        foreach ($leftConjuncts as $leftConjunct) {
            foreach ($rightConjuncts as $j => $rightConjunct) {
                if ($leftConjunct->equivalentTo($rightConjunct)) {
                    unset($rightConjuncts[$j]);
                    continue 2;
                }
            }

            // $leftConjunct was not found in $rightConjuncts
            return false;
        }

        // All $leftConjuncts were found. Check if any $rightConjuncts are left
        return 0 === count($rightConjuncts);
    }

    /**
     * {@inheritdoc}
     */
    public function toString()
    {
        return implode(' && ', array_map(function (Expression $conjunct) {
            return $conjunct instanceof OrX ? '('.$conjunct->toString().')' : $conjunct->toString();
        }, $this->conjuncts));
    }

    public function __toString()
    {
        return $this->toString();
    }
}


================================================
FILE: src/Logic/Literal.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Logic;

use Webmozart\Expression\Expr;
use Webmozart\Expression\Expression;

/**
 * A logical literal.
 *
 * In pure logics, a literal is any part of a formula that does not contain
 * "and" or "or" operators. In this package, the definition of a literal is
 * widened to any logical expression that is *not* a conjunction/disjunction.
 *
 * Examples:
 *
 *  * not endsWith(".css")
 *  * greaterThan(0)
 *  * not (greaterThan(0) and lessThan(120))
 *
 * The following examples are *not* literals:
 *
 *  * greaterThan(0) and lessThan(120)
 *  * in(["A", "B", "C]) or null()
 *
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
abstract class Literal implements Expression
{
    public function andX(Expression $expr)
    {
        if ($expr instanceof AlwaysTrue) {
            return $this;
        } elseif ($expr instanceof AlwaysFalse) {
            return $expr;
        }

        if ($this->equivalentTo($expr)) {
            return $this;
        }

        return new AndX(array($this, $expr));
    }

    public function andNot(Expression $expr)
    {
        return $this->andX(Expr::not($expr));
    }

    public function andTrue()
    {
        return $this;
    }

    public function andFalse()
    {
        return Expr::false();
    }

    public function andKey($keyName, Expression $expr)
    {
        return $this->andX(Expr::key($keyName, $expr));
    }

    public function andMethod($methodName, $args)
    {
        return $this->andX(call_user_func_array(array('Webmozart\Expression\Expr', 'method'), func_get_args()));
    }

    public function andProperty($propertyName, Expression $expr)
    {
        return $this->andX(Expr::property($propertyName, $expr));
    }

    public function andAtLeast($count, Expression $expr)
    {
        return $this->andX(Expr::atLeast($count, $expr));
    }

    public function andAtMost($count, Expression $expr)
    {
        return $this->andX(Expr::atMost($count, $expr));
    }

    public function andExactly($count, Expression $expr)
    {
        return $this->andX(Expr::exactly($count, $expr));
    }

    public function andAll(Expression $expr)
    {
        return $this->andX(Expr::all($expr));
    }

    public function andCount(Expression $expr)
    {
        return $this->andX(Expr::count($expr));
    }

    public function andNull()
    {
        return $this->andX(Expr::null());
    }

    public function andNotNull()
    {
        return $this->andX(Expr::notNull());
    }

    public function andEmpty()
    {
        return $this->andX(Expr::isEmpty());
    }

    public function andNotEmpty()
    {
        return $this->andX(Expr::notEmpty());
    }

    public function andInstanceOf($className)
    {
        return $this->andX(Expr::isInstanceOf($className));
    }

    public function andEquals($value)
    {
        return $this->andX(Expr::equals($value));
    }

    public function andNotEquals($value)
    {
        return $this->andX(Expr::notEquals($value));
    }

    public function andSame($value)
    {
        return $this->andX(Expr::same($value));
    }

    public function andNotSame($value)
    {
        return $this->andX(Expr::notSame($value));
    }

    public function andGreaterThan($value)
    {
        return $this->andX(Expr::greaterThan($value));
    }

    public function andGreaterThanEqual($value)
    {
        return $this->andX(Expr::greaterThanEqual($value));
    }

    public function andLessThan($value)
    {
        return $this->andX(Expr::lessThan($value));
    }

    public function andLessThanEqual($value)
    {
        return $this->andX(Expr::lessThanEqual($value));
    }

    public function andIn(array $values)
    {
        return $this->andX(Expr::in($values));
    }

    public function andMatches($regExp)
    {
        return $this->andX(Expr::matches($regExp));
    }

    public function andStartsWith($prefix)
    {
        return $this->andX(Expr::startsWith($prefix));
    }

    public function andEndsWith($suffix)
    {
        return $this->andX(Expr::endsWith($suffix));
    }

    public function andContains($string)
    {
        return $this->andX(Expr::contains($string));
    }

    public function andKeyExists($keyName)
    {
        return $this->andX(Expr::keyExists($keyName));
    }

    public function andKeyNotExists($keyName)
    {
        return $this->andX(Expr::keyNotExists($keyName));
    }

    public function orX(Expression $expr)
    {
        if ($expr instanceof AlwaysFalse) {
            return $this;
        } elseif ($expr instanceof AlwaysTrue) {
            return $expr;
        }

        if ($this->equivalentTo($expr)) {
            return $this;
        }

        return new OrX(array($this, $expr));
    }

    public function orNot(Expression $expr)
    {
        return $this->orX(Expr::not($expr));
    }

    public function orTrue()
    {
        return Expr::true();
    }

    public function orFalse()
    {
        return $this;
    }

    public function orKey($keyName, Expression $expr)
    {
        return $this->orX(Expr::key($keyName, $expr));
    }

    public function orMethod($methodName, $args)
    {
        return $this->orX(call_user_func_array(array('Webmozart\Expression\Expr', 'method'), func_get_args()));
    }

    public function orProperty($propertyName, Expression $expr)
    {
        return $this->orX(Expr::property($propertyName, $expr));
    }

    public function orAtLeast($count, Expression $expr)
    {
        return $this->orX(Expr::atLeast($count, $expr));
    }

    public function orAtMost($count, Expression $expr)
    {
        return $this->orX(Expr::atMost($count, $expr));
    }

    public function orExactly($count, Expression $expr)
    {
        return $this->orX(Expr::exactly($count, $expr));
    }

    public function orAll(Expression $expr)
    {
        return $this->orX(Expr::all($expr));
    }

    public function orCount(Expression $expr)
    {
        return $this->orX(Expr::count($expr));
    }

    public function orNull()
    {
        return $this->orX(Expr::null());
    }

    public function orNotNull()
    {
        return $this->orX(Expr::notNull());
    }

    public function orEmpty()
    {
        return $this->orX(Expr::isEmpty());
    }

    public function orNotEmpty()
    {
        return $this->orX(Expr::notEmpty());
    }

    public function orInstanceOf($className)
    {
        return $this->orX(Expr::isInstanceOf($className));
    }

    public function orEquals($value)
    {
        return $this->orX(Expr::equals($value));
    }

    public function orNotEquals($value)
    {
        return $this->orX(Expr::notEquals($value));
    }

    public function orSame($value)
    {
        return $this->orX(Expr::same($value));
    }

    public function orNotSame($value)
    {
        return $this->orX(Expr::notSame($value));
    }

    public function orGreaterThan($value)
    {
        return $this->orX(Expr::greaterThan($value));
    }

    public function orGreaterThanEqual($value)
    {
        return $this->orX(Expr::greaterThanEqual($value));
    }

    public function orLessThan($value)
    {
        return $this->orX(Expr::lessThan($value));
    }

    public function orLessThanEqual($value)
    {
        return $this->orX(Expr::lessThanEqual($value));
    }

    public function orIn(array $values)
    {
        return $this->orX(Expr::in($values));
    }

    public function orMatches($regExp)
    {
        return $this->orX(Expr::matches($regExp));
    }

    public function orStartsWith($prefix)
    {
        return $this->orX(Expr::startsWith($prefix));
    }

    public function orEndsWith($suffix)
    {
        return $this->orX(Expr::endsWith($suffix));
    }

    public function orContains($string)
    {
        return $this->orX(Expr::contains($string));
    }

    public function orKeyExists($keyName)
    {
        return $this->orX(Expr::keyExists($keyName));
    }

    public function orKeyNotExists($keyName)
    {
        return $this->orX(Expr::keyNotExists($keyName));
    }

    public function __toString()
    {
        return $this->toString();
    }
}


================================================
FILE: src/Logic/Not.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Logic;

use Webmozart\Expression\Expression;

/**
 * Negates another expression.
 *
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class Not extends Literal
{
    /**
     * @var Expression
     */
    private $expr;

    /**
     * Creates the negation.
     *
     * @param Expression $expr The negated expression.
     */
    public function __construct(Expression $expr)
    {
        $this->expr = $expr;
    }

    /**
     * Returns the negated expression.
     *
     * @return Expression The negated expression.
     */
    public function getNegatedExpression()
    {
        return $this->expr;
    }

    /**
     * {@inheritdoc}
     */
    public function evaluate($value)
    {
        return !$this->expr->evaluate($value);
    }

    /**
     * {@inheritdoc}
     */
    public function equivalentTo(Expression $other)
    {
        // Since this class is final, we can check with instanceof
        return $other instanceof $this && $this->expr->equivalentTo($other->expr);
    }

    /**
     * {@inheritdoc}
     */
    public function toString()
    {
        $exprString = $this->expr->toString();

        if (isset($exprString[0]) && '(' === $exprString[0]) {
            return 'not'.$exprString;
        }

        return 'not('.$exprString.')';
    }
}


================================================
FILE: src/Logic/OrX.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Logic;

use Webmozart\Expression\Expr;
use Webmozart\Expression\Expression;

/**
 * A disjunction of expressions.
 *
 * A disjunction is a set of {@link Expression} instances connected by logical
 * "or" operators.
 *
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class OrX implements Expression
{
    /**
     * @var Expression[]
     */
    private $disjuncts = array();

    /**
     * Creates a disjunction of the given expressions.
     *
     * @param Expression[] $disjuncts The disjuncts.
     */
    public function __construct(array $disjuncts = array())
    {
        foreach ($disjuncts as $disjunct) {
            if ($disjunct instanceof self) {
                foreach ($disjunct->disjuncts as $expr) {
                    // $disjunct is guaranteed not to contain Disjunctions
                    $this->disjuncts[] = $expr;
                }
            } else {
                $this->disjuncts[] = $disjunct;
            }
        }
    }

    /**
     * Returns the disjuncts of the disjunction.
     *
     * @return Expression[] The disjuncts.
     */
    public function getDisjuncts()
    {
        return $this->disjuncts;
    }

    public function orX(Expression $expr)
    {
        if ($expr instanceof AlwaysFalse) {
            return $this;
        } elseif ($expr instanceof AlwaysTrue) {
            return $expr;
        }

        foreach ($this->disjuncts as $disjunct) {
            if ($disjunct->equivalentTo($expr)) {
                return $this;
            }
        }

        $disjuncts = $this->disjuncts;

        if ($expr instanceof self) {
            $disjuncts = array_merge($disjuncts, $expr->disjuncts);
        } else {
            $disjuncts[] = $expr;
        }

        return new self($disjuncts);
    }

    public function orNot(Expression $expr)
    {
        return $this->orX(Expr::not($expr));
    }

    public function orTrue()
    {
        return Expr::true();
    }

    public function orFalse()
    {
        return $this;
    }

    public function orKey($keyName, Expression $expr)
    {
        return $this->orX(Expr::key($keyName, $expr));
    }

    public function orMethod($methodName, $args)
    {
        return $this->orX(call_user_func_array(array('Webmozart\Expression\Expr', 'method'), func_get_args()));
    }

    public function orProperty($propertyName, Expression $expr)
    {
        return $this->orX(Expr::property($propertyName, $expr));
    }

    public function orAtLeast($count, Expression $expr)
    {
        return $this->orX(Expr::atLeast($count, $expr));
    }

    public function orAtMost($count, Expression $expr)
    {
        return $this->orX(Expr::atMost($count, $expr));
    }

    public function orExactly($count, Expression $expr)
    {
        return $this->orX(Expr::exactly($count, $expr));
    }

    public function orCount(Expression $expr)
    {
        return $this->orX(Expr::count($expr));
    }

    public function orAll(Expression $expr)
    {
        return $this->orX(Expr::all($expr));
    }

    public function orNull()
    {
        return $this->orX(Expr::null());
    }

    public function orNotNull()
    {
        return $this->orX(Expr::notNull());
    }

    public function orEmpty()
    {
        return $this->orX(Expr::isEmpty());
    }

    public function orNotEmpty()
    {
        return $this->orX(Expr::notEmpty());
    }

    public function orInstanceOf($className)
    {
        return $this->orX(Expr::isInstanceOf($className));
    }

    public function orEquals($value)
    {
        return $this->orX(Expr::equals($value));
    }

    public function orNotEquals($value)
    {
        return $this->orX(Expr::notEquals($value));
    }

    public function orSame($value)
    {
        return $this->orX(Expr::same($value));
    }

    public function orNotSame($value)
    {
        return $this->orX(Expr::notSame($value));
    }

    public function orGreaterThan($value)
    {
        return $this->orX(Expr::greaterThan($value));
    }

    public function orGreaterThanEqual($value)
    {
        return $this->orX(Expr::greaterThanEqual($value));
    }

    public function orLessThan($value)
    {
        return $this->orX(Expr::lessThan($value));
    }

    public function orLessThanEqual($value)
    {
        return $this->orX(Expr::lessThanEqual($value));
    }

    public function orIn(array $values)
    {
        return $this->orX(Expr::in($values));
    }

    public function orMatches($regExp)
    {
        return $this->orX(Expr::matches($regExp));
    }

    public function orStartsWith($prefix)
    {
        return $this->orX(Expr::startsWith($prefix));
    }

    public function orEndsWith($suffix)
    {
        return $this->orX(Expr::endsWith($suffix));
    }

    public function orContains($string)
    {
        return $this->orX(Expr::contains($string));
    }

    public function orKeyExists($keyName)
    {
        return $this->orX(Expr::keyExists($keyName));
    }

    public function orKeyNotExists($keyName)
    {
        return $this->orX(Expr::keyNotExists($keyName));
    }

    /**
     * {@inheritdoc}
     */
    public function evaluate($values)
    {
        foreach ($this->disjuncts as $expr) {
            if ($expr->evaluate($values)) {
                return true;
            }
        }

        return false;
    }

    /**
     * {@inheritdoc}
     */
    public function equivalentTo(Expression $other)
    {
        if (get_class($this) !== get_class($other)) {
            return false;
        }

        /* @var static $other */
        $leftDisjuncts = $this->disjuncts;
        $rightDisjuncts = $other->disjuncts;

        foreach ($leftDisjuncts as $leftDisjunct) {
            foreach ($rightDisjuncts as $j => $rightDisjunct) {
                if ($leftDisjunct->equivalentTo($rightDisjunct)) {
                    unset($rightDisjuncts[$j]);
                    continue 2;
                }
            }

            // $leftDisjunct was not found in $rightDisjuncts
            return false;
        }

        // All $leftDisjuncts were found. Check if any $rightDisjuncts are left
        return 0 === count($rightDisjuncts);
    }

    /**
     * {@inheritdoc}
     */
    public function toString()
    {
        return implode(' || ', array_map(function (Expression $disjunct) {
            return $disjunct instanceof AndX ? '('.$disjunct->toString().')' : $disjunct->toString();
        }, $this->disjuncts));
    }

    public function __toString()
    {
        return $this->toString();
    }
}


================================================
FILE: src/PhpUnit/ExpressionComparator.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\PhpUnit;

use SebastianBergmann\Comparator\Comparator;
use SebastianBergmann\Comparator\ComparisonFailure;
use Webmozart\Expression\Expression;

/**
 * Compares {@link Expression} objects for equality.
 *
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class ExpressionComparator extends Comparator
{
    /**
     * {@inheritdoc}
     */
    public function accepts($expected, $actual)
    {
        return $expected instanceof Expression && $actual instanceof Expression;
    }

    /**
     * {@inheritdoc}
     */
    public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false, array &$processed = array())
    {
        if (get_class($actual) !== get_class($expected)) {
            throw new ComparisonFailure(
                $expected,
                $actual,
                $this->exporter->export($expected),
                $this->exporter->export($actual),
                false,
                sprintf(
                    '%s is not instance of expected class "%s".',
                    $this->exporter->export($actual),
                    get_class($expected)
                )
            );
        }

        /** @var Expression $actual */
        if (!$actual->equivalentTo($expected)) {
            throw new ComparisonFailure(
                $expected,
                $actual,
                $expected->toString(),
                $actual->toString(),
                false,
                'Failed asserting that two expressions are equal.'
            );
        }
    }
}


================================================
FILE: src/Selector/All.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Selector;

use Traversable;

/**
 * Checks that all iterator entries match an expression.
 *
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class All extends Selector
{
    /**
     * {@inheritdoc}
     */
    public function evaluate($value)
    {
        if (!is_array($value) && !$value instanceof Traversable) {
            return false;
        }

        foreach ($value as $entry) {
            if (!$this->expr->evaluate($entry)) {
                return false;
            }
        }

        return true;
    }

    /**
     * {@inheritdoc}
     */
    public function toString()
    {
        return 'all('.$this->expr->toString().')';
    }
}


================================================
FILE: src/Selector/AtLeast.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Selector;

use Traversable;
use Webmozart\Expression\Expression;

/**
 * Checks that at least N iterator entries match an expression.
 *
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class AtLeast extends Selector
{
    /**
     * @var int
     */
    private $count;

    /**
     * Creates the expression.
     *
     * @param int        $count The minimum number of entries that must match
     *                          the expression.
     * @param Expression $expr  The expression to evaluate with each entry.
     */
    public function __construct($count, Expression $expr)
    {
        parent::__construct($expr);

        $this->count = (int) $count;
    }

    /**
     * {@inheritdoc}
     */
    public function evaluate($value)
    {
        if (!is_array($value) && !$value instanceof Traversable) {
            return false;
        }

        $found = 0;

        foreach ($value as $entry) {
            if ($this->expr->evaluate($entry)) {
                ++$found;

                if ($found >= $this->count) {
                    return true;
                }
            }
        }

        return false;
    }

    /**
     * {@inheritdoc}
     */
    public function equivalentTo(Expression $other)
    {
        if (!parent::equivalentTo($other)) {
            return false;
        }

        /* @var static $other */
        return $this->count === $other->count;
    }

    /**
     * {@inheritdoc}
     */
    public function toString()
    {
        return 'atLeast('.$this->count.', '.$this->expr->toString().')';
    }
}


================================================
FILE: src/Selector/AtMost.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Selector;

use Traversable;
use Webmozart\Expression\Expression;

/**
 * Checks that at most N iterator entries match an expression.
 *
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class AtMost extends Selector
{
    /**
     * @var int
     */
    private $count;

    /**
     * Creates the expression.
     *
     * @param int        $count The maximum number of entries that must match
     *                          the expression.
     * @param Expression $expr  The expression to evaluate with each entry.
     */
    public function __construct($count, Expression $expr)
    {
        parent::__construct($expr);

        $this->count = (int) $count;
    }

    /**
     * {@inheritdoc}
     */
    public function evaluate($value)
    {
        if (!is_array($value) && !$value instanceof Traversable) {
            return false;
        }

        $found = 0;

        foreach ($value as $entry) {
            if ($this->expr->evaluate($entry)) {
                ++$found;

                if ($found > $this->count) {
                    return false;
                }
            }
        }

        return true;
    }

    /**
     * {@inheritdoc}
     */
    public function equivalentTo(Expression $other)
    {
        if (!parent::equivalentTo($other)) {
            return false;
        }

        /* @var static $other */
        return $this->count === $other->count;
    }

    /**
     * {@inheritdoc}
     */
    public function toString()
    {
        return 'atMost('.$this->count.', '.$this->expr->toString().')';
    }
}


================================================
FILE: src/Selector/Count.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Selector;

use Countable;

/**
 * Checks that the count of a collection matches an expression.
 *
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class Count extends Selector
{
    /**
     * {@inheritdoc}
     */
    public function evaluate($value)
    {
        if (!is_array($value) && !$value instanceof Countable) {
            return false;
        }

        return $this->expr->evaluate(count($value));
    }

    /**
     * {@inheritdoc}
     */
    public function toString()
    {
        return 'count('.$this->expr->toString().')';
    }
}


================================================
FILE: src/Selector/Exactly.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Selector;

use Traversable;
use Webmozart\Expression\Expression;

/**
 * Checks that exactly N iterator entries match an expression.
 *
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class Exactly extends Selector
{
    /**
     * @var int
     */
    private $count;

    /**
     * Creates the expression.
     *
     * @param int        $count The number of entries that must match the
     *                          expression.
     * @param Expression $expr  The expression to evaluate with each entry.
     */
    public function __construct($count, Expression $expr)
    {
        parent::__construct($expr);

        $this->count = (int) $count;
    }

    /**
     * {@inheritdoc}
     */
    public function evaluate($value)
    {
        if (!is_array($value) && !$value instanceof Traversable) {
            return false;
        }

        $found = 0;

        foreach ($value as $entry) {
            if ($this->expr->evaluate($entry)) {
                ++$found;

                if ($found > $this->count) {
                    return false;
                }
            }
        }

        return $found === $this->count;
    }

    /**
     * {@inheritdoc}
     */
    public function equivalentTo(Expression $other)
    {
        if (!parent::equivalentTo($other)) {
            return false;
        }

        /* @var static $other */
        return $this->count === $other->count;
    }

    /**
     * {@inheritdoc}
     */
    public function toString()
    {
        return 'exactly('.$this->count.', '.$this->expr->toString().')';
    }
}


================================================
FILE: src/Selector/Key.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Selector;

use Webmozart\Expression\Expression;
use Webmozart\Expression\Logic\AndX;
use Webmozart\Expression\Logic\OrX;

/**
 * Checks whether an array key matches an expression.
 *
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class Key extends Selector
{
    /**
     * @var string
     */
    private $key;

    /**
     * Creates the expression.
     *
     * @param string|int $key  The array key.
     * @param Expression $expr The expression to evaluate for the key.
     */
    public function __construct($key, Expression $expr)
    {
        parent::__construct($expr);

        $this->key = (string) $key;
    }

    /**
     * Returns the array key.
     *
     * @return string|int The array key.
     */
    public function getKey()
    {
        return $this->key;
    }

    /**
     * {@inheritdoc}
     */
    public function evaluate($value)
    {
        if (!is_array($value)) {
            return false;
        }

        if (!array_key_exists($this->key, $value)) {
            return false;
        }

        return $this->expr->evaluate($value[$this->key]);
    }

    /**
     * {@inheritdoc}
     */
    public function equivalentTo(Expression $other)
    {
        if (!parent::equivalentTo($other)) {
            return false;
        }

        /* @var static $other */
        return $this->key === $other->key;
    }

    /**
     * {@inheritdoc}
     */
    public function toString()
    {
        $exprString = $this->expr->toString();

        if ($this->expr instanceof AndX || $this->expr instanceof OrX) {
            return $this->key.'{'.$exprString.'}';
        }

        // Append "functions" with "."
        if (isset($exprString[0]) && ctype_alpha($exprString[0])) {
            return $this->key.'.'.$exprString;
        }

        return $this->key.$exprString;
    }
}


================================================
FILE: src/Selector/Method.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Selector;

use Webmozart\Expression\Expression;
use Webmozart\Expression\Logic\AndX;
use Webmozart\Expression\Logic\OrX;
use Webmozart\Expression\Util\StringUtil;

/**
 * Checks whether the result of a method call matches an expression.
 *
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class Method extends Selector
{
    /**
     * @var string
     */
    private $methodName;

    /**
     * @var array
     */
    private $arguments;

    /**
     * Creates the expression.
     *
     * @param string     $methodName The name of the method to call.
     * @param array      $arguments  The arguments to pass to the method.
     * @param Expression $expr       The expression to evaluate for the result.
     */
    public function __construct($methodName, array $arguments, Expression $expr)
    {
        parent::__construct($expr);

        $this->methodName = $methodName;
        $this->arguments = $arguments;
    }

    /**
     * Returns the method name.
     *
     * @return string The method name.
     */
    public function getMethodName()
    {
        return $this->methodName;
    }

    /**
     * Returns the method arguments.
     *
     * @return array The method arguments.
     */
    public function getArguments()
    {
        return $this->arguments;
    }

    /**
     * {@inheritdoc}
     */
    public function evaluate($value)
    {
        if (!is_object($value)) {
            return false;
        }

        if (!method_exists($value, $this->methodName)) {
            return false;
        }

        return $this->expr->evaluate(call_user_func_array(array($value, $this->methodName), $this->arguments));
    }

    /**
     * {@inheritdoc}
     */
    public function equivalentTo(Expression $other)
    {
        if (!parent::equivalentTo($other)) {
            return false;
        }

        /* @var static $other */
        return $this->methodName === $other->methodName && $this->arguments === $other->arguments;
    }

    /**
     * {@inheritdoc}
     */
    public function toString()
    {
        $exprString = $this->expr->toString();
        $argsString = implode(', ', StringUtil::formatValues($this->arguments));

        if ($this->expr instanceof AndX || $this->expr instanceof OrX) {
            return $this->methodName.'('.$argsString.'){'.$exprString.'}';
        }

        // Append "functions" with "."
        if (isset($exprString[0]) && ctype_alpha($exprString[0])) {
            return $this->methodName.'('.$argsString.').'.$exprString;
        }

        return $this->methodName.'('.$argsString.')'.$exprString;
    }
}


================================================
FILE: src/Selector/Property.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Selector;

use Webmozart\Expression\Expression;
use Webmozart\Expression\Logic\AndX;
use Webmozart\Expression\Logic\OrX;

/**
 * Checks whether the value of a property matches an expression.
 *
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class Property extends Selector
{
    /**
     * @var string
     */
    private $propertyName;

    /**
     * Creates the expression.
     *
     * @param string     $propertyName The name of the property.
     * @param Expression $expr         The expression to evaluate for the result.
     */
    public function __construct($propertyName, Expression $expr)
    {
        parent::__construct($expr);

        $this->propertyName = $propertyName;
    }

    /**
     * Returns the property name.
     *
     * @return string The property name.
     */
    public function getPropertyName()
    {
        return $this->propertyName;
    }

    /**
     * {@inheritdoc}
     */
    public function evaluate($value)
    {
        if (!is_object($value)) {
            return false;
        }

        $propertyName = $this->propertyName;

        if (!property_exists($value, $propertyName)) {
            return false;
        }

        return $this->expr->evaluate($value->$propertyName);
    }

    /**
     * {@inheritdoc}
     */
    public function equivalentTo(Expression $other)
    {
        if (!parent::equivalentTo($other)) {
            return false;
        }

        /* @var static $other */
        return $this->propertyName === $other->propertyName;
    }

    /**
     * {@inheritdoc}
     */
    public function toString()
    {
        $exprString = $this->expr->toString();

        if ($this->expr instanceof AndX || $this->expr instanceof OrX) {
            return $this->propertyName.'{'.$exprString.'}';
        }

        // Append "functions" with "."
        if (isset($exprString[0]) && ctype_alpha($exprString[0])) {
            return $this->propertyName.'.'.$exprString;
        }

        return $this->propertyName.$exprString;
    }
}


================================================
FILE: src/Selector/Selector.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Selector;

use Webmozart\Expression\Expression;
use Webmozart\Expression\Logic\Literal;

/**
 * A logical selector.
 *
 * Evaluates an expression for elements of a structured value.
 *
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
abstract class Selector extends Literal
{
    /**
     * @var Expression
     */
    protected $expr;

    /**
     * Checks whether a value selected from the evaluated value matches an
     * expression.
     *
     * @param Expression $expr The expression to evaluate for the selected value.
     */
    public function __construct(Expression $expr)
    {
        $this->expr = $expr;
    }

    /**
     * Returns the expression that is evaluated for the selected value.
     *
     * @return Expression The inner expression.
     */
    public function getExpression()
    {
        return $this->expr;
    }

    /**
     * {@inheritdoc}
     */
    public function equivalentTo(Expression $other)
    {
        if (get_class($this) !== get_class($other)) {
            return false;
        }

        /* @var Selector $other */
        return $this->expr->equivalentTo($other->expr);
    }
}


================================================
FILE: src/Traversal/ExpressionTraverser.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Traversal;

use Webmozart\Expression\Expression;
use Webmozart\Expression\Logic\AndX;
use Webmozart\Expression\Logic\Not;
use Webmozart\Expression\Logic\OrX;
use Webmozart\Expression\Selector\Key;

/**
 * Traverses {@link Expression} instances.
 *
 * You can attach {@link ExpressionVisitor} instances to the traverse which
 * will be invoked for every node of the expression tree.
 *
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class ExpressionTraverser
{
    /**
     * @var ExpressionVisitor[]
     */
    private $visitors = array();

    /**
     * Adds a visitor to the traverser.
     *
     * The visitors are invoked in the order in which they are added.
     *
     * @param ExpressionVisitor $visitor The visitor to add.
     */
    public function addVisitor(ExpressionVisitor $visitor)
    {
        $this->visitors[] = $visitor;
    }

    /**
     * Removes a visitor from the traverser.
     *
     * If the visitor was added multiple times, all instances are removed.
     *
     * @param ExpressionVisitor $visitor The visitor to remove.
     */
    public function removeVisitor(ExpressionVisitor $visitor)
    {
        while (false !== ($key = array_search($visitor, $this->visitors, true))) {
            unset($this->visitors[$key]);
        }

        $this->visitors = array_values($this->visitors);
    }

    /**
     * Returns the visitors of the traverser.
     *
     * @return ExpressionVisitor[] The visitors.
     */
    public function getVisitors()
    {
        return $this->visitors;
    }

    /**
     * Traverses an expression.
     *
     * @param Expression $expr The expression to traverse.
     *
     * @return Expression The modified expression. May be `null` if the
     *                    expression was removed entirely.
     */
    public function traverse(Expression $expr)
    {
        // Do one full traversal per visitor. If any of the visitors removes
        // the expression entirely, subsequent visitors are not invoked.
        foreach ($this->visitors as $visitor) {
            $expr = $this->traverseForVisitor($expr, $visitor);

            if (!$expr) {
                return null;
            }
        }

        return $expr;
    }

    private function traverseForVisitor(Expression $expr, ExpressionVisitor $visitor)
    {
        $expr = $visitor->enterExpression($expr);

        if ($expr instanceof Key) {
            $expr = $this->traverseKey($expr);
        } elseif ($expr instanceof Not) {
            $expr = $this->traverseNot($expr);
        } elseif ($expr instanceof AndX) {
            $expr = $this->traverseConjunction($expr);
        } elseif ($expr instanceof OrX) {
            $expr = $this->traverseDisjunction($expr);
        }

        if ($expr) {
            $expr = $visitor->leaveExpression($expr);
        }

        return $expr;
    }

    private function traverseKey(Key $expr)
    {
        $innerExpr1 = $expr->getExpression();
        $innerExpr2 = $this->traverse($innerExpr1);

        if ($innerExpr1 === $innerExpr2) {
            return $expr;
        }

        return $innerExpr2 ? new Key($expr->getKey(), $innerExpr2) : null;
    }

    private function traverseNot(Not $expr)
    {
        $negatedExpr1 = $expr->getNegatedExpression();
        $negatedExpr2 = $this->traverse($negatedExpr1);

        if ($negatedExpr1 === $negatedExpr2) {
            return $expr;
        }

        return $negatedExpr2 ? new Not($negatedExpr2) : null;
    }

    private function traverseConjunction(AndX $expr)
    {
        $conjuncts1 = $expr->getConjuncts();
        $conjuncts2 = array();

        foreach ($conjuncts1 as $conjunct) {
            if ($conjunct = $this->traverse($conjunct)) {
                $conjuncts2[] = $conjunct;
            }
        }

        if ($conjuncts1 === $conjuncts2) {
            return $expr;
        }

        return $conjuncts2 ? new AndX($conjuncts2) : null;
    }

    private function traverseDisjunction(OrX $expr)
    {
        $disjuncts1 = $expr->getDisjuncts();
        $disjuncts2 = array();

        foreach ($disjuncts1 as $disjunct) {
            if ($disjunct = $this->traverse($disjunct)) {
                $disjuncts2[] = $disjunct;
            }
        }

        if ($disjuncts1 === $disjuncts2) {
            return $expr;
        }

        return $disjuncts2 ? new OrX($disjuncts2) : null;
    }
}


================================================
FILE: src/Traversal/ExpressionVisitor.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Traversal;

use Webmozart\Expression\Expression;

/**
 * Visits the nodes of an {@link Expression} tree.
 *
 * The visitor needs to be attached to a {@link ExpressionTraverser}. The
 * traverser invokes the visitor for every node of the expression tree.
 *
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
interface ExpressionVisitor
{
    /**
     * Called when the traverser enters an expression.
     *
     * @param Expression $expr The expression.
     *
     * @return Expression The modified expression.
     */
    public function enterExpression(Expression $expr);

    /**
     * Called when the traverser leaves an expression.
     *
     * @param Expression $expr The expression.
     *
     * @return Expression|null The modified expression or `null` if the
     *                         expression should be removed from the tree.
     */
    public function leaveExpression(Expression $expr);
}


================================================
FILE: src/Util/StringUtil.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Util;

/**
 * Contains string utility methods.
 *
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class StringUtil
{
    /**
     * Formats a value as string.
     *
     * @param mixed $value The value.
     *
     * @return string The value as string.
     */
    public static function formatValue($value)
    {
        if (null === $value) {
            return 'null';
        }

        if (true === $value) {
            return 'true';
        }

        if (false === $value) {
            return 'false';
        }

        if (is_string($value)) {
            return '"'.$value.'"';
        }

        if (is_object($value)) {
            return 'object';
        }

        if (is_array($value)) {
            return 'array';
        }

        return (string) $value;
    }

    /**
     * Formats a list of values as strings.
     *
     * @param array $values The values.
     *
     * @return array The values as strings.
     */
    public static function formatValues(array $values)
    {
        foreach ($values as $key => $value) {
            $values[$key] = self::formatValue($value);
        }

        return $values;
    }

    /**
     * May not be instantiated.
     */
    private function __construct()
    {
    }
}


================================================
FILE: tests/Comparison/ContainsTest.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Tests\Comparison;

use PHPUnit_Framework_TestCase;
use Webmozart\Expression\Constraint\Contains;

/**
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 * @author Stephan Wentz <stephan@wentz.it>
 */
class ContainsTest extends PHPUnit_Framework_TestCase
{
    public function testEvaluate()
    {
        $expr = new Contains('test');

        $this->assertTrue($expr->evaluate('testString'));
        $this->assertTrue($expr->evaluate('anothertest'));
        $this->assertTrue($expr->evaluate('testtest'));
        $this->assertTrue($expr->evaluate('test'));
        $this->assertFalse($expr->evaluate('xest'));
        $this->assertFalse($expr->evaluate('est'));
        $this->assertFalse($expr->evaluate('tesx'));
    }

    public function testToString()
    {
        $expr = new Contains('testString');

        $this->assertSame('contains("testString")', $expr->toString());
    }
}


================================================
FILE: tests/Comparison/EndsWithTest.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Tests\Comparison;

use PHPUnit_Framework_TestCase;
use Webmozart\Expression\Constraint\EndsWith;

/**
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class EndsWithTest extends PHPUnit_Framework_TestCase
{
    public function testEvaluate()
    {
        $expr = new EndsWith('.css');

        $this->assertTrue($expr->evaluate('style.css'));
        $this->assertFalse($expr->evaluate('style.css.dist'));
    }

    public function testToString()
    {
        $expr = new EndsWith('.css');

        $this->assertSame('endsWith(".css")', $expr->toString());
    }
}


================================================
FILE: tests/Comparison/EqualsTest.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Tests\Comparison;

use PHPUnit_Framework_TestCase;
use Webmozart\Expression\Constraint\Equals;

/**
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class EqualsTest extends PHPUnit_Framework_TestCase
{
    public function testEvaluate()
    {
        $expr = new Equals('10');

        $this->assertTrue($expr->evaluate('10'));
        $this->assertTrue($expr->evaluate(10));
        $this->assertTrue($expr->evaluate(10.0));
        $this->assertFalse($expr->evaluate('100'));
        $this->assertFalse($expr->evaluate(11));
    }

    public function testToString()
    {
        $expr = new Equals('10');

        $this->assertSame('=="10"', $expr->toString());
    }
}


================================================
FILE: tests/Comparison/GreaterThanEqualTest.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information => please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Tests\Comparison;

use PHPUnit_Framework_TestCase;
use Webmozart\Expression\Constraint\GreaterThanEqual;

/**
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class GreaterThanEqualTest extends PHPUnit_Framework_TestCase
{
    public function testEvaluate()
    {
        $expr = new GreaterThanEqual(10);

        $this->assertTrue($expr->evaluate(11));
        $this->assertTrue($expr->evaluate(11.0));
        $this->assertTrue($expr->evaluate('11'));
        $this->assertTrue($expr->evaluate(10));
        $this->assertFalse($expr->evaluate(9));
    }

    public function testToString()
    {
        $expr = new GreaterThanEqual(10);

        $this->assertSame('>=10', $expr->toString());
    }
}


================================================
FILE: tests/Comparison/GreaterThanTest.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information => please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Tests\Comparison;

use PHPUnit_Framework_TestCase;
use Webmozart\Expression\Constraint\GreaterThan;

/**
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class GreaterThanTest extends PHPUnit_Framework_TestCase
{
    public function testEvaluate()
    {
        $expr = new GreaterThan(10);

        $this->assertTrue($expr->evaluate(11));
        $this->assertTrue($expr->evaluate(11.0));
        $this->assertTrue($expr->evaluate('11'));
        $this->assertFalse($expr->evaluate(10));
        $this->assertFalse($expr->evaluate(9));
    }

    public function testToString()
    {
        $expr = new GreaterThan(10);

        $this->assertSame('>10', $expr->toString());
    }
}


================================================
FILE: tests/Comparison/InTest.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Tests\Comparison;

use PHPUnit_Framework_TestCase;
use Webmozart\Expression\Constraint\In;

/**
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class InTest extends PHPUnit_Framework_TestCase
{
    public function testEvaluateStrict()
    {
        $expr = new In(array('1', '2', '3'));

        $this->assertTrue($expr->evaluate('1'));
        $this->assertFalse($expr->evaluate(1));
        $this->assertFalse($expr->evaluate(1.0));
        $this->assertFalse($expr->evaluate(0));
        $this->assertFalse($expr->evaluate(10));
        $this->assertFalse($expr->evaluate(null));
    }

    public function testEvaluateNonStrict()
    {
        $expr = new In(array('1', '2', '3'), false);

        $this->assertTrue($expr->evaluate('1'));
        $this->assertTrue($expr->evaluate(1));
        $this->assertTrue($expr->evaluate(1.0));
        $this->assertFalse($expr->evaluate(0));
        $this->assertFalse($expr->evaluate(10));
        $this->assertFalse($expr->evaluate(null));
    }

    public function testToString()
    {
        $expr = new In(array('1', '2', '3'), false);

        $this->assertSame('in("1", "2", "3")', $expr->toString());
    }
}


================================================
FILE: tests/Comparison/IsEmptyTest.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Tests\Comparison;

use PHPUnit_Framework_TestCase;
use Webmozart\Expression\Constraint\IsEmpty;

/**
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class IsEmptyTest extends PHPUnit_Framework_TestCase
{
    public function testEvaluate()
    {
        $expr = new IsEmpty();

        $this->assertTrue($expr->evaluate(null));
        $this->assertTrue($expr->evaluate(0));
        $this->assertTrue($expr->evaluate(''));
        $this->assertTrue($expr->evaluate(false));
        $this->assertFalse($expr->evaluate(true));
        $this->assertFalse($expr->evaluate('abcd'));
    }

    public function testToString()
    {
        $expr = new IsEmpty();

        $this->assertSame('empty()', $expr->toString());
    }
}


================================================
FILE: tests/Comparison/IsInstanceOfTest.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Tests\Comparison;

use DirectoryIterator;
use PHPUnit_Framework_TestCase;
use SplFileInfo;
use Webmozart\Expression\Constraint\IsInstanceOf;

/**
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class IsInstanceOfTest extends PHPUnit_Framework_TestCase
{
    public function testEvaluate()
    {
        $expr = new IsInstanceOf('SplFileInfo');

        $this->assertTrue($expr->evaluate(new SplFileInfo(__DIR__)));
        $this->assertTrue($expr->evaluate(new DirectoryIterator(__DIR__)));
        $this->assertFalse($expr->evaluate((object) array()));
        $this->assertFalse($expr->evaluate(array()));
        $this->assertFalse($expr->evaluate('foobar'));
    }

    public function testToString()
    {
        $expr = new IsInstanceOf('SplFileInfo');

        $this->assertSame('instanceOf(SplFileInfo)', $expr->toString());
    }
}


================================================
FILE: tests/Comparison/KeyExistsTest.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Tests\Comparison;

use PHPUnit_Framework_TestCase;
use Webmozart\Expression\Constraint\KeyExists;

/**
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class KeyExistsTest extends PHPUnit_Framework_TestCase
{
    public function testEvaluate()
    {
        $expr = new KeyExists('key');

        $this->assertTrue($expr->evaluate(array('key' => 11)));
        $this->assertFalse($expr->evaluate(array()));
        $this->assertFalse($expr->evaluate('foobar'));
    }

    public function testToString()
    {
        $expr = new KeyExists('key');

        $this->assertSame('keyExists("key")', $expr->toString());
    }
}


================================================
FILE: tests/Comparison/KeyNotExistsTest.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Tests\Comparison;

use PHPUnit_Framework_TestCase;
use Webmozart\Expression\Constraint\KeyNotExists;

/**
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class KeyNotExistsTest extends PHPUnit_Framework_TestCase
{
    public function testEvaluate()
    {
        $expr = new KeyNotExists('key');

        $this->assertTrue($expr->evaluate(array()));
        $this->assertFalse($expr->evaluate(array('key' => 11)));
        $this->assertFalse($expr->evaluate('foobar'));
    }

    public function testToString()
    {
        $expr = new KeyNotExists('key');

        $this->assertSame('keyNotExists("key")', $expr->toString());
    }
}


================================================
FILE: tests/Comparison/LessThanEqualTest.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information => please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Tests\Comparison;

use PHPUnit_Framework_TestCase;
use Webmozart\Expression\Constraint\LessThanEqual;

/**
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class LessThanEqualTest extends PHPUnit_Framework_TestCase
{
    public function testEvaluate()
    {
        $expr = new LessThanEqual(10);

        $this->assertTrue($expr->evaluate(9));
        $this->assertTrue($expr->evaluate(9.0));
        $this->assertTrue($expr->evaluate('9'));
        $this->assertTrue($expr->evaluate(10));
        $this->assertFalse($expr->evaluate(11));
    }

    public function testToString()
    {
        $expr = new LessThanEqual(10);

        $this->assertSame('<=10', $expr->toString());
    }
}


================================================
FILE: tests/Comparison/LessThanTest.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information => please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Tests\Comparison;

use PHPUnit_Framework_TestCase;
use Webmozart\Expression\Constraint\LessThan;

/**
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class LessThanTest extends PHPUnit_Framework_TestCase
{
    public function testEvaluate()
    {
        $expr = new LessThan(10);

        $this->assertTrue($expr->evaluate(9));
        $this->assertTrue($expr->evaluate(9.0));
        $this->assertTrue($expr->evaluate('9'));
        $this->assertFalse($expr->evaluate(10));
        $this->assertFalse($expr->evaluate(11));
    }

    public function testToString()
    {
        $expr = new LessThan(10);

        $this->assertSame('<10', $expr->toString());
    }
}


================================================
FILE: tests/Comparison/MatchesTest.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Tests\Comparison;

use PHPUnit_Framework_TestCase;
use Webmozart\Expression\Constraint\Matches;

/**
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class MatchesTest extends PHPUnit_Framework_TestCase
{
    public function testEvaluate()
    {
        $expr = new Matches('~^\d{4}$~');

        $this->assertTrue($expr->evaluate('1010'));
        $this->assertTrue($expr->evaluate(1010));
        $this->assertFalse($expr->evaluate('abcd'));
        $this->assertFalse($expr->evaluate('10101'));
    }

    public function testToString()
    {
        $expr = new Matches('~^\d{4}$~');

        $this->assertSame('matches("~^\d{4}$~")', $expr->toString());
    }
}


================================================
FILE: tests/Comparison/NotEqualsTest.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Tests\Comparison;

use PHPUnit_Framework_TestCase;
use Webmozart\Expression\Constraint\NotEquals;

/**
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class NotEqualsTest extends PHPUnit_Framework_TestCase
{
    public function testEvaluate()
    {
        $expr = new NotEquals('10');

        $this->assertTrue($expr->evaluate('100'));
        $this->assertTrue($expr->evaluate(11));
        $this->assertFalse($expr->evaluate('10'));
        $this->assertFalse($expr->evaluate(10));
        $this->assertFalse($expr->evaluate(10.0));
    }

    public function testToString()
    {
        $expr = new NotEquals('10');

        $this->assertSame('!="10"', $expr->toString());
    }
}


================================================
FILE: tests/Comparison/NotSameTest.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Tests\Comparison;

use PHPUnit_Framework_TestCase;
use Webmozart\Expression\Constraint\NotSame;

/**
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class NotSameTest extends PHPUnit_Framework_TestCase
{
    public function testEvaluate()
    {
        $expr = new NotSame('10');

        $this->assertTrue($expr->evaluate('100'));
        $this->assertTrue($expr->evaluate(11));
        $this->assertTrue($expr->evaluate(10));
        $this->assertTrue($expr->evaluate(10.0));
        $this->assertFalse($expr->evaluate('10'));
    }

    public function testToString()
    {
        $expr = new NotSame('10');

        $this->assertSame('!=="10"', $expr->toString());
    }
}


================================================
FILE: tests/Comparison/SameTest.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Tests\Comparison;

use PHPUnit_Framework_TestCase;
use Webmozart\Expression\Constraint\Same;

/**
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class SameTest extends PHPUnit_Framework_TestCase
{
    public function testEvaluate()
    {
        $expr = new Same('10');

        $this->assertTrue($expr->evaluate('10'));
        $this->assertFalse($expr->evaluate('100'));
        $this->assertFalse($expr->evaluate(11));
        $this->assertFalse($expr->evaluate(10));
        $this->assertFalse($expr->evaluate(10.0));
    }

    public function testToString()
    {
        $expr = new Same('10');

        $this->assertSame('==="10"', $expr->toString());
    }
}


================================================
FILE: tests/Comparison/StartsWithTest.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Tests\Comparison;

use PHPUnit_Framework_TestCase;
use Webmozart\Expression\Constraint\StartsWith;

/**
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class StartsWithTest extends PHPUnit_Framework_TestCase
{
    public function testEvaluate()
    {
        $expr = new StartsWith('Thomas');

        $this->assertTrue($expr->evaluate('Thomas Edison'));
        $this->assertFalse($expr->evaluate('Mr. Thomas Edison'));
    }

    public function testToString()
    {
        $expr = new StartsWith('Thomas');

        $this->assertSame('startsWith("Thomas")', $expr->toString());
    }
}


================================================
FILE: tests/DomainExpressionsTest.php
================================================
<?php

/*
 * This file is part of the vendor/project package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Tests;

use PHPUnit_Framework_TestCase;
use Webmozart\Expression\Expr;
use Webmozart\Expression\Tests\Fixtures\Customer;
use Webmozart\Expression\Tests\Fixtures\HasPreviousBookings;
use Webmozart\Expression\Tests\Fixtures\IsPremium;

/**
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class DomainExpressionsTest extends PHPUnit_Framework_TestCase
{
    public function testDomainExpressions()
    {
        $c1 = new Customer();
        $c1->setPremium(true);
        $c2 = new Customer();
        $c2->setBookings(array('booking1', 'booking2'));
        $c3 = new Customer();
        $c3->setPremium(true);
        $c3->setBookings(array('booking1'));

        $customers = array($c1, $c2, $c3);

        $this->assertEquals(array($c1, 2 => $c3), Expr::filter($customers, new IsPremium()));
        $this->assertEquals(array(1 => $c2, 2 => $c3), Expr::filter($customers, new HasPreviousBookings()));
        $this->assertEquals(array(2 => $c3), Expr::filter($customers, Expr::andX(array(
            new HasPreviousBookings(),
            new IsPremium(),
        ))));
    }
}


================================================
FILE: tests/EquivalenceTest.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Tests;

use PHPUnit_Framework_TestCase;
use Webmozart\Expression\Constraint\Contains;
use Webmozart\Expression\Constraint\EndsWith;
use Webmozart\Expression\Constraint\Equals;
use Webmozart\Expression\Constraint\GreaterThan;
use Webmozart\Expression\Constraint\GreaterThanEqual;
use Webmozart\Expression\Constraint\In;
use Webmozart\Expression\Constraint\IsEmpty;
use Webmozart\Expression\Constraint\IsInstanceOf;
use Webmozart\Expression\Constraint\KeyExists;
use Webmozart\Expression\Constraint\KeyNotExists;
use Webmozart\Expression\Constraint\LessThan;
use Webmozart\Expression\Constraint\LessThanEqual;
use Webmozart\Expression\Constraint\Matches;
use Webmozart\Expression\Constraint\NotEquals;
use Webmozart\Expression\Constraint\NotSame;
use Webmozart\Expression\Constraint\Same;
use Webmozart\Expression\Constraint\StartsWith;
use Webmozart\Expression\Expression;
use Webmozart\Expression\Logic\AlwaysFalse;
use Webmozart\Expression\Logic\AlwaysTrue;
use Webmozart\Expression\Logic\Not;
use Webmozart\Expression\Selector\All;
use Webmozart\Expression\Selector\AtLeast;
use Webmozart\Expression\Selector\AtMost;
use Webmozart\Expression\Selector\Count;
use Webmozart\Expression\Selector\Exactly;
use Webmozart\Expression\Selector\Key;
use Webmozart\Expression\Selector\Method;
use Webmozart\Expression\Selector\Property;

/**
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class EquivalenceTest extends PHPUnit_Framework_TestCase
{
    public function getEquivalentCriteria()
    {
        return array(
            array(new Same('10'), new Same('10')),
            array(new Same('10'), new In(array('10'), true)),

            array(new NotSame('10'), new NotSame('10')),

            array(new Equals('10'), new Equals('10')),
            array(new Equals('10'), new Equals(10)),
            array(new Equals('10'), new In(array('10'), false)),
            array(new Equals('10'), new In(array(10), false)),

            array(new NotEquals('10'), new NotEquals('10')),
            array(new NotEquals('10'), new NotEquals(10)),

            array(new GreaterThan('10'), new GreaterThan('10')),
            array(new GreaterThan('10'), new GreaterThan(10)),

            array(new GreaterThanEqual('10'), new GreaterThanEqual('10')),
            array(new GreaterThanEqual('10'), new GreaterThanEqual(10)),

            array(new LessThan('10'), new LessThan('10')),
            array(new LessThan('10'), new LessThan(10)),

            array(new LessThanEqual('10'), new LessThanEqual('10')),
            array(new LessThanEqual('10'), new LessThanEqual(10)),

            array(new IsEmpty(), new IsEmpty()),

            array(new IsInstanceOf('SplFileInfo'), new IsInstanceOf('SplFileInfo')),

            array(new KeyExists('10'), new KeyExists('10')),
            array(new KeyExists('10'), new KeyExists(10)),

            array(new KeyNotExists('10'), new KeyNotExists('10')),
            array(new KeyNotExists('10'), new KeyNotExists(10)),

            array(new Matches('foo.*'), new Matches('foo.*')),

            array(new In(array('10'), false), new In(array('10'), false)),
            array(new In(array('10'), false), new In(array(10), false)),
            array(new In(array('10'), true), new In(array('10'), true)),

            array(new StartsWith('10'), new StartsWith('10')),
            array(new StartsWith('10'), new StartsWith(10)),

            array(new EndsWith('10'), new EndsWith('10')),
            array(new EndsWith('10'), new EndsWith(10)),

            array(new Contains('10'), new Contains('10')),
            array(new Contains('10'), new Contains(10)),

            array(new Not(new Same('10')), new Not(new Same('10'))),

            array(new Key('key', new Same('10')), new Key('key', new Same('10'))),
            array(new Key('42', new Same('10')), new Key(42, new Same('10'))),

            array(new Property('prop', new Same('10')), new Property('prop', new Same('10'))),

            array(new Method('getFoo', array(), new Same('10')), new Method('getFoo', array(), new Same('10'))),
            array(new Method('getFoo', array(42), new Same('10')), new Method('getFoo', array(42), new Same('10'))),

            array(new AtLeast(1, new Same('10')), new AtLeast(1, new Same('10'))),

            array(new AtMost(1, new Same('10')), new AtMost(1, new Same('10'))),

            array(new Exactly(1, new Same('10')), new Exactly(1, new Same('10'))),

            array(new All(new Same('10')), new All(new Same('10'))),

            array(new Count(new Same(10)), new Count(new Same(10))),

            array(new AlwaysTrue(), new AlwaysTrue()),

            array(new AlwaysFalse(), new AlwaysFalse()),
        );
    }

    /**
     * @dataProvider getEquivalentCriteria
     */
    public function testEquivalence(Expression $left, Expression $right)
    {
        $this->assertTrue($left->equivalentTo($right));
        $this->assertTrue($right->equivalentTo($left));
    }

    public function getNonEquivalentCriteria()
    {
        return array(
            array(new Same('10'), new Same('11')),
            array(new Same('10'), new Same(10)),
            array(new Same('10'), new Equals('10')),

            array(new Same('10'), new In(array(10), true)),
            array(new Same('10'), new In(array('10'), false)),
            array(new Same('10'), new In(array(), true)),
            array(new Same('10'), new In(array('10', '11'), true)),

            array(new NotSame('10'), new NotSame('11')),
            array(new NotSame('10'), new NotSame(10)),
            array(new NotSame('10'), new NotEquals('10')),

            array(new Equals('10'), new Equals('11')),
            array(new Equals('10'), new In(array('10'), true)),
            array(new Equals('10'), new In(array(), false)),
            array(new Equals('10'), new In(array('10', '11'), false)),

            array(new GreaterThan('10'), new GreaterThan('11')),
            array(new GreaterThan('10'), new LessThan('10')),

            array(new GreaterThanEqual('10'), new GreaterThanEqual('11')),
            array(new GreaterThanEqual('10'), new LessThan('10')),

            array(new LessThan('10'), new LessThan('11')),
            array(new LessThan('10'), new GreaterThan('10')),

            array(new LessThanEqual('10'), new LessThanEqual('11')),
            array(new LessThanEqual('10'), new GreaterThan('10')),

            array(new IsInstanceOf('SplFileInfo'), new IsInstanceOf('DateTime')),

            array(new KeyExists('10'), new KeyExists('11')),
            array(new KeyExists('foo'), new KeyExists(0)),
            array(new KeyExists('10'), new Equals('10')),

            array(new KeyNotExists('10'), new KeyNotExists('11')),
            array(new KeyNotExists('foo'), new KeyNotExists(0)),
            array(new KeyNotExists('10'), new Equals('10')),

            array(new Matches('10'), new Matches(10)),
            array(new Matches('10'), new Equals('10')),

            array(new In(array('10'), true), new In(array('11'), true)),
            array(new In(array('10'), true), new In(array(10), true)),
            array(new In(array('10'), true), new In(array('11'), false)),
            array(new In(array('10'), true), new IsEmpty()),

            array(new StartsWith('10'), new StartsWith('11')),
            array(new StartsWith('foo'), new StartsWith(0)),
            array(new StartsWith('10'), new Equals('10')),

            array(new EndsWith('10'), new EndsWith('11')),
            array(new EndsWith('foo'), new EndsWith(0)),
            array(new EndsWith('10'), new Equals('10')),

            array(new Contains('10'), new Contains('11')),
            array(new Contains('foo'), new Contains(0)),
            array(new Contains('10'), new Equals('10')),

            array(new Not(new Same('10')), new Not(new Same(10))),
            array(new Not(new Same('10')), new Same(10)),

            array(new Key('foo', new Same('10')), new Key('bar', new Same('10'))),
            array(new Key('foo', new Same('10')), new Key(0, new Same('10'))),
            array(new Key('foo', new Same('10')), new Key('foo', new Same(10))),
            array(new Key('foo', new Same('10')), new Same('10')),

            array(new Property('foo', new Same('10')), new Property('bar', new Same('10'))),
            array(new Property('foo', new Same('10')), new Property('foo', new Same(10))),
            array(new Property('foo', new Same('10')), new Same('10')),

            array(new Method('getFoo', array(42), new Same('10')), new Method('getFoo', array('42'), new Same('10'))),
            array(new Method('getFoo', array(42), new Same('10')), new Method('getFoo', array(42, true), new Same('10'))),
            array(new Method('getFoo', array(), new Same('10')), new Method('getBar', array(), new Same('10'))),
            array(new Method('getFoo', array(), new Same('10')), new Method('getFoo', array(), new Same(10))),
            array(new Method('getFoo', array(), new Same('10')), new Same('10')),

            array(new AtLeast(1, new Same('10')), new AtLeast(2, new Same('10'))),
            array(new AtLeast(1, new Same('10')), new AtLeast(1, new Same(10))),
            array(new AtLeast(1, new Same('10')), new Same('10')),

            array(new AtMost(1, new Same('10')), new AtMost(2, new Same('10'))),
            array(new AtMost(1, new Same('10')), new AtMost(1, new Same(10))),
            array(new AtMost(1, new Same('10')), new Same('10')),

            array(new Exactly(1, new Same('10')), new Exactly(2, new Same('10'))),
            array(new Exactly(1, new Same('10')), new Exactly(1, new Same(10))),
            array(new Exactly(1, new Same('10')), new Same('10')),

            array(new All(new Same('10')), new All(new Same(10))),
            array(new All(new Same('10')), new Same('10')),

            array(new Count(new Same('10')), new Count(new Same(10))),
            array(new Count(new Same('10')), new Same('10')),

            array(new AlwaysTrue(), new Same(10)),

            array(new AlwaysFalse(), new Same(10)),
        );
    }

    /**
     * @dataProvider getNonEquivalentCriteria
     */
    public function testNonEquivalence(Expression $left, Expression $right)
    {
        $this->assertFalse($left->equivalentTo($right));
        $this->assertFalse($right->equivalentTo($left));
    }
}


================================================
FILE: tests/ExprTest.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Tests;

use ArrayObject;
use PHPUnit_Framework_TestCase;
use Webmozart\Expression\Constraint\Contains;
use Webmozart\Expression\Constraint\EndsWith;
use Webmozart\Expression\Constraint\Equals;
use Webmozart\Expression\Constraint\GreaterThan;
use Webmozart\Expression\Constraint\GreaterThanEqual;
use Webmozart\Expression\Constraint\In;
use Webmozart\Expression\Constraint\IsEmpty;
use Webmozart\Expression\Constraint\IsInstanceOf;
use Webmozart\Expression\Constraint\KeyExists;
use Webmozart\Expression\Constraint\KeyNotExists;
use Webmozart\Expression\Constraint\LessThan;
use Webmozart\Expression\Constraint\LessThanEqual;
use Webmozart\Expression\Constraint\Matches;
use Webmozart\Expression\Constraint\NotEquals;
use Webmozart\Expression\Constraint\NotSame;
use Webmozart\Expression\Constraint\Same;
use Webmozart\Expression\Constraint\StartsWith;
use Webmozart\Expression\Expr;
use Webmozart\Expression\Logic\AlwaysFalse;
use Webmozart\Expression\Logic\AlwaysTrue;
use Webmozart\Expression\Logic\AndX;
use Webmozart\Expression\Logic\Not;
use Webmozart\Expression\Logic\OrX;
use Webmozart\Expression\Selector\All;
use Webmozart\Expression\Selector\AtLeast;
use Webmozart\Expression\Selector\AtMost;
use Webmozart\Expression\Selector\Count;
use Webmozart\Expression\Selector\Exactly;
use Webmozart\Expression\Selector\Key;
use Webmozart\Expression\Selector\Method;
use Webmozart\Expression\Selector\Property;

/**
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class ExprTest extends PHPUnit_Framework_TestCase
{
    public static function getComparisons()
    {
        return array(
            array(
                'keyExists',
                array('key'),
                new KeyExists('key'),
            ),
            array(
                'keyNotExists',
                array('key'),
                new KeyNotExists('key'),
            ),
            array(
                'null',
                array(),
                new Same(null),
            ),
            array(
                'notNull',
                array(),
                new NotSame(null),
            ),
            array(
                'isEmpty',
                array(),
                new IsEmpty(),
            ),
            array(
                'isInstanceOf',
                array('DateTime'),
                new IsInstanceOf('DateTime'),
            ),
            array(
                'notEmpty',
                array(),
                new Not(new IsEmpty()),
            ),
            array(
                'equals',
                array(10),
                new Equals(10),
            ),
            array(
                'notEquals',
                array(10),
                new NotEquals(10),
            ),
            array(
                'same',
                array(10),
                new Same(10),
            ),
            array(
                'notSame',
                array(10),
                new NotSame(10),
            ),
            array(
                'greaterThan',
                array(10),
                new GreaterThan(10),
            ),
            array(
                'greaterThanEqual',
                array(10),
                new GreaterThanEqual(10),
            ),
            array(
                'lessThan',
                array(10),
                new LessThan(10),
            ),
            array(
                'lessThanEqual',
                array(10),
                new LessThanEqual(10),
            ),
            array(
                'matches',
                array('~^\d{4}$~'),
                new Matches('~^\d{4}$~'),
            ),
            array(
                'startsWith',
                array('Thomas'),
                new StartsWith('Thomas'),
            ),
            array(
                'endsWith',
                array('.css'),
                new EndsWith('.css'),
            ),
            array(
                'contains',
                array('css'),
                new Contains('css'),
            ),
            array(
                'in',
                array(array('1', '2', '3')),
                new In(array('1', '2', '3')),
            ),
        );
    }

    public static function getMethodTests()
    {
        $expr = new Same('10');

        return array_merge(array(
            array(
                'not',
                array($expr),
                new Not($expr),
            ),
            array(
                'key',
                array('key', $expr),
                new Key('key', $expr),
            ),
            array(
                'method',
                array('getFoo', $expr),
                new Method('getFoo', array(), $expr),
            ),
            array(
                'method',
                array('getFoo', 42, 'bar', $expr),
                new Method('getFoo', array(42, 'bar'), $expr),
            ),
            array(
                'property',
                array('prop', $expr),
                new Property('prop', $expr),
            ),
            array(
                'atLeast',
                array(2, $expr),
                new AtLeast(2, $expr),
            ),
            array(
                'atMost',
                array(2, $expr),
                new AtMost(2, $expr),
            ),
            array(
                'exactly',
                array(2, $expr),
                new Exactly(2, $expr),
            ),
            array(
                'all',
                array($expr),
                new All($expr),
            ),
            array(
                'count',
                array($expr),
                new Count($expr),
            ),
            array(
                'true',
                array(),
                new AlwaysTrue(),
            ),
            array(
                'false',
                array(),
                new AlwaysFalse(),
            ),
        ), self::getComparisons());
    }

    /**
     * @dataProvider getMethodTests
     */
    public function testCreate($method, $args, $expected)
    {
        $this->assertEquals($expected, call_user_func_array(array('Webmozart\Expression\Expr', $method), $args));
    }

    public function testExpr()
    {
        $expr = new Same(true);

        $this->assertSame($expr, Expr::expr($expr));
    }

    public function testAndX()
    {
        $andX = new AndX(array(new GreaterThan(5), new LessThan(10)));

        $this->assertEquals($andX, Expr::andX(array(Expr::greaterThan(5), Expr::lessThan(10))));
    }

    public function testOrX()
    {
        $andX = new OrX(array(new LessThan(5), new GreaterThan(10)));

        $this->assertEquals($andX, Expr::orX(array(Expr::lessThan(5), Expr::greaterThan(10))));
    }

    public function testFilterArray()
    {
        $input = range(1, 10);
        $output = array_filter($input, function ($i) {
            return $i > 4;
        });

        $this->assertSame($output, Expr::filter($input, Expr::greaterThan(4)));
    }

    public function testFilterCollection()
    {
        $input = new ArrayObject(range(1, 10));
        $output = new ArrayObject(array_filter(range(1, 10), function ($i) {
            return $i > 4;
        }));

        $this->assertEquals($output, Expr::filter($input, Expr::greaterThan(4)));
    }
}


================================================
FILE: tests/Fixtures/Customer.php
================================================
<?php

/*
 * This file is part of the vendor/project package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Tests\Fixtures;

class Customer
{
    private $premium = false;

    private $bookings = array();

    public function setPremium($premium)
    {
        $this->premium = (bool) $premium;
    }

    public function isPremium()
    {
        return $this->premium;
    }

    public function setBookings(array $bookings)
    {
        $this->bookings = $bookings;
    }

    public function getBookings()
    {
        return $this->bookings;
    }
}


================================================
FILE: tests/Fixtures/HasPreviousBookings.php
================================================
<?php

/*
 * This file is part of the vendor/project package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Tests\Fixtures;

use Webmozart\Expression\Expr;
use Webmozart\Expression\Selector\Method;

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


================================================
FILE: tests/Fixtures/IsPremium.php
================================================
<?php

/*
 * This file is part of the vendor/project package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Tests\Fixtures;

use Webmozart\Expression\Expr;
use Webmozart\Expression\Selector\Method;

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


================================================
FILE: tests/Logic/AlwaysFalseTest.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Tests\Logic;

use PHPUnit_Framework_TestCase;
use stdClass;
use Webmozart\Expression\Constraint\Same;
use Webmozart\Expression\Logic\AlwaysFalse;

/**
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class AlwaysFalseTest extends PHPUnit_Framework_TestCase
{
    public function testEvaluate()
    {
        $expr = new AlwaysFalse();

        $this->assertFalse($expr->evaluate(123));
        $this->assertFalse($expr->evaluate(true));
        $this->assertFalse($expr->evaluate(new stdClass()));
    }

    public function testToString()
    {
        $expr = new AlwaysFalse();

        $this->assertSame('false', $expr->toString());
    }

    public function testAndReturnsFalse()
    {
        $expr = new AlwaysFalse();
        $same = new Same('10');

        $this->assertSame($expr, $expr->andX($same));
    }

    public function testOrReturnsConjunct()
    {
        $expr = new AlwaysFalse();
        $same = new Same('10');

        $this->assertSame($same, $expr->orX($same));
    }
}


================================================
FILE: tests/Logic/AlwaysTrueTest.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Tests\Logic;

use PHPUnit_Framework_TestCase;
use stdClass;
use Webmozart\Expression\Constraint\Same;
use Webmozart\Expression\Logic\AlwaysTrue;

/**
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class AlwaysTrueTest extends PHPUnit_Framework_TestCase
{
    public function testEvaluate()
    {
        $expr = new AlwaysTrue();

        $this->assertTrue($expr->evaluate(123));
        $this->assertTrue($expr->evaluate(true));
        $this->assertTrue($expr->evaluate(new stdClass()));
    }

    public function testToString()
    {
        $expr = new AlwaysTrue();

        $this->assertSame('true', $expr->toString());
    }

    public function testAndReturnsConjunct()
    {
        $expr = new AlwaysTrue();
        $same = new Same('10');

        $this->assertSame($same, $expr->andX($same));
    }

    public function testOrReturnsTrue()
    {
        $expr = new AlwaysTrue();
        $same = new Same('10');

        $this->assertSame($expr, $expr->orX($same));
    }
}


================================================
FILE: tests/Logic/ConjunctionTest.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Tests\Logic;

use PHPUnit_Framework_TestCase;
use Webmozart\Expression\Constraint\Contains;
use Webmozart\Expression\Constraint\EndsWith;
use Webmozart\Expression\Constraint\GreaterThan;
use Webmozart\Expression\Constraint\Same;
use Webmozart\Expression\Expr;
use Webmozart\Expression\Logic\AndX;
use Webmozart\Expression\Logic\OrX;
use Webmozart\Expression\Selector\Key;

/**
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class ConjunctionTest extends PHPUnit_Framework_TestCase
{
    public function testCreate()
    {
        $conjunction = new AndX(array(
            $notNull = new Same('10'),
            $greaterThan = new GreaterThan('age', 0),
        ));

        $this->assertSame(array($notNull, $greaterThan), $conjunction->getConjuncts());
    }

    public function testCreateInlinesConjunction()
    {
        $conjunction = new AndX(array(
            $notNull = new Same('10'),
            new AndX(array($greaterThan = new GreaterThan('age', 0))),
        ));

        $this->assertSame(array($notNull, $greaterThan), $conjunction->getConjuncts());
    }

    public function testAndX()
    {
        $conjunction1 = new AndX(array($notNull = new Same('10')));

        // Expressions are value objects, hence we must not alter the original
        // conjunction
        $conjunction2 = $conjunction1->andX($greaterThan = new GreaterThan('age', 0));

        $this->assertSame(array($notNull), $conjunction1->getConjuncts());
        $this->assertSame(array($notNull, $greaterThan), $conjunction2->getConjuncts());
    }

    public function testAndXIgnoresDuplicates()
    {
        $conjunction1 = new AndX(array($notNull = new Same('10')));
        $conjunction2 = $conjunction1->andX(new Same('10'));

        $this->assertSame($conjunction1, $conjunction2);
    }

    public function testAndXInlinesConjunctions()
    {
        $conjunction1 = new AndX(array($notNull = new Same('10')));
        $conjunction2 = new AndX(array($greaterThan = new GreaterThan('name')));

        $conjunction3 = $conjunction1->andX($conjunction2);

        $this->assertSame(array($notNull), $conjunction1->getConjuncts());
        $this->assertSame(array($greaterThan), $conjunction2->getConjuncts());
        $this->assertSame(array($notNull, $greaterThan), $conjunction3->getConjuncts());
    }

    public function testAndTrueIgnored()
    {
        $conjunction1 = new AndX(array($notNull = new Same('10')));
        $conjunction2 = $conjunction1->andTrue();

        $this->assertSame($conjunction1, $conjunction2);
    }

    public function testAndXIgnoresTrue()
    {
        $conjunction1 = new AndX(array($notNull = new Same('10')));
        $conjunction2 = $conjunction1->andX(Expr::true());

        $this->assertSame($conjunction1, $conjunction2);
    }

    public function testAndFalseReturnsFalse()
    {
        $conjunction1 = new AndX(array($notNull = new Same('10')));
        $conjunction2 = $conjunction1->andFalse();

        $this->assertInstanceOf('Webmozart\Expression\Logic\AlwaysFalse', $conjunction2);
    }

    public function testAndXReturnsFalse()
    {
        $conjunction1 = new AndX(array($notNull = new Same('10')));
        $conjunction2 = $conjunction1->andX($false = Expr::false());

        $this->assertSame($false, $conjunction2);
    }

    /**
     * @dataProvider \Webmozart\Expression\Tests\ExprTest::getMethodTests
     */
    public function testAnd($method, $args, $expected)
    {
        // tested separately
        if ('true' === $method || 'false' === $method) {
            return;
        }

        if ('is' === substr($method, 0, 2)) {
            $method = substr($method, 2);
        }

        $method = 'and'.ucfirst($method);
        $conjunction1 = new AndX();

        $conjunction2 = call_user_func_array(array($conjunction1, $method), $args);

        $this->assertEquals(array(), $conjunction1->getConjuncts());
        $this->assertEquals(array($expected), $conjunction2->getConjuncts());
    }

    public function testEvaluate()
    {
        $conjunction = new AndX(array(
            new Key('name', new Same('Thomas')),
            new Key('age', new GreaterThan(0)),
        ));

        $this->assertTrue($conjunction->evaluate(array('name' => 'Thomas', 'age' => 35)));
        $this->assertFalse($conjunction->evaluate(array('name' => null, 'age' => 35)));
        $this->assertFalse($conjunction->evaluate(array('name' => 'Thomas', 'age' => 0)));
        $this->assertFalse($conjunction->evaluate(array('name' => null, 'age' => 0)));
    }

    public function testEquivalentTo()
    {
        $conjunction1 = new AndX(array(
            new Key('name', new Same('10')),
            new Key('age', new GreaterThan(0)),
        ));

        // conjunctions match independent of the order of the conjuncts
        $conjunction2 = new AndX(array(
            new Key('age', new GreaterThan(0)),
            new Key('name', new Same('10')),
        ));

        $conjunction3 = new AndX(array(
            new Key('age', new GreaterThan(0)),
        ));

        $this->assertTrue($conjunction1->equivalentTo($conjunction2));
        $this->assertTrue($conjunction2->equivalentTo($conjunction1));
        $this->assertFalse($conjunction2->equivalentTo($conjunction3));
        $this->assertFalse($conjunction3->equivalentTo($conjunction2));
        $this->assertFalse($conjunction1->equivalentTo($conjunction3));
        $this->assertFalse($conjunction3->equivalentTo($conjunction1));
    }

    public function testToString()
    {
        $expr1 = new AndX();
        $expr2 = new AndX(array(new GreaterThan(10), new EndsWith('.css')));
        $expr3 = new AndX(array(new GreaterThan(10), new OrX(array(new Contains('foo'), new EndsWith('.css')))));

        $this->assertSame('', $expr1->toString());
        $this->assertSame('>10 && endsWith(".css")', $expr2->toString());
        $this->assertSame('>10 && (contains("foo") || endsWith(".css"))', $expr3->toString());
    }
}


================================================
FILE: tests/Logic/DisjunctionTest.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Tests\Logic;

use PHPUnit_Framework_TestCase;
use Webmozart\Expression\Constraint\Contains;
use Webmozart\Expression\Constraint\EndsWith;
use Webmozart\Expression\Constraint\GreaterThan;
use Webmozart\Expression\Constraint\Same;
use Webmozart\Expression\Logic\AlwaysFalse;
use Webmozart\Expression\Logic\AlwaysTrue;
use Webmozart\Expression\Logic\AndX;
use Webmozart\Expression\Logic\OrX;
use Webmozart\Expression\Selector\Key;

/**
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class DisjunctionTest extends PHPUnit_Framework_TestCase
{
    public function testCreate()
    {
        $disjunction = new OrX(array(
            $notNull = new Same('10'),
            $greaterThan = new GreaterThan('age', 0),
        ));

        $this->assertSame(array($notNull, $greaterThan), $disjunction->getDisjuncts());
    }

    public function testCreateInlinesDisjunctions()
    {
        $disjunction = new OrX(array(
            $notNull = new Same('10'),
            new OrX(array($greaterThan = new GreaterThan('age', 0))),
        ));

        $this->assertSame(array($notNull, $greaterThan), $disjunction->getDisjuncts());
    }

    public function testOrX()
    {
        $disjunction1 = new OrX(array($notNull = new Same('10')));

        // Expressions are value objects, hence we must not alter the original
        // conjunction
        $disjunction2 = $disjunction1->orX($greaterThan = new GreaterThan('age', 0));

        $this->assertSame(array($notNull), $disjunction1->getDisjuncts());
        $this->assertSame(array($notNull, $greaterThan), $disjunction2->getDisjuncts());
    }

    public function testOrXIgnoresDuplicates()
    {
        $disjunction1 = new OrX(array($notNull = new Same('10')));
        $disjunction2 = $disjunction1->orX(new Same('10'));

        $this->assertSame($disjunction1, $disjunction2);
    }

    public function testOrXInlinesDisjunctions()
    {
        $disjunction1 = new OrX(array($notNull = new Same('10')));
        $disjunction2 = new OrX(array($greaterThan = new GreaterThan('age', 0)));

        // Expressions are value objects, hence we must not alter the original
        // conjunction
        $disjunction3 = $disjunction1->orX($disjunction2);

        $this->assertSame(array($notNull), $disjunction1->getDisjuncts());
        $this->assertSame(array($greaterThan), $disjunction2->getDisjuncts());
        $this->assertSame(array($notNull, $greaterThan), $disjunction3->getDisjuncts());
    }

    public function testOrFalseIgnored()
    {
        $disjunction1 = new OrX(array($notNull = new Same('10')));
        $disjunction2 = $disjunction1->orFalse();

        $this->assertSame($disjunction1, $disjunction2);
    }

    public function testOrXIgnoresFalse()
    {
        $disjunction1 = new OrX(array($notNull = new Same('10')));
        $disjunction2 = $disjunction1->orX(new AlwaysFalse());

        $this->assertSame($disjunction1, $disjunction2);
    }

    public function testOrTrueReturnsTrue()
    {
        $disjunction1 = new OrX(array($notNull = new Same('10')));
        $disjunction2 = $disjunction1->orTrue();

        $this->assertInstanceOf('Webmozart\Expression\Logic\AlwaysTrue', $disjunction2);
    }

    public function testOrXReturnsTrue()
    {
        $disjunction1 = new OrX(array($notNull = new Same('10')));
        $disjunction2 = $disjunction1->orX($true = new AlwaysTrue());

        $this->assertSame($true, $disjunction2);
    }

    /**
     * @dataProvider \Webmozart\Expression\Tests\ExprTest::getMethodTests
     */
    public function testOr($method, $args, $expected)
    {
        // tested separately
        if ('true' === $method || 'false' === $method) {
            return;
        }

        if ('is' === substr($method, 0, 2)) {
            $method = substr($method, 2);
        }

        $method = 'or'.ucfirst($method);
        $disjunction1 = new OrX();

        $disjunction2 = call_user_func_array(array($disjunction1, $method), $args);

        $this->assertEquals(array(), $disjunction1->getDisjuncts());
        $this->assertEquals(array($expected), $disjunction2->getDisjuncts());
    }

    public function testEvaluate()
    {
        $disjunction = new OrX(array(
            new Key('name', new Same('Thomas')),
            new Key('age', new GreaterThan(0)),
        ));

        $this->assertTrue($disjunction->evaluate(array('name' => 'Thomas', 'age' => 35)));
        $this->assertTrue($disjunction->evaluate(array('name' => null, 'age' => 35)));
        $this->assertTrue($disjunction->evaluate(array('name' => 'Thomas', 'age' => 0)));
        $this->assertFalse($disjunction->evaluate(array('name' => null, 'age' => 0)));
    }

    public function testEquivalentTo()
    {
        $disjunction1 = new OrX(array(
            new Key('name', new Same('10')),
            new Key('age', new GreaterThan(0)),
        ));

        // disjunctions match independent of the order of the conjuncts
        $disjunction2 = new OrX(array(
            new Key('age', new GreaterThan(0)),
            new Key('name', new Same('10')),
        ));

        $disjunction3 = new OrX(array(
            new Key('age', new GreaterThan(0)),
        ));

        $this->assertTrue($disjunction1->equivalentTo($disjunction2));
        $this->assertTrue($disjunction2->equivalentTo($disjunction1));
        $this->assertFalse($disjunction2->equivalentTo($disjunction3));
        $this->assertFalse($disjunction3->equivalentTo($disjunction2));
        $this->assertFalse($disjunction1->equivalentTo($disjunction3));
        $this->assertFalse($disjunction3->equivalentTo($disjunction1));
    }

    public function testToString()
    {
        $expr1 = new OrX();
        $expr2 = new OrX(array(new GreaterThan(10), new EndsWith('.css')));
        $expr3 = new OrX(array(new GreaterThan(10), new AndX(array(new Contains('foo'), new EndsWith('.css')))));

        $this->assertSame('', $expr1->toString());
        $this->assertSame('>10 || endsWith(".css")', $expr2->toString());
        $this->assertSame('>10 || (contains("foo") && endsWith(".css"))', $expr3->toString());
    }
}


================================================
FILE: tests/Logic/Fixtures/TestLiteral.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Tests\Logic\Fixtures;

use Webmozart\Expression\Expression;
use Webmozart\Expression\Logic\Literal;

/**
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class TestLiteral extends Literal
{
    private $value;

    public function __construct($value = null)
    {
        $this->value = $value;
    }

    public function evaluate($value)
    {
    }

    public function equivalentTo(Expression $other)
    {
        return $other instanceof $this && $this->value === $other->value;
    }

    public function toString()
    {
    }
}


================================================
FILE: tests/Logic/LiteralTest.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Tests\Logic;

use PHPUnit_Framework_TestCase;
use Webmozart\Expression\Constraint\Same;
use Webmozart\Expression\Expr;
use Webmozart\Expression\Logic\AlwaysFalse;
use Webmozart\Expression\Logic\AlwaysTrue;
use Webmozart\Expression\Logic\AndX;
use Webmozart\Expression\Logic\OrX;
use Webmozart\Expression\Tests\Logic\Fixtures\TestLiteral;

/**
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class LiteralTest extends PHPUnit_Framework_TestCase
{
    public function testAndX()
    {
        $literal = new TestLiteral();
        $expr = new Same('10');

        $this->assertEquals(new AndX(array($literal, $expr)), $literal->andX($expr));
    }

    public function testAndXIgnoresDuplicates()
    {
        $literal = new TestLiteral('value');

        $this->assertEquals($literal, $literal->andX(new TestLiteral('value')));
    }

    public function testAndTrueIgnored()
    {
        $literal = new TestLiteral('value');
        $conjunction = $literal->andTrue();

        $this->assertSame($literal, $conjunction);
    }

    public function testAndXIgnoresTrue()
    {
        $literal = new TestLiteral('value');
        $conjunction = $literal->andX(Expr::true());

        $this->assertSame($literal, $conjunction);
    }

    public function testAndFalseReturnsFalse()
    {
        $literal = new TestLiteral('value');
        $conjunction = $literal->andFalse();

        $this->assertInstanceOf('Webmozart\Expression\Logic\AlwaysFalse', $conjunction);
    }

    public function testAndXReturnsFalse()
    {
        $literal = new TestLiteral('value');
        $conjunction = $literal->andX($false = Expr::false());

        $this->assertSame($false, $conjunction);
    }

    /**
     * @dataProvider \Webmozart\Expression\Tests\ExprTest::getMethodTests
     */
    public function testAnd($method, $args, $expected)
    {
        // tested separately
        if ('true' === $method || 'false' === $method) {
            return;
        }

        if ('is' === substr($method, 0, 2)) {
            $method = substr($method, 2);
        }

        $method = 'and'.ucfirst($method);
        $literal = new TestLiteral();

        $result = call_user_func_array(array($literal, $method), $args);

        $this->assertEquals(new AndX(array($literal, $expected)), $result);
    }

    public function testOrX()
    {
        $literal = new TestLiteral();
        $expr = new Same('10');

        $this->assertEquals(new OrX(array($literal, $expr)), $literal->orX($expr));
    }

    public function testOrXIgnoresDuplicates()
    {
        $literal = new TestLiteral('value');

        $this->assertEquals($literal, $literal->orX(new TestLiteral('value')));
    }

    public function testOrFalseIgnored()
    {
        $literal = new TestLiteral('value');
        $disjunction = $literal->orFalse();

        $this->assertSame($literal, $disjunction);
    }

    public function testOrXIgnoresFalse()
    {
        $literal = new TestLiteral('value');
        $disjunction = $literal->orX(new AlwaysFalse());

        $this->assertSame($literal, $disjunction);
    }

    public function testOrTrueReturnsTrue()
    {
        $literal = new TestLiteral('value');
        $disjunction = $literal->orTrue();

        $this->assertInstanceOf('Webmozart\Expression\Logic\AlwaysTrue', $disjunction);
    }

    public function testOrXReturnsTrue()
    {
        $literal = new TestLiteral('value');
        $disjunction = $literal->orX($true = new AlwaysTrue());

        $this->assertSame($true, $disjunction);
    }

    /**
     * @dataProvider \Webmozart\Expression\Tests\ExprTest::getMethodTests
     */
    public function testOr($method, $args, $expected)
    {
        // tested separately
        if ('true' === $method || 'false' === $method) {
            return;
        }

        if ('is' === substr($method, 0, 2)) {
            $method = substr($method, 2);
        }

        $method = 'or'.ucfirst($method);
        $literal = new TestLiteral();

        $result = call_user_func_array(array($literal, $method), $args);

        $this->assertEquals(new OrX(array($literal, $expected)), $result);
    }
}


================================================
FILE: tests/Logic/NotTest.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Tests\Logic;

use PHPUnit_Framework_TestCase;
use Webmozart\Expression\Constraint\GreaterThan;
use Webmozart\Expression\Constraint\LessThan;
use Webmozart\Expression\Constraint\StartsWith;
use Webmozart\Expression\Logic\Not;
use Webmozart\Expression\Logic\OrX;

/**
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class NotTest extends PHPUnit_Framework_TestCase
{
    public function testEvaluate()
    {
        $expr = new Not(new StartsWith('Thomas'));

        $this->assertTrue($expr->evaluate('Mr. Thomas Edison'));
        $this->assertFalse($expr->evaluate('Thomas Edison'));
    }

    public function testEquivalentTo()
    {
        $expr1 = new Not(new OrX(array(new LessThan(0), new GreaterThan(10))));
        $expr2 = new Not(new OrX(array(new GreaterThan(10), new LessThan(0))));
        $expr3 = new Not(new OrX(array(new GreaterThan(10))));

        $this->assertTrue($expr1->equivalentTo($expr2));
        $this->assertFalse($expr2->equivalentTo($expr3));
        $this->assertFalse($expr1->equivalentTo($expr3));
    }

    public function testToString()
    {
        $expr1 = new Not(new StartsWith('Thomas'));
        $expr2 = new Not(new OrX(array(new GreaterThan(10), new LessThan(0))));

        $this->assertSame('not(startsWith("Thomas"))', $expr1->toString());
        $this->assertSame('not(>10 || <0)', $expr2->toString());
    }
}


================================================
FILE: tests/Selector/AllTest.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Tests\Selector;

use ArrayIterator;
use PHPUnit_Framework_TestCase;
use Webmozart\Expression\Constraint\EndsWith;
use Webmozart\Expression\Constraint\GreaterThan;
use Webmozart\Expression\Logic\AndX;
use Webmozart\Expression\Selector\All;

/**
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class AllTest extends PHPUnit_Framework_TestCase
{
    public function testEvaluate()
    {
        $all = new All(new GreaterThan(10));

        $this->assertTrue($all->evaluate(array(11, 12, 13)));
        $this->assertTrue($all->evaluate(array(11, 12)));
        $this->assertFalse($all->evaluate(array(10, 11, 12)));
        $this->assertFalse($all->evaluate(array(9, 10, 11, 12)));
        $this->assertTrue($all->evaluate(new ArrayIterator(array(11, 12, 13))));
        $this->assertTrue($all->evaluate(new ArrayIterator(array(11, 12))));
        $this->assertFalse($all->evaluate(new ArrayIterator(array(10, 11, 12))));
        $this->assertFalse($all->evaluate(new ArrayIterator(array(9, 10, 11, 12))));

        $this->assertTrue($all->evaluate(array()));
        $this->assertFalse($all->evaluate('foobar'));
    }

    public function testToString()
    {
        $expr1 = new All(new GreaterThan(10));
        $expr2 = new All(new EndsWith('.css'));
        $expr3 = new All(new AndX(array(
            new GreaterThan(10),
            new EndsWith('.css'),
        )));

        $this->assertSame('all(>10)', $expr1->toString());
        $this->assertSame('all(endsWith(".css"))', $expr2->toString());
        $this->assertSame('all(>10 && endsWith(".css"))', $expr3->toString());
    }
}


================================================
FILE: tests/Selector/AtLeastTest.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Tests\Selector;

use ArrayIterator;
use PHPUnit_Framework_TestCase;
use Webmozart\Expression\Constraint\EndsWith;
use Webmozart\Expression\Constraint\GreaterThan;
use Webmozart\Expression\Logic\AndX;
use Webmozart\Expression\Selector\AtLeast;

/**
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class AtLeastTest extends PHPUnit_Framework_TestCase
{
    public function testEvaluate()
    {
        $atLeast1 = new AtLeast(1, new GreaterThan(10));
        $atLeast2 = new AtLeast(2, new GreaterThan(10));

        $this->assertTrue($atLeast1->evaluate(array(9, 10, 11, 12)));
        $this->assertTrue($atLeast1->evaluate(array(9, 10, 11)));
        $this->assertFalse($atLeast1->evaluate(array(9, 10)));
        $this->assertTrue($atLeast1->evaluate(new ArrayIterator(array(9, 10, 11, 12))));
        $this->assertTrue($atLeast1->evaluate(new ArrayIterator(array(9, 10, 11))));
        $this->assertFalse($atLeast1->evaluate(new ArrayIterator(array(9, 10))));

        $this->assertTrue($atLeast2->evaluate(array(9, 10, 11, 12)));
        $this->assertFalse($atLeast2->evaluate(array(9, 10, 11)));
        $this->assertFalse($atLeast2->evaluate(array(9, 10)));
        $this->assertTrue($atLeast2->evaluate(new ArrayIterator(array(9, 10, 11, 12))));
        $this->assertFalse($atLeast2->evaluate(new ArrayIterator(array(9, 10, 11))));
        $this->assertFalse($atLeast2->evaluate(new ArrayIterator(array(9, 10))));

        $this->assertFalse($atLeast1->evaluate(array()));
        $this->assertFalse($atLeast1->evaluate('foobar'));
    }

    public function testToString()
    {
        $expr1 = new AtLeast(1, new GreaterThan(10));
        $expr2 = new AtLeast(2, new EndsWith('.css'));
        $expr3 = new AtLeast(3, new AndX(array(
            new GreaterThan(10),
            new EndsWith('.css'),
        )));

        $this->assertSame('atLeast(1, >10)', $expr1->toString());
        $this->assertSame('atLeast(2, endsWith(".css"))', $expr2->toString());
        $this->assertSame('atLeast(3, >10 && endsWith(".css"))', $expr3->toString());
    }
}


================================================
FILE: tests/Selector/AtMostTest.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Tests\Selector;

use ArrayIterator;
use PHPUnit_Framework_TestCase;
use Webmozart\Expression\Constraint\EndsWith;
use Webmozart\Expression\Constraint\GreaterThan;
use Webmozart\Expression\Logic\AndX;
use Webmozart\Expression\Selector\AtMost;

/**
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class AtMostTest extends PHPUnit_Framework_TestCase
{
    public function testEvaluate()
    {
        $atMost1 = new AtMost(1, new GreaterThan(10));
        $atMost2 = new AtMost(2, new GreaterThan(10));

        $this->assertFalse($atMost1->evaluate(array(9, 10, 11, 12)));
        $this->assertTrue($atMost1->evaluate(array(9, 10, 11)));
        $this->assertTrue($atMost1->evaluate(array(9, 10)));
        $this->assertFalse($atMost1->evaluate(new ArrayIterator(array(9, 10, 11, 12))));
        $this->assertTrue($atMost1->evaluate(new ArrayIterator(array(9, 10, 11))));
        $this->assertTrue($atMost1->evaluate(new ArrayIterator(array(9, 10))));

        $this->assertFalse($atMost2->evaluate(array(9, 10, 11, 12, 13)));
        $this->assertTrue($atMost2->evaluate(array(9, 10, 11, 12)));
        $this->assertTrue($atMost2->evaluate(array(9, 10, 11)));
        $this->assertTrue($atMost2->evaluate(array(9, 10)));
        $this->assertFalse($atMost2->evaluate(new ArrayIterator(array(9, 10, 11, 12, 13))));
        $this->assertTrue($atMost2->evaluate(new ArrayIterator(array(9, 10, 11, 12))));
        $this->assertTrue($atMost2->evaluate(new ArrayIterator(array(9, 10, 11))));
        $this->assertTrue($atMost2->evaluate(new ArrayIterator(array(9, 10))));

        $this->assertTrue($atMost1->evaluate(array()));
        $this->assertFalse($atMost1->evaluate('foobar'));
    }

    public function testToString()
    {
        $expr1 = new AtMost(1, new GreaterThan(10));
        $expr2 = new AtMost(2, new EndsWith('.css'));
        $expr3 = new AtMost(3, new AndX(array(
            new GreaterThan(10),
            new EndsWith('.css'),
        )));

        $this->assertSame('atMost(1, >10)', $expr1->toString());
        $this->assertSame('atMost(2, endsWith(".css"))', $expr2->toString());
        $this->assertSame('atMost(3, >10 && endsWith(".css"))', $expr3->toString());
    }
}


================================================
FILE: tests/Selector/CountTest.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Tests\Selector;

use ArrayIterator;
use PHPUnit_Framework_TestCase;
use Webmozart\Expression\Constraint\GreaterThan;
use Webmozart\Expression\Selector\Count;

/**
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class CountTest extends PHPUnit_Framework_TestCase
{
    public function testEvaluate()
    {
        $all = new Count(new GreaterThan(1));

        $this->assertTrue($all->evaluate(array(1, 2, 3)));
        $this->assertTrue($all->evaluate(array(1, 2)));
        $this->assertFalse($all->evaluate(array(1)));
        $this->assertTrue($all->evaluate(new ArrayIterator(array(1, 2, 3))));
        $this->assertTrue($all->evaluate(new ArrayIterator(array(1, 2))));
        $this->assertFalse($all->evaluate(new ArrayIterator(array(1))));

        $this->assertFalse($all->evaluate(array()));
        $this->assertFalse($all->evaluate('foobar'));
    }

    public function testToString()
    {
        $expr = new Count(new GreaterThan(10));

        $this->assertSame('count(>10)', $expr->toString());
    }
}


================================================
FILE: tests/Selector/ExactlyTest.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Tests\Selector;

use ArrayIterator;
use PHPUnit_Framework_TestCase;
use Webmozart\Expression\Constraint\EndsWith;
use Webmozart\Expression\Constraint\GreaterThan;
use Webmozart\Expression\Logic\AndX;
use Webmozart\Expression\Selector\Exactly;

/**
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class ExactlyTest extends PHPUnit_Framework_TestCase
{
    public function testEvaluate()
    {
        $exactly1 = new Exactly(1, new GreaterThan(10));
        $exactly2 = new Exactly(2, new GreaterThan(10));

        $this->assertFalse($exactly1->evaluate(array(9, 10, 11, 12)));
        $this->assertTrue($exactly1->evaluate(array(9, 10, 11)));
        $this->assertFalse($exactly1->evaluate(array(9, 10)));
        $this->assertFalse($exactly1->evaluate(new ArrayIterator(array(9, 10, 11, 12))));
        $this->assertTrue($exactly1->evaluate(new ArrayIterator(array(9, 10, 11))));
        $this->assertFalse($exactly1->evaluate(new ArrayIterator(array(9, 10))));

        $this->assertTrue($exactly2->evaluate(array(9, 10, 11, 12)));
        $this->assertFalse($exactly2->evaluate(array(9, 10, 11)));
        $this->assertFalse($exactly2->evaluate(array(9, 10)));
        $this->assertTrue($exactly2->evaluate(new ArrayIterator(array(9, 10, 11, 12))));
        $this->assertFalse($exactly2->evaluate(new ArrayIterator(array(9, 10, 11))));
        $this->assertFalse($exactly2->evaluate(new ArrayIterator(array(9, 10))));

        $this->assertFalse($exactly1->evaluate(array()));
        $this->assertFalse($exactly1->evaluate('foobar'));
    }

    public function testToString()
    {
        $expr1 = new Exactly(1, new GreaterThan(10));
        $expr2 = new Exactly(2, new EndsWith('.css'));
        $expr3 = new Exactly(3, new AndX(array(
            new GreaterThan(10),
            new EndsWith('.css'),
        )));

        $this->assertSame('exactly(1, >10)', $expr1->toString());
        $this->assertSame('exactly(2, endsWith(".css"))', $expr2->toString());
        $this->assertSame('exactly(3, >10 && endsWith(".css"))', $expr3->toString());
    }
}


================================================
FILE: tests/Selector/Fixtures/TestSelector.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Tests\Selector\Fixtures;

use Webmozart\Expression\Expression;
use Webmozart\Expression\Selector\Selector;

/**
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class TestSelector extends Selector
{
    /**
     * @var string|int
     */
    private $key;

    public function __construct($key, Expression $expr)
    {
        parent::__construct($expr);

        $this->key = $key;
    }

    /**
     * {@inheritdoc}
     */
    public function evaluate($value)
    {
        return $this->expr->evaluate($value[$this->key]);
    }

    /**
     * {@inheritdoc}
     */
    public function toString()
    {
    }
}


================================================
FILE: tests/Selector/KeyTest.php
================================================
<?php

/*
 * This file is part of the webmozart/expression package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Expression\Tests\Selector;

use PHPUnit_Framework_TestCase;
use Webmozart\Expression\Constraint\EndsWith;
use Webmozart\Expression\Constraint\GreaterThan;
use Webmozart\Expression\Logic\AndX;
use Webmozart\Expression\Selector\Key;

/**
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class KeyTest extends PHPUnit_Framework_TestCase
{
    public function testEvaluate()
    {
        $expr = new Key('key', new GreaterThan(10));

        $this->assertTrue($expr->evaluat
Download .txt
gitextract_uy2uw1lp/

├── .composer-auth.json
├── .gitignore
├── .styleci.yml
├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── appveyor.yml
├── composer.json
├── phpunit.xml.dist
├── src/
│   ├── Constraint/
│   │   ├── Contains.php
│   │   ├── EndsWith.php
│   │   ├── Equals.php
│   │   ├── GreaterThan.php
│   │   ├── GreaterThanEqual.php
│   │   ├── In.php
│   │   ├── IsEmpty.php
│   │   ├── IsInstanceOf.php
│   │   ├── KeyExists.php
│   │   ├── KeyNotExists.php
│   │   ├── LessThan.php
│   │   ├── LessThanEqual.php
│   │   ├── Matches.php
│   │   ├── NotEquals.php
│   │   ├── NotSame.php
│   │   ├── Same.php
│   │   └── StartsWith.php
│   ├── Expr.php
│   ├── Expression.php
│   ├── Logic/
│   │   ├── AlwaysFalse.php
│   │   ├── AlwaysTrue.php
│   │   ├── AndX.php
│   │   ├── Literal.php
│   │   ├── Not.php
│   │   └── OrX.php
│   ├── PhpUnit/
│   │   └── ExpressionComparator.php
│   ├── Selector/
│   │   ├── All.php
│   │   ├── AtLeast.php
│   │   ├── AtMost.php
│   │   ├── Count.php
│   │   ├── Exactly.php
│   │   ├── Key.php
│   │   ├── Method.php
│   │   ├── Property.php
│   │   └── Selector.php
│   ├── Traversal/
│   │   ├── ExpressionTraverser.php
│   │   └── ExpressionVisitor.php
│   └── Util/
│       └── StringUtil.php
└── tests/
    ├── Comparison/
    │   ├── ContainsTest.php
    │   ├── EndsWithTest.php
    │   ├── EqualsTest.php
    │   ├── GreaterThanEqualTest.php
    │   ├── GreaterThanTest.php
    │   ├── InTest.php
    │   ├── IsEmptyTest.php
    │   ├── IsInstanceOfTest.php
    │   ├── KeyExistsTest.php
    │   ├── KeyNotExistsTest.php
    │   ├── LessThanEqualTest.php
    │   ├── LessThanTest.php
    │   ├── MatchesTest.php
    │   ├── NotEqualsTest.php
    │   ├── NotSameTest.php
    │   ├── SameTest.php
    │   └── StartsWithTest.php
    ├── DomainExpressionsTest.php
    ├── EquivalenceTest.php
    ├── ExprTest.php
    ├── Fixtures/
    │   ├── Customer.php
    │   ├── HasPreviousBookings.php
    │   └── IsPremium.php
    ├── Logic/
    │   ├── AlwaysFalseTest.php
    │   ├── AlwaysTrueTest.php
    │   ├── ConjunctionTest.php
    │   ├── DisjunctionTest.php
    │   ├── Fixtures/
    │   │   └── TestLiteral.php
    │   ├── LiteralTest.php
    │   └── NotTest.php
    ├── Selector/
    │   ├── AllTest.php
    │   ├── AtLeastTest.php
    │   ├── AtMostTest.php
    │   ├── CountTest.php
    │   ├── ExactlyTest.php
    │   ├── Fixtures/
    │   │   └── TestSelector.php
    │   ├── KeyTest.php
    │   ├── MethodTest.php
    │   ├── PropertyTest.php
    │   └── SelectorTest.php
    └── Traversal/
        └── ExpressionTraverserTest.php
Download .txt
SYMBOL INDEX (571 symbols across 79 files)

FILE: src/Constraint/Contains.php
  class Contains (line 26) | class Contains extends Literal
    method __construct (line 38) | public function __construct($comparedValue)
    method getComparedValue (line 48) | public function getComparedValue()
    method evaluate (line 56) | public function evaluate($value)
    method equivalentTo (line 64) | public function equivalentTo(Expression $other)
    method toString (line 73) | public function toString()

FILE: src/Constraint/EndsWith.php
  class EndsWith (line 25) | class EndsWith extends Literal
    method __construct (line 37) | public function __construct($acceptedSuffix)
    method getAcceptedSuffix (line 47) | public function getAcceptedSuffix()
    method evaluate (line 55) | public function evaluate($value)
    method equivalentTo (line 63) | public function equivalentTo(Expression $other)
    method toString (line 72) | public function toString()

FILE: src/Constraint/Equals.php
  class Equals (line 27) | class Equals extends Literal
    method __construct (line 39) | public function __construct($comparedValue)
    method getComparedValue (line 49) | public function getComparedValue()
    method evaluate (line 57) | public function evaluate($value)
    method equivalentTo (line 65) | public function equivalentTo(Expression $other)
    method toString (line 78) | public function toString()

FILE: src/Constraint/GreaterThan.php
  class GreaterThan (line 27) | class GreaterThan extends Literal
    method __construct (line 39) | public function __construct($comparedValue)
    method getComparedValue (line 49) | public function getComparedValue()
    method evaluate (line 57) | public function evaluate($value)
    method equivalentTo (line 65) | public function equivalentTo(Expression $other)
    method toString (line 74) | public function toString()

FILE: src/Constraint/GreaterThanEqual.php
  class GreaterThanEqual (line 27) | class GreaterThanEqual extends Literal
    method __construct (line 39) | public function __construct($comparedValue)
    method getComparedValue (line 49) | public function getComparedValue()
    method evaluate (line 57) | public function evaluate($value)
    method equivalentTo (line 65) | public function equivalentTo(Expression $other)
    method toString (line 74) | public function toString()

FILE: src/Constraint/In.php
  class In (line 25) | class In extends Literal
    method __construct (line 43) | public function __construct(array $acceptedValues, $strict = true)
    method getAcceptedValues (line 54) | public function getAcceptedValues()
    method isStrict (line 65) | public function isStrict()
    method evaluate (line 73) | public function evaluate($value)
    method equivalentTo (line 81) | public function equivalentTo(Expression $other)
    method toString (line 121) | public function toString()

FILE: src/Constraint/IsEmpty.php
  class IsEmpty (line 24) | class IsEmpty extends Literal
    method evaluate (line 29) | public function evaluate($value)
    method equivalentTo (line 37) | public function equivalentTo(Expression $other)
    method toString (line 46) | public function toString()

FILE: src/Constraint/IsInstanceOf.php
  class IsInstanceOf (line 24) | class IsInstanceOf extends Literal
    method __construct (line 36) | public function __construct($className)
    method getClassName (line 46) | public function getClassName()
    method evaluate (line 54) | public function evaluate($value)
    method equivalentTo (line 62) | public function equivalentTo(Expression $other)
    method toString (line 75) | public function toString()

FILE: src/Constraint/KeyExists.php
  class KeyExists (line 25) | class KeyExists extends Literal
    method __construct (line 37) | public function __construct($key)
    method getKey (line 47) | public function getKey()
    method evaluate (line 55) | public function evaluate($value)
    method equivalentTo (line 67) | public function equivalentTo(Expression $other)
    method toString (line 76) | public function toString()

FILE: src/Constraint/KeyNotExists.php
  class KeyNotExists (line 25) | class KeyNotExists extends Literal
    method __construct (line 37) | public function __construct($key)
    method getKey (line 47) | public function getKey()
    method evaluate (line 55) | public function evaluate($value)
    method equivalentTo (line 67) | public function equivalentTo(Expression $other)
    method toString (line 76) | public function toString()

FILE: src/Constraint/LessThan.php
  class LessThan (line 27) | class LessThan extends Literal
    method __construct (line 39) | public function __construct($comparedValue)
    method getComparedValue (line 49) | public function getComparedValue()
    method evaluate (line 57) | public function evaluate($value)
    method equivalentTo (line 65) | public function equivalentTo(Expression $other)
    method toString (line 74) | public function toString()

FILE: src/Constraint/LessThanEqual.php
  class LessThanEqual (line 27) | class LessThanEqual extends Literal
    method __construct (line 39) | public function __construct($comparedValue)
    method getComparedValue (line 49) | public function getComparedValue()
    method evaluate (line 57) | public function evaluate($value)
    method equivalentTo (line 65) | public function equivalentTo(Expression $other)
    method toString (line 74) | public function toString()

FILE: src/Constraint/Matches.php
  class Matches (line 27) | class Matches extends Literal
    method __construct (line 39) | public function __construct($regExp)
    method getRegularExpression (line 49) | public function getRegularExpression()
    method evaluate (line 57) | public function evaluate($value)
    method equivalentTo (line 65) | public function equivalentTo(Expression $other)
    method toString (line 74) | public function toString()

FILE: src/Constraint/NotEquals.php
  class NotEquals (line 27) | class NotEquals extends Literal
    method __construct (line 39) | public function __construct($comparedValue)
    method getComparedValue (line 49) | public function getComparedValue()
    method evaluate (line 57) | public function evaluate($value)
    method equivalentTo (line 65) | public function equivalentTo(Expression $other)
    method toString (line 74) | public function toString()

FILE: src/Constraint/NotSame.php
  class NotSame (line 27) | class NotSame extends Literal
    method __construct (line 39) | public function __construct($comparedValue)
    method getComparedValue (line 49) | public function getComparedValue()
    method evaluate (line 57) | public function evaluate($value)
    method equivalentTo (line 65) | public function equivalentTo(Expression $other)
    method toString (line 74) | public function toString()

FILE: src/Constraint/Same.php
  class Same (line 27) | class Same extends Literal
    method __construct (line 39) | public function __construct($comparedValue)
    method getComparedValue (line 49) | public function getComparedValue()
    method evaluate (line 57) | public function evaluate($value)
    method equivalentTo (line 65) | public function equivalentTo(Expression $other)
    method toString (line 78) | public function toString()

FILE: src/Constraint/StartsWith.php
  class StartsWith (line 25) | class StartsWith extends Literal
    method __construct (line 37) | public function __construct($acceptedPrefix)
    method getAcceptedPrefix (line 47) | public function getAcceptedPrefix()
    method evaluate (line 55) | public function evaluate($value)
    method equivalentTo (line 63) | public function equivalentTo(Expression $other)
    method toString (line 72) | public function toString()

FILE: src/Expr.php
  class Expr (line 86) | class Expr
    method filter (line 99) | public static function filter($collection, Expression $expr)
    method expr (line 132) | public static function expr(Expression $expr)
    method not (line 144) | public static function not(Expression $expr)
    method andX (line 156) | public static function andX(array $conjuncts)
    method orX (line 168) | public static function orX(array $disjuncts)
    method true (line 178) | public static function true()
    method false (line 188) | public static function false()
    method key (line 201) | public static function key($key, Expression $expr)
    method method (line 215) | public static function method($methodName, $args)
    method property (line 232) | public static function property($propertyName, Expression $expr)
    method atLeast (line 245) | public static function atLeast($count, Expression $expr)
    method atMost (line 258) | public static function atMost($count, Expression $expr)
    method exactly (line 271) | public static function exactly($count, Expression $expr)
    method all (line 283) | public static function all(Expression $expr)
    method count (line 295) | public static function count(Expression $expr)
    method null (line 305) | public static function null()
    method notNull (line 315) | public static function notNull()
    method isEmpty (line 325) | public static function isEmpty()
    method notEmpty (line 335) | public static function notEmpty()
    method isInstanceOf (line 347) | public static function isInstanceOf($className)
    method equals (line 359) | public static function equals($value)
    method notEquals (line 371) | public static function notEquals($value)
    method same (line 383) | public static function same($value)
    method notSame (line 395) | public static function notSame($value)
    method greaterThan (line 407) | public static function greaterThan($value)
    method greaterThanEqual (line 419) | public static function greaterThanEqual($value)
    method lessThan (line 431) | public static function lessThan($value)
    method lessThanEqual (line 443) | public static function lessThanEqual($value)
    method in (line 455) | public static function in(array $values)
    method matches (line 467) | public static function matches($regExp)
    method startsWith (line 479) | public static function startsWith($prefix)
    method endsWith (line 491) | public static function endsWith($suffix)
    method contains (line 503) | public static function contains($string)
    method keyExists (line 515) | public static function keyExists($keyName)
    method keyNotExists (line 527) | public static function keyNotExists($keyName)
    method __construct (line 535) | private function __construct()

FILE: src/Expression.php
  type Expression (line 21) | interface Expression
    method evaluate (line 31) | public function evaluate($value);
    method equivalentTo (line 41) | public function equivalentTo(Expression $other);
    method toString (line 48) | public function toString();

FILE: src/Logic/AlwaysFalse.php
  class AlwaysFalse (line 23) | class AlwaysFalse extends Literal
    method evaluate (line 28) | public function evaluate($value)
    method equivalentTo (line 36) | public function equivalentTo(Expression $other)
    method toString (line 45) | public function toString()
    method andX (line 53) | public function andX(Expression $expr)
    method orX (line 61) | public function orX(Expression $expr)

FILE: src/Logic/AlwaysTrue.php
  class AlwaysTrue (line 23) | class AlwaysTrue extends Literal
    method evaluate (line 28) | public function evaluate($value)
    method equivalentTo (line 36) | public function equivalentTo(Expression $other)
    method toString (line 45) | public function toString()
    method andX (line 53) | public function andX(Expression $expr)
    method orX (line 61) | public function orX(Expression $expr)

FILE: src/Logic/AndX.php
  class AndX (line 27) | class AndX implements Expression
    method __construct (line 39) | public function __construct(array $conjuncts = array())
    method getConjuncts (line 58) | public function getConjuncts()
    method andX (line 63) | public function andX(Expression $expr)
    method andNot (line 88) | public function andNot(Expression $expr)
    method andTrue (line 93) | public function andTrue()
    method andFalse (line 98) | public function andFalse()
    method andKey (line 103) | public function andKey($keyName, Expression $expr)
    method andMethod (line 108) | public function andMethod($methodName, $args)
    method andProperty (line 113) | public function andProperty($propertyName, Expression $expr)
    method andAtLeast (line 118) | public function andAtLeast($count, Expression $expr)
    method andAtMost (line 123) | public function andAtMost($count, Expression $expr)
    method andExactly (line 128) | public function andExactly($count, Expression $expr)
    method andAll (line 133) | public function andAll(Expression $expr)
    method andCount (line 138) | public function andCount(Expression $expr)
    method andNull (line 143) | public function andNull()
    method andNotNull (line 148) | public function andNotNull()
    method andEmpty (line 153) | public function andEmpty()
    method andNotEmpty (line 158) | public function andNotEmpty()
    method andInstanceOf (line 163) | public function andInstanceOf($className)
    method andEquals (line 168) | public function andEquals($value)
    method andNotEquals (line 173) | public function andNotEquals($value)
    method andSame (line 178) | public function andSame($value)
    method andNotSame (line 183) | public function andNotSame($value)
    method andGreaterThan (line 188) | public function andGreaterThan($value)
    method andGreaterThanEqual (line 193) | public function andGreaterThanEqual($value)
    method andLessThan (line 198) | public function andLessThan($value)
    method andLessThanEqual (line 203) | public function andLessThanEqual($value)
    method andIn (line 208) | public function andIn(array $values)
    method andMatches (line 213) | public function andMatches($regExp)
    method andStartsWith (line 218) | public function andStartsWith($prefix)
    method andEndsWith (line 223) | public function andEndsWith($suffix)
    method andContains (line 228) | public function andContains($string)
    method andKeyExists (line 233) | public function andKeyExists($keyName)
    method andKeyNotExists (line 238) | public function andKeyNotExists($keyName)
    method evaluate (line 246) | public function evaluate($values)
    method equivalentTo (line 260) | public function equivalentTo(Expression $other)
    method toString (line 289) | public function toString()
    method __toString (line 296) | public function __toString()

FILE: src/Logic/Literal.php
  class Literal (line 39) | abstract class Literal implements Expression
    method andX (line 41) | public function andX(Expression $expr)
    method andNot (line 56) | public function andNot(Expression $expr)
    method andTrue (line 61) | public function andTrue()
    method andFalse (line 66) | public function andFalse()
    method andKey (line 71) | public function andKey($keyName, Expression $expr)
    method andMethod (line 76) | public function andMethod($methodName, $args)
    method andProperty (line 81) | public function andProperty($propertyName, Expression $expr)
    method andAtLeast (line 86) | public function andAtLeast($count, Expression $expr)
    method andAtMost (line 91) | public function andAtMost($count, Expression $expr)
    method andExactly (line 96) | public function andExactly($count, Expression $expr)
    method andAll (line 101) | public function andAll(Expression $expr)
    method andCount (line 106) | public function andCount(Expression $expr)
    method andNull (line 111) | public function andNull()
    method andNotNull (line 116) | public function andNotNull()
    method andEmpty (line 121) | public function andEmpty()
    method andNotEmpty (line 126) | public function andNotEmpty()
    method andInstanceOf (line 131) | public function andInstanceOf($className)
    method andEquals (line 136) | public function andEquals($value)
    method andNotEquals (line 141) | public function andNotEquals($value)
    method andSame (line 146) | public function andSame($value)
    method andNotSame (line 151) | public function andNotSame($value)
    method andGreaterThan (line 156) | public function andGreaterThan($value)
    method andGreaterThanEqual (line 161) | public function andGreaterThanEqual($value)
    method andLessThan (line 166) | public function andLessThan($value)
    method andLessThanEqual (line 171) | public function andLessThanEqual($value)
    method andIn (line 176) | public function andIn(array $values)
    method andMatches (line 181) | public function andMatches($regExp)
    method andStartsWith (line 186) | public function andStartsWith($prefix)
    method andEndsWith (line 191) | public function andEndsWith($suffix)
    method andContains (line 196) | public function andContains($string)
    method andKeyExists (line 201) | public function andKeyExists($keyName)
    method andKeyNotExists (line 206) | public function andKeyNotExists($keyName)
    method orX (line 211) | public function orX(Expression $expr)
    method orNot (line 226) | public function orNot(Expression $expr)
    method orTrue (line 231) | public function orTrue()
    method orFalse (line 236) | public function orFalse()
    method orKey (line 241) | public function orKey($keyName, Expression $expr)
    method orMethod (line 246) | public function orMethod($methodName, $args)
    method orProperty (line 251) | public function orProperty($propertyName, Expression $expr)
    method orAtLeast (line 256) | public function orAtLeast($count, Expression $expr)
    method orAtMost (line 261) | public function orAtMost($count, Expression $expr)
    method orExactly (line 266) | public function orExactly($count, Expression $expr)
    method orAll (line 271) | public function orAll(Expression $expr)
    method orCount (line 276) | public function orCount(Expression $expr)
    method orNull (line 281) | public function orNull()
    method orNotNull (line 286) | public function orNotNull()
    method orEmpty (line 291) | public function orEmpty()
    method orNotEmpty (line 296) | public function orNotEmpty()
    method orInstanceOf (line 301) | public function orInstanceOf($className)
    method orEquals (line 306) | public function orEquals($value)
    method orNotEquals (line 311) | public function orNotEquals($value)
    method orSame (line 316) | public function orSame($value)
    method orNotSame (line 321) | public function orNotSame($value)
    method orGreaterThan (line 326) | public function orGreaterThan($value)
    method orGreaterThanEqual (line 331) | public function orGreaterThanEqual($value)
    method orLessThan (line 336) | public function orLessThan($value)
    method orLessThanEqual (line 341) | public function orLessThanEqual($value)
    method orIn (line 346) | public function orIn(array $values)
    method orMatches (line 351) | public function orMatches($regExp)
    method orStartsWith (line 356) | public function orStartsWith($prefix)
    method orEndsWith (line 361) | public function orEndsWith($suffix)
    method orContains (line 366) | public function orContains($string)
    method orKeyExists (line 371) | public function orKeyExists($keyName)
    method orKeyNotExists (line 376) | public function orKeyNotExists($keyName)
    method __toString (line 381) | public function __toString()

FILE: src/Logic/Not.php
  class Not (line 23) | class Not extends Literal
    method __construct (line 35) | public function __construct(Expression $expr)
    method getNegatedExpression (line 45) | public function getNegatedExpression()
    method evaluate (line 53) | public function evaluate($value)
    method equivalentTo (line 61) | public function equivalentTo(Expression $other)
    method toString (line 70) | public function toString()

FILE: src/Logic/OrX.php
  class OrX (line 27) | class OrX implements Expression
    method __construct (line 39) | public function __construct(array $disjuncts = array())
    method getDisjuncts (line 58) | public function getDisjuncts()
    method orX (line 63) | public function orX(Expression $expr)
    method orNot (line 88) | public function orNot(Expression $expr)
    method orTrue (line 93) | public function orTrue()
    method orFalse (line 98) | public function orFalse()
    method orKey (line 103) | public function orKey($keyName, Expression $expr)
    method orMethod (line 108) | public function orMethod($methodName, $args)
    method orProperty (line 113) | public function orProperty($propertyName, Expression $expr)
    method orAtLeast (line 118) | public function orAtLeast($count, Expression $expr)
    method orAtMost (line 123) | public function orAtMost($count, Expression $expr)
    method orExactly (line 128) | public function orExactly($count, Expression $expr)
    method orCount (line 133) | public function orCount(Expression $expr)
    method orAll (line 138) | public function orAll(Expression $expr)
    method orNull (line 143) | public function orNull()
    method orNotNull (line 148) | public function orNotNull()
    method orEmpty (line 153) | public function orEmpty()
    method orNotEmpty (line 158) | public function orNotEmpty()
    method orInstanceOf (line 163) | public function orInstanceOf($className)
    method orEquals (line 168) | public function orEquals($value)
    method orNotEquals (line 173) | public function orNotEquals($value)
    method orSame (line 178) | public function orSame($value)
    method orNotSame (line 183) | public function orNotSame($value)
    method orGreaterThan (line 188) | public function orGreaterThan($value)
    method orGreaterThanEqual (line 193) | public function orGreaterThanEqual($value)
    method orLessThan (line 198) | public function orLessThan($value)
    method orLessThanEqual (line 203) | public function orLessThanEqual($value)
    method orIn (line 208) | public function orIn(array $values)
    method orMatches (line 213) | public function orMatches($regExp)
    method orStartsWith (line 218) | public function orStartsWith($prefix)
    method orEndsWith (line 223) | public function orEndsWith($suffix)
    method orContains (line 228) | public function orContains($string)
    method orKeyExists (line 233) | public function orKeyExists($keyName)
    method orKeyNotExists (line 238) | public function orKeyNotExists($keyName)
    method evaluate (line 246) | public function evaluate($values)
    method equivalentTo (line 260) | public function equivalentTo(Expression $other)
    method toString (line 289) | public function toString()
    method __toString (line 296) | public function __toString()

FILE: src/PhpUnit/ExpressionComparator.php
  class ExpressionComparator (line 25) | class ExpressionComparator extends Comparator
    method accepts (line 30) | public function accepts($expected, $actual)
    method assertEquals (line 38) | public function assertEquals($expected, $actual, $delta = 0.0, $canoni...

FILE: src/Selector/All.php
  class All (line 23) | class All extends Selector
    method evaluate (line 28) | public function evaluate($value)
    method toString (line 46) | public function toString()

FILE: src/Selector/AtLeast.php
  class AtLeast (line 24) | class AtLeast extends Selector
    method __construct (line 38) | public function __construct($count, Expression $expr)
    method evaluate (line 48) | public function evaluate($value)
    method equivalentTo (line 72) | public function equivalentTo(Expression $other)
    method toString (line 85) | public function toString()

FILE: src/Selector/AtMost.php
  class AtMost (line 24) | class AtMost extends Selector
    method __construct (line 38) | public function __construct($count, Expression $expr)
    method evaluate (line 48) | public function evaluate($value)
    method equivalentTo (line 72) | public function equivalentTo(Expression $other)
    method toString (line 85) | public function toString()

FILE: src/Selector/Count.php
  class Count (line 23) | class Count extends Selector
    method evaluate (line 28) | public function evaluate($value)
    method toString (line 40) | public function toString()

FILE: src/Selector/Exactly.php
  class Exactly (line 24) | class Exactly extends Selector
    method __construct (line 38) | public function __construct($count, Expression $expr)
    method evaluate (line 48) | public function evaluate($value)
    method equivalentTo (line 72) | public function equivalentTo(Expression $other)
    method toString (line 85) | public function toString()

FILE: src/Selector/Key.php
  class Key (line 25) | class Key extends Selector
    method __construct (line 38) | public function __construct($key, Expression $expr)
    method getKey (line 50) | public function getKey()
    method evaluate (line 58) | public function evaluate($value)
    method equivalentTo (line 74) | public function equivalentTo(Expression $other)
    method toString (line 87) | public function toString()

FILE: src/Selector/Method.php
  class Method (line 26) | class Method extends Selector
    method __construct (line 45) | public function __construct($methodName, array $arguments, Expression ...
    method getMethodName (line 58) | public function getMethodName()
    method getArguments (line 68) | public function getArguments()
    method evaluate (line 76) | public function evaluate($value)
    method equivalentTo (line 92) | public function equivalentTo(Expression $other)
    method toString (line 105) | public function toString()

FILE: src/Selector/Property.php
  class Property (line 25) | class Property extends Selector
    method __construct (line 38) | public function __construct($propertyName, Expression $expr)
    method getPropertyName (line 50) | public function getPropertyName()
    method evaluate (line 58) | public function evaluate($value)
    method equivalentTo (line 76) | public function equivalentTo(Expression $other)
    method toString (line 89) | public function toString()

FILE: src/Selector/Selector.php
  class Selector (line 26) | abstract class Selector extends Literal
    method __construct (line 39) | public function __construct(Expression $expr)
    method getExpression (line 49) | public function getExpression()
    method equivalentTo (line 57) | public function equivalentTo(Expression $other)

FILE: src/Traversal/ExpressionTraverser.php
  class ExpressionTraverser (line 30) | class ExpressionTraverser
    method addVisitor (line 44) | public function addVisitor(ExpressionVisitor $visitor)
    method removeVisitor (line 56) | public function removeVisitor(ExpressionVisitor $visitor)
    method getVisitors (line 70) | public function getVisitors()
    method traverse (line 83) | public function traverse(Expression $expr)
    method traverseForVisitor (line 98) | private function traverseForVisitor(Expression $expr, ExpressionVisito...
    method traverseKey (line 119) | private function traverseKey(Key $expr)
    method traverseNot (line 131) | private function traverseNot(Not $expr)
    method traverseConjunction (line 143) | private function traverseConjunction(AndX $expr)
    method traverseDisjunction (line 161) | private function traverseDisjunction(OrX $expr)

FILE: src/Traversal/ExpressionVisitor.php
  type ExpressionVisitor (line 26) | interface ExpressionVisitor
    method enterExpression (line 35) | public function enterExpression(Expression $expr);
    method leaveExpression (line 45) | public function leaveExpression(Expression $expr);

FILE: src/Util/StringUtil.php
  class StringUtil (line 21) | class StringUtil
    method formatValue (line 30) | public static function formatValue($value)
    method formatValues (line 66) | public static function formatValues(array $values)
    method __construct (line 78) | private function __construct()

FILE: tests/Comparison/ContainsTest.php
  class ContainsTest (line 23) | class ContainsTest extends PHPUnit_Framework_TestCase
    method testEvaluate (line 25) | public function testEvaluate()
    method testToString (line 38) | public function testToString()

FILE: tests/Comparison/EndsWithTest.php
  class EndsWithTest (line 22) | class EndsWithTest extends PHPUnit_Framework_TestCase
    method testEvaluate (line 24) | public function testEvaluate()
    method testToString (line 32) | public function testToString()

FILE: tests/Comparison/EqualsTest.php
  class EqualsTest (line 22) | class EqualsTest extends PHPUnit_Framework_TestCase
    method testEvaluate (line 24) | public function testEvaluate()
    method testToString (line 35) | public function testToString()

FILE: tests/Comparison/GreaterThanEqualTest.php
  class GreaterThanEqualTest (line 22) | class GreaterThanEqualTest extends PHPUnit_Framework_TestCase
    method testEvaluate (line 24) | public function testEvaluate()
    method testToString (line 35) | public function testToString()

FILE: tests/Comparison/GreaterThanTest.php
  class GreaterThanTest (line 22) | class GreaterThanTest extends PHPUnit_Framework_TestCase
    method testEvaluate (line 24) | public function testEvaluate()
    method testToString (line 35) | public function testToString()

FILE: tests/Comparison/InTest.php
  class InTest (line 22) | class InTest extends PHPUnit_Framework_TestCase
    method testEvaluateStrict (line 24) | public function testEvaluateStrict()
    method testEvaluateNonStrict (line 36) | public function testEvaluateNonStrict()
    method testToString (line 48) | public function testToString()

FILE: tests/Comparison/IsEmptyTest.php
  class IsEmptyTest (line 22) | class IsEmptyTest extends PHPUnit_Framework_TestCase
    method testEvaluate (line 24) | public function testEvaluate()
    method testToString (line 36) | public function testToString()

FILE: tests/Comparison/IsInstanceOfTest.php
  class IsInstanceOfTest (line 24) | class IsInstanceOfTest extends PHPUnit_Framework_TestCase
    method testEvaluate (line 26) | public function testEvaluate()
    method testToString (line 37) | public function testToString()

FILE: tests/Comparison/KeyExistsTest.php
  class KeyExistsTest (line 22) | class KeyExistsTest extends PHPUnit_Framework_TestCase
    method testEvaluate (line 24) | public function testEvaluate()
    method testToString (line 33) | public function testToString()

FILE: tests/Comparison/KeyNotExistsTest.php
  class KeyNotExistsTest (line 22) | class KeyNotExistsTest extends PHPUnit_Framework_TestCase
    method testEvaluate (line 24) | public function testEvaluate()
    method testToString (line 33) | public function testToString()

FILE: tests/Comparison/LessThanEqualTest.php
  class LessThanEqualTest (line 22) | class LessThanEqualTest extends PHPUnit_Framework_TestCase
    method testEvaluate (line 24) | public function testEvaluate()
    method testToString (line 35) | public function testToString()

FILE: tests/Comparison/LessThanTest.php
  class LessThanTest (line 22) | class LessThanTest extends PHPUnit_Framework_TestCase
    method testEvaluate (line 24) | public function testEvaluate()
    method testToString (line 35) | public function testToString()

FILE: tests/Comparison/MatchesTest.php
  class MatchesTest (line 22) | class MatchesTest extends PHPUnit_Framework_TestCase
    method testEvaluate (line 24) | public function testEvaluate()
    method testToString (line 34) | public function testToString()

FILE: tests/Comparison/NotEqualsTest.php
  class NotEqualsTest (line 22) | class NotEqualsTest extends PHPUnit_Framework_TestCase
    method testEvaluate (line 24) | public function testEvaluate()
    method testToString (line 35) | public function testToString()

FILE: tests/Comparison/NotSameTest.php
  class NotSameTest (line 22) | class NotSameTest extends PHPUnit_Framework_TestCase
    method testEvaluate (line 24) | public function testEvaluate()
    method testToString (line 35) | public function testToString()

FILE: tests/Comparison/SameTest.php
  class SameTest (line 22) | class SameTest extends PHPUnit_Framework_TestCase
    method testEvaluate (line 24) | public function testEvaluate()
    method testToString (line 35) | public function testToString()

FILE: tests/Comparison/StartsWithTest.php
  class StartsWithTest (line 22) | class StartsWithTest extends PHPUnit_Framework_TestCase
    method testEvaluate (line 24) | public function testEvaluate()
    method testToString (line 32) | public function testToString()

FILE: tests/DomainExpressionsTest.php
  class DomainExpressionsTest (line 25) | class DomainExpressionsTest extends PHPUnit_Framework_TestCase
    method testDomainExpressions (line 27) | public function testDomainExpressions()

FILE: tests/EquivalenceTest.php
  class EquivalenceTest (line 50) | class EquivalenceTest extends PHPUnit_Framework_TestCase
    method getEquivalentCriteria (line 52) | public function getEquivalentCriteria()
    method testEquivalence (line 134) | public function testEquivalence(Expression $left, Expression $right)
    method getNonEquivalentCriteria (line 140) | public function getNonEquivalentCriteria()
    method testNonEquivalence (line 248) | public function testNonEquivalence(Expression $left, Expression $right)

FILE: tests/ExprTest.php
  class ExprTest (line 53) | class ExprTest extends PHPUnit_Framework_TestCase
    method getComparisons (line 55) | public static function getComparisons()
    method getMethodTests (line 161) | public static function getMethodTests()
    method testCreate (line 232) | public function testCreate($method, $args, $expected)
    method testExpr (line 237) | public function testExpr()
    method testAndX (line 244) | public function testAndX()
    method testOrX (line 251) | public function testOrX()
    method testFilterArray (line 258) | public function testFilterArray()
    method testFilterCollection (line 268) | public function testFilterCollection()

FILE: tests/Fixtures/Customer.php
  class Customer (line 14) | class Customer
    method setPremium (line 20) | public function setPremium($premium)
    method isPremium (line 25) | public function isPremium()
    method setBookings (line 30) | public function setBookings(array $bookings)
    method getBookings (line 35) | public function getBookings()

FILE: tests/Fixtures/HasPreviousBookings.php
  class HasPreviousBookings (line 17) | class HasPreviousBookings extends Method
    method __construct (line 19) | public function __construct()

FILE: tests/Fixtures/IsPremium.php
  class IsPremium (line 17) | class IsPremium extends Method
    method __construct (line 19) | public function __construct()

FILE: tests/Logic/AlwaysFalseTest.php
  class AlwaysFalseTest (line 24) | class AlwaysFalseTest extends PHPUnit_Framework_TestCase
    method testEvaluate (line 26) | public function testEvaluate()
    method testToString (line 35) | public function testToString()
    method testAndReturnsFalse (line 42) | public function testAndReturnsFalse()
    method testOrReturnsConjunct (line 50) | public function testOrReturnsConjunct()

FILE: tests/Logic/AlwaysTrueTest.php
  class AlwaysTrueTest (line 24) | class AlwaysTrueTest extends PHPUnit_Framework_TestCase
    method testEvaluate (line 26) | public function testEvaluate()
    method testToString (line 35) | public function testToString()
    method testAndReturnsConjunct (line 42) | public function testAndReturnsConjunct()
    method testOrReturnsTrue (line 50) | public function testOrReturnsTrue()

FILE: tests/Logic/ConjunctionTest.php
  class ConjunctionTest (line 29) | class ConjunctionTest extends PHPUnit_Framework_TestCase
    method testCreate (line 31) | public function testCreate()
    method testCreateInlinesConjunction (line 41) | public function testCreateInlinesConjunction()
    method testAndX (line 51) | public function testAndX()
    method testAndXIgnoresDuplicates (line 63) | public function testAndXIgnoresDuplicates()
    method testAndXInlinesConjunctions (line 71) | public function testAndXInlinesConjunctions()
    method testAndTrueIgnored (line 83) | public function testAndTrueIgnored()
    method testAndXIgnoresTrue (line 91) | public function testAndXIgnoresTrue()
    method testAndFalseReturnsFalse (line 99) | public function testAndFalseReturnsFalse()
    method testAndXReturnsFalse (line 107) | public function testAndXReturnsFalse()
    method testAnd (line 118) | public function testAnd($method, $args, $expected)
    method testEvaluate (line 138) | public function testEvaluate()
    method testEquivalentTo (line 151) | public function testEquivalentTo()
    method testToString (line 176) | public function testToString()

FILE: tests/Logic/DisjunctionTest.php
  class DisjunctionTest (line 30) | class DisjunctionTest extends PHPUnit_Framework_TestCase
    method testCreate (line 32) | public function testCreate()
    method testCreateInlinesDisjunctions (line 42) | public function testCreateInlinesDisjunctions()
    method testOrX (line 52) | public function testOrX()
    method testOrXIgnoresDuplicates (line 64) | public function testOrXIgnoresDuplicates()
    method testOrXInlinesDisjunctions (line 72) | public function testOrXInlinesDisjunctions()
    method testOrFalseIgnored (line 86) | public function testOrFalseIgnored()
    method testOrXIgnoresFalse (line 94) | public function testOrXIgnoresFalse()
    method testOrTrueReturnsTrue (line 102) | public function testOrTrueReturnsTrue()
    method testOrXReturnsTrue (line 110) | public function testOrXReturnsTrue()
    method testOr (line 121) | public function testOr($method, $args, $expected)
    method testEvaluate (line 141) | public function testEvaluate()
    method testEquivalentTo (line 154) | public function testEquivalentTo()
    method testToString (line 179) | public function testToString()

FILE: tests/Logic/Fixtures/TestLiteral.php
  class TestLiteral (line 22) | class TestLiteral extends Literal
    method __construct (line 26) | public function __construct($value = null)
    method evaluate (line 31) | public function evaluate($value)
    method equivalentTo (line 35) | public function equivalentTo(Expression $other)
    method toString (line 40) | public function toString()

FILE: tests/Logic/LiteralTest.php
  class LiteralTest (line 28) | class LiteralTest extends PHPUnit_Framework_TestCase
    method testAndX (line 30) | public function testAndX()
    method testAndXIgnoresDuplicates (line 38) | public function testAndXIgnoresDuplicates()
    method testAndTrueIgnored (line 45) | public function testAndTrueIgnored()
    method testAndXIgnoresTrue (line 53) | public function testAndXIgnoresTrue()
    method testAndFalseReturnsFalse (line 61) | public function testAndFalseReturnsFalse()
    method testAndXReturnsFalse (line 69) | public function testAndXReturnsFalse()
    method testAnd (line 80) | public function testAnd($method, $args, $expected)
    method testOrX (line 99) | public function testOrX()
    method testOrXIgnoresDuplicates (line 107) | public function testOrXIgnoresDuplicates()
    method testOrFalseIgnored (line 114) | public function testOrFalseIgnored()
    method testOrXIgnoresFalse (line 122) | public function testOrXIgnoresFalse()
    method testOrTrueReturnsTrue (line 130) | public function testOrTrueReturnsTrue()
    method testOrXReturnsTrue (line 138) | public function testOrXReturnsTrue()
    method testOr (line 149) | public function testOr($method, $args, $expected)

FILE: tests/Logic/NotTest.php
  class NotTest (line 26) | class NotTest extends PHPUnit_Framework_TestCase
    method testEvaluate (line 28) | public function testEvaluate()
    method testEquivalentTo (line 36) | public function testEquivalentTo()
    method testToString (line 47) | public function testToString()

FILE: tests/Selector/AllTest.php
  class AllTest (line 26) | class AllTest extends PHPUnit_Framework_TestCase
    method testEvaluate (line 28) | public function testEvaluate()
    method testToString (line 45) | public function testToString()

FILE: tests/Selector/AtLeastTest.php
  class AtLeastTest (line 26) | class AtLeastTest extends PHPUnit_Framework_TestCase
    method testEvaluate (line 28) | public function testEvaluate()
    method testToString (line 51) | public function testToString()

FILE: tests/Selector/AtMostTest.php
  class AtMostTest (line 26) | class AtMostTest extends PHPUnit_Framework_TestCase
    method testEvaluate (line 28) | public function testEvaluate()
    method testToString (line 53) | public function testToString()

FILE: tests/Selector/CountTest.php
  class CountTest (line 24) | class CountTest extends PHPUnit_Framework_TestCase
    method testEvaluate (line 26) | public function testEvaluate()
    method testToString (line 41) | public function testToString()

FILE: tests/Selector/ExactlyTest.php
  class ExactlyTest (line 26) | class ExactlyTest extends PHPUnit_Framework_TestCase
    method testEvaluate (line 28) | public function testEvaluate()
    method testToString (line 51) | public function testToString()

FILE: tests/Selector/Fixtures/TestSelector.php
  class TestSelector (line 22) | class TestSelector extends Selector
    method __construct (line 29) | public function __construct($key, Expression $expr)
    method evaluate (line 39) | public function evaluate($value)
    method toString (line 47) | public function toString()

FILE: tests/Selector/KeyTest.php
  class KeyTest (line 25) | class KeyTest extends PHPUnit_Framework_TestCase
    method testEvaluate (line 27) | public function testEvaluate()
    method testToString (line 37) | public function testToString()

FILE: tests/Selector/MethodTest.php
  class MethodTest (line 26) | class MethodTest extends PHPUnit_Framework_TestCase
    method testEvaluate (line 28) | public function testEvaluate()
    method testEvaluateWithArguments (line 38) | public function testEvaluateWithArguments()
    method testToString (line 46) | public function testToString()
  class MethodTest_TestClass (line 61) | class MethodTest_TestClass
    method __construct (line 65) | public function __construct($foo)
    method getFoo (line 70) | public function getFoo()
    method getBar (line 75) | public function getBar($x)

FILE: tests/Selector/PropertyTest.php
  class PropertyTest (line 25) | class PropertyTest extends PHPUnit_Framework_TestCase
    method testEvaluate (line 27) | public function testEvaluate()
    method testToString (line 38) | public function testToString()

FILE: tests/Selector/SelectorTest.php
  class SelectorTest (line 23) | class SelectorTest extends PHPUnit_Framework_TestCase
    method testGetExpression (line 25) | public function testGetExpression()
    method testEquivalentTo (line 32) | public function testEquivalentTo()

FILE: tests/Traversal/ExpressionTraverserTest.php
  class ExpressionTraverserTest (line 28) | class ExpressionTraverserTest extends PHPUnit_Framework_TestCase
    method setUp (line 35) | protected function setUp()
    method testAddVisitor (line 40) | public function testAddVisitor()
    method testRemoveVisitor (line 52) | public function testRemoveVisitor()
    method testTraverse (line 65) | public function testTraverse()
    method testModifyExprInEnterExpression (line 84) | public function testModifyExprInEnterExpression()
    method testModifyExprInLeaveExpression (line 104) | public function testModifyExprInLeaveExpression()
    method testRemoveExpr (line 124) | public function testRemoveExpr()
    method testTraverseMultipleVisitors (line 143) | public function testTraverseMultipleVisitors()
    method testTraverseSkipsSubsequentVisitorsIfExpressionRemoved (line 174) | public function testTraverseSkipsSubsequentVisitorsIfExpressionRemoved()
    method testTraverseNot (line 200) | public function testTraverseNot()
    method testModifyNotChildInEnterExpression (line 227) | public function testModifyNotChildInEnterExpression()
    method testModifyNotChildInLeaveExpression (line 255) | public function testModifyNotChildInLeaveExpression()
    method testRemoveNotChild (line 283) | public function testRemoveNotChild()
    method testTraverseKey (line 306) | public function testTraverseKey()
    method testModifyKeyChildInEnterExpression (line 333) | public function testModifyKeyChildInEnterExpression()
    method testModifyKeyChildInLeaveExpression (line 361) | public function testModifyKeyChildInLeaveExpression()
    method testRemoveKeyChild (line 389) | public function testRemoveKeyChild()
    method testTraverseConjunction (line 412) | public function testTraverseConjunction()
    method testModifyConjunctInEnterExpression (line 450) | public function testModifyConjunctInEnterExpression()
    method testModifyConjunctInLeaveExpression (line 492) | public function testModifyConjunctInLeaveExpression()
    method testRemoveConjunct (line 534) | public function testRemoveConjunct()
    method testRemoveAllConjuncts (line 573) | public function testRemoveAllConjuncts()
    method testTraverseDisjunction (line 607) | public function testTraverseDisjunction()
    method testModifyDisjunctInEnterExpression (line 645) | public function testModifyDisjunctInEnterExpression()
    method testModifyDisjunctInLeaveExpression (line 687) | public function testModifyDisjunctInLeaveExpression()
    method testRemoveDisjunct (line 729) | public function testRemoveDisjunct()
    method testRemoveAllDisjuncts (line 768) | public function testRemoveAllDisjuncts()
Condensed preview — 89 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (237K chars).
[
  {
    "path": ".composer-auth.json",
    "chars": 267,
    "preview": "{\n    \"github-oauth\": {\n        \"github.com\": \"PLEASE DO NOT USE THIS TOKEN IN YOUR OWN PROJECTS/FORKS\",\n        \"github"
  },
  {
    "path": ".gitignore",
    "chars": 23,
    "preview": "/vendor/\ncomposer.lock\n"
  },
  {
    "path": ".styleci.yml",
    "chars": 179,
    "preview": "preset: symfony\n\nenabled:\n    - ordered_use\n\ndisabled:\n    - empty_return\n    - phpdoc_annotation_without_dot # This is "
  },
  {
    "path": ".travis.yml",
    "chars": 1055,
    "preview": "language: php\n\nsudo: false\n\nbranches:\n  only:\n    - master\n\ncache:\n  directories:\n    - $HOME/.composer/cache/files\n\nmat"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 1837,
    "preview": "Changelog\n=========\n\n* 1.0.0 (2015-12-17)\n\n * added `Expr::filter()` to filter collections\n * removed `final` keyword of"
  },
  {
    "path": "LICENSE",
    "chars": 1084,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2014 Bernhard Schussek\n\nPermission is hereby granted, free of charge, to any person"
  },
  {
    "path": "README.md",
    "chars": 12721,
    "preview": "Webmozart Expression\n====================\n\n[![Build Status](https://travis-ci.org/webmozart/expression.svg?branch=master"
  },
  {
    "path": "appveyor.yml",
    "chars": 1404,
    "preview": "build: false\nplatform: x86\nclone_folder: c:\\projects\\webmozart\\expression\n\nbranches:\n  only:\n    - master\n\ncache:\n  - c:"
  },
  {
    "path": "composer.json",
    "chars": 840,
    "preview": "{\n    \"name\": \"webmozart/expression\",\n    \"description\": \"Implementation of the Specification pattern and logical expres"
  },
  {
    "path": "phpunit.xml.dist",
    "chars": 450,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<phpunit bootstrap=\"vendor/autoload.php\" colors=\"true\">\n    <testsuites>\n       "
  },
  {
    "path": "src/Constraint/Contains.php",
    "chars": 1672,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "src/Constraint/EndsWith.php",
    "chars": 1661,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "src/Constraint/Equals.php",
    "chars": 1797,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "src/Constraint/GreaterThan.php",
    "chars": 1660,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "src/Constraint/GreaterThanEqual.php",
    "chars": 1680,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "src/Constraint/In.php",
    "chars": 3192,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "src/Constraint/IsEmpty.php",
    "chars": 954,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "src/Constraint/IsInstanceOf.php",
    "chars": 1581,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "src/Constraint/KeyExists.php",
    "chars": 1562,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "src/Constraint/KeyNotExists.php",
    "chars": 1577,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "src/Constraint/LessThan.php",
    "chars": 1654,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "src/Constraint/LessThanEqual.php",
    "chars": 1674,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "src/Constraint/Matches.php",
    "chars": 1639,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "src/Constraint/NotEquals.php",
    "chars": 1658,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "src/Constraint/NotSame.php",
    "chars": 1665,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "src/Constraint/Same.php",
    "chars": 1807,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "src/Constraint/StartsWith.php",
    "chars": 1636,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "src/Expr.php",
    "chars": 13665,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "src/Expression.php",
    "chars": 1193,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "src/Logic/AlwaysFalse.php",
    "chars": 1132,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "src/Logic/AlwaysTrue.php",
    "chars": 1124,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "src/Logic/AndX.php",
    "chars": 6915,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "src/Logic/Literal.php",
    "chars": 8427,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "src/Logic/Not.php",
    "chars": 1590,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "src/Logic/OrX.php",
    "chars": 6851,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "src/PhpUnit/ExpressionComparator.php",
    "chars": 1859,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "src/Selector/All.php",
    "chars": 966,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "src/Selector/AtLeast.php",
    "chars": 1869,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "src/Selector/AtMost.php",
    "chars": 1865,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "src/Selector/Count.php",
    "chars": 863,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "src/Selector/Exactly.php",
    "chars": 1878,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "src/Selector/Key.php",
    "chars": 2133,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "src/Selector/Method.php",
    "chars": 2903,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "src/Selector/Property.php",
    "chars": 2325,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "src/Selector/Selector.php",
    "chars": 1441,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "src/Traversal/ExpressionTraverser.php",
    "chars": 4686,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "src/Traversal/ExpressionVisitor.php",
    "chars": 1219,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "src/Util/StringUtil.php",
    "chars": 1554,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "tests/Comparison/ContainsTest.php",
    "chars": 1195,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "tests/Comparison/EndsWithTest.php",
    "chars": 876,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "tests/Comparison/EqualsTest.php",
    "chars": 983,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "tests/Comparison/GreaterThanEqualTest.php",
    "chars": 1014,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "tests/Comparison/GreaterThanTest.php",
    "chars": 994,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "tests/Comparison/InTest.php",
    "chars": 1473,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "tests/Comparison/IsEmptyTest.php",
    "chars": 1031,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "tests/Comparison/IsInstanceOfTest.php",
    "chars": 1151,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "tests/Comparison/KeyExistsTest.php",
    "chars": 931,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "tests/Comparison/KeyNotExistsTest.php",
    "chars": 946,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "tests/Comparison/LessThanEqualTest.php",
    "chars": 1000,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "tests/Comparison/LessThanTest.php",
    "chars": 980,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "tests/Comparison/MatchesTest.php",
    "chars": 975,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "tests/Comparison/NotEqualsTest.php",
    "chars": 996,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "tests/Comparison/NotSameTest.php",
    "chars": 987,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "tests/Comparison/SameTest.php",
    "chars": 978,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "tests/Comparison/StartsWithTest.php",
    "chars": 899,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "tests/DomainExpressionsTest.php",
    "chars": 1384,
    "preview": "<?php\n\n/*\n * This file is part of the vendor/project package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *\n * Fo"
  },
  {
    "path": "tests/EquivalenceTest.php",
    "chars": 10653,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "tests/ExprTest.php",
    "chars": 7612,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "tests/Fixtures/Customer.php",
    "chars": 728,
    "preview": "<?php\n\n/*\n * This file is part of the vendor/project package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *\n * Fo"
  },
  {
    "path": "tests/Fixtures/HasPreviousBookings.php",
    "chars": 548,
    "preview": "<?php\n\n/*\n * This file is part of the vendor/project package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *\n * Fo"
  },
  {
    "path": "tests/Fixtures/IsPremium.php",
    "chars": 519,
    "preview": "<?php\n\n/*\n * This file is part of the vendor/project package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *\n * Fo"
  },
  {
    "path": "tests/Logic/AlwaysFalseTest.php",
    "chars": 1308,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "tests/Logic/AlwaysTrueTest.php",
    "chars": 1297,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "tests/Logic/ConjunctionTest.php",
    "chars": 6278,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "tests/Logic/DisjunctionTest.php",
    "chars": 6407,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "tests/Logic/Fixtures/TestLiteral.php",
    "chars": 844,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "tests/Logic/LiteralTest.php",
    "chars": 4440,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "tests/Logic/NotTest.php",
    "chars": 1673,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "tests/Selector/AllTest.php",
    "chars": 1903,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "tests/Selector/AtLeastTest.php",
    "chars": 2372,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "tests/Selector/AtMostTest.php",
    "chars": 2508,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "tests/Selector/CountTest.php",
    "chars": 1329,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "tests/Selector/ExactlyTest.php",
    "chars": 2374,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "tests/Selector/Fixtures/TestSelector.php",
    "chars": 925,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "tests/Selector/KeyTest.php",
    "chars": 1472,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "tests/Selector/MethodTest.php",
    "chars": 2191,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "tests/Selector/PropertyTest.php",
    "chars": 1586,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "tests/Selector/SelectorTest.php",
    "chars": 1516,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  },
  {
    "path": "tests/Traversal/ExpressionTraverserTest.php",
    "chars": 27695,
    "preview": "<?php\n\n/*\n * This file is part of the webmozart/expression package.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *"
  }
]

About this extraction

This page contains the full source code of the webmozart/expression GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 89 files (217.1 KB), approximately 57.7k tokens, and a symbol index with 571 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!