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 ``` 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 ================================================ ./tests/ ./src/ ================================================ FILE: src/Constraint/Contains.php ================================================ * * 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 * @author Stephan Wentz */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 * @author Stephan Wentz */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 ================================================ * * 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 ================================================ * * 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ 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 ================================================ * * 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 */ class KeyTest extends PHPUnit_Framework_TestCase { public function testEvaluate() { $expr = new Key('key', new GreaterThan(10)); $this->assertTrue($expr->evaluate(array('key' => 11))); $this->assertFalse($expr->evaluate(array('key' => 9))); $this->assertFalse($expr->evaluate(array())); $this->assertFalse($expr->evaluate('foobar')); } public function testToString() { $expr1 = new Key('name', new GreaterThan(10)); $expr2 = new Key('name', new EndsWith('.css')); $expr3 = new Key('name', new AndX(array( new GreaterThan(10), new EndsWith('.css'), ))); $this->assertSame('name>10', $expr1->toString()); $this->assertSame('name.endsWith(".css")', $expr2->toString()); $this->assertSame('name{>10 && endsWith(".css")}', $expr3->toString()); } } ================================================ FILE: tests/Selector/MethodTest.php ================================================ * * 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 stdClass; use Webmozart\Expression\Constraint\EndsWith; use Webmozart\Expression\Constraint\GreaterThan; use Webmozart\Expression\Logic\AndX; use Webmozart\Expression\Selector\Method; /** * @since 1.0 * * @author Bernhard Schussek */ class MethodTest extends PHPUnit_Framework_TestCase { public function testEvaluate() { $expr = new Method('getFoo', array(), new GreaterThan(10)); $this->assertTrue($expr->evaluate(new MethodTest_TestClass(11))); $this->assertFalse($expr->evaluate(new MethodTest_TestClass(9))); $this->assertFalse($expr->evaluate('foobar')); $this->assertFalse($expr->evaluate(new stdClass())); } public function testEvaluateWithArguments() { $expr = new Method('getBar', array(2), new GreaterThan(10)); $this->assertTrue($expr->evaluate(new MethodTest_TestClass(9))); $this->assertFalse($expr->evaluate(new MethodTest_TestClass(7))); } public function testToString() { $expr1 = new Method('getName', array(42, true), new GreaterThan(10)); $expr2 = new Method('getName', array('foo'), new EndsWith('.css')); $expr3 = new Method('getName', array(new stdClass()), new AndX(array( new GreaterThan(10), new EndsWith('.css'), ))); $this->assertSame('getName(42, true)>10', $expr1->toString()); $this->assertSame('getName("foo").endsWith(".css")', $expr2->toString()); $this->assertSame('getName(object){>10 && endsWith(".css")}', $expr3->toString()); } } class MethodTest_TestClass { private $foo; public function __construct($foo) { $this->foo = $foo; } public function getFoo() { return $this->foo; } public function getBar($x) { return $this->foo + $x; } } ================================================ FILE: tests/Selector/PropertyTest.php ================================================ * * 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\Property; /** * @since 1.0 * * @author Bernhard Schussek */ class PropertyTest extends PHPUnit_Framework_TestCase { public function testEvaluate() { $expr = new Property('prop', new GreaterThan(10)); $this->assertTrue($expr->evaluate((object) array('prop' => 11))); $this->assertFalse($expr->evaluate((object) array('prop' => 9))); $this->assertFalse($expr->evaluate((object) array())); $this->assertFalse($expr->evaluate(array())); $this->assertFalse($expr->evaluate('foobar')); } public function testToString() { $expr1 = new Property('prop', new GreaterThan(10)); $expr2 = new Property('prop', new EndsWith('.css')); $expr3 = new Property('prop', new AndX(array( new GreaterThan(10), new EndsWith('.css'), ))); $this->assertSame('prop>10', $expr1->toString()); $this->assertSame('prop.endsWith(".css")', $expr2->toString()); $this->assertSame('prop{>10 && endsWith(".css")}', $expr3->toString()); } } ================================================ FILE: tests/Selector/SelectorTest.php ================================================ * * 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\Same; use Webmozart\Expression\Tests\Selector\Fixtures\TestSelector; /** * @since 1.0 * * @author Bernhard Schussek */ class SelectorTest extends PHPUnit_Framework_TestCase { public function testGetExpression() { $selector = new TestSelector('key', $expr = new Same('10')); $this->assertSame($expr, $selector->getExpression()); } public function testEquivalentTo() { // "key" is ignored in the dummy implementation $selector1 = new TestSelector('key', new Same('10')); $selector2 = new TestSelector('key', new Same('10')); $selector3 = new TestSelector('key', new Same(10)); $this->assertTrue($selector1->equivalentTo($selector2)); $this->assertTrue($selector2->equivalentTo($selector1)); $this->assertFalse($selector1->equivalentTo($selector3)); $this->assertFalse($selector3->equivalentTo($selector1)); $this->assertFalse($selector2->equivalentTo($selector3)); $this->assertFalse($selector3->equivalentTo($selector2)); $this->assertFalse($selector1->equivalentTo(new Same('10'))); } } ================================================ FILE: tests/Traversal/ExpressionTraverserTest.php ================================================ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Webmozart\Expression\Tests\Traversal; use PHPUnit_Framework_TestCase; use Webmozart\Expression\Constraint\GreaterThan; use Webmozart\Expression\Constraint\Same; use Webmozart\Expression\Logic\AndX; use Webmozart\Expression\Logic\Not; use Webmozart\Expression\Logic\OrX; use Webmozart\Expression\Selector\Key; use Webmozart\Expression\Traversal\ExpressionTraverser; /** * @since 1.0 * * @author Bernhard Schussek */ class ExpressionTraverserTest extends PHPUnit_Framework_TestCase { /** * @var ExpressionTraverser */ private $traverser; protected function setUp() { $this->traverser = new ExpressionTraverser(); } public function testAddVisitor() { $visitor1 = $this->getMock('Webmozart\Expression\Traversal\ExpressionVisitor'); $visitor2 = $this->getMock('Webmozart\Expression\Traversal\ExpressionVisitor'); $this->traverser->addVisitor($visitor1); $this->traverser->addVisitor($visitor2); $this->traverser->addVisitor($visitor1); $this->assertSame(array($visitor1, $visitor2, $visitor1), $this->traverser->getVisitors()); } public function testRemoveVisitor() { $visitor1 = $this->getMock('Webmozart\Expression\Traversal\ExpressionVisitor'); $visitor2 = $this->getMock('Webmozart\Expression\Traversal\ExpressionVisitor'); $this->traverser->addVisitor($visitor1); $this->traverser->addVisitor($visitor2); $this->traverser->addVisitor($visitor1); $this->traverser->removeVisitor($visitor1); $this->assertSame(array($visitor2), $this->traverser->getVisitors()); } public function testTraverse() { $expr = new GreaterThan(10); $visitor = $this->getMock('Webmozart\Expression\Traversal\ExpressionVisitor'); $visitor->expects($this->at(0)) ->method('enterExpression') ->with($this->identicalTo($expr)) ->willReturn($expr); $visitor->expects($this->at(1)) ->method('leaveExpression') ->with($this->identicalTo($expr)) ->willReturn($expr); $this->traverser->addVisitor($visitor); $this->assertSame($expr, $this->traverser->traverse($expr)); } public function testModifyExprInEnterExpression() { $expr1 = new GreaterThan(10); $expr2 = new GreaterThan(5); $visitor = $this->getMock('Webmozart\Expression\Traversal\ExpressionVisitor'); $visitor->expects($this->at(0)) ->method('enterExpression') ->with($this->identicalTo($expr1)) ->willReturn($expr2); $visitor->expects($this->at(1)) ->method('leaveExpression') ->with($this->identicalTo($expr2)) ->willReturn($expr2); $this->traverser->addVisitor($visitor); $this->assertSame($expr2, $this->traverser->traverse($expr1)); } public function testModifyExprInLeaveExpression() { $expr1 = new GreaterThan(10); $expr2 = new GreaterThan(5); $visitor = $this->getMock('Webmozart\Expression\Traversal\ExpressionVisitor'); $visitor->expects($this->at(0)) ->method('enterExpression') ->with($this->identicalTo($expr1)) ->willReturn($expr1); $visitor->expects($this->at(1)) ->method('leaveExpression') ->with($this->identicalTo($expr1)) ->willReturn($expr2); $this->traverser->addVisitor($visitor); $this->assertSame($expr2, $this->traverser->traverse($expr1)); } public function testRemoveExpr() { $expr = new GreaterThan(10); $visitor = $this->getMock('Webmozart\Expression\Traversal\ExpressionVisitor'); $visitor->expects($this->at(0)) ->method('enterExpression') ->with($this->identicalTo($expr)) ->willReturn($expr); $visitor->expects($this->at(1)) ->method('leaveExpression') ->with($this->identicalTo($expr)) ->willReturn(null); $this->traverser->addVisitor($visitor); $this->assertNull($this->traverser->traverse($expr)); } public function testTraverseMultipleVisitors() { $expr1 = new GreaterThan(10); $expr2 = new GreaterThan(5); $visitor1 = $this->getMock('Webmozart\Expression\Traversal\ExpressionVisitor'); $visitor1->expects($this->at(0)) ->method('enterExpression') ->with($this->identicalTo($expr1)) ->willReturn($expr1); $visitor1->expects($this->at(1)) ->method('leaveExpression') ->with($this->identicalTo($expr1)) ->willReturn($expr2); $visitor2 = $this->getMock('Webmozart\Expression\Traversal\ExpressionVisitor'); $visitor2->expects($this->at(0)) ->method('enterExpression') ->with($this->identicalTo($expr2)) ->willReturn($expr2); $visitor2->expects($this->at(1)) ->method('leaveExpression') ->with($this->identicalTo($expr2)) ->willReturn($expr2); $this->traverser->addVisitor($visitor1); $this->traverser->addVisitor($visitor2); $this->assertSame($expr2, $this->traverser->traverse($expr1)); } public function testTraverseSkipsSubsequentVisitorsIfExpressionRemoved() { $expr = new GreaterThan(10); $visitor1 = $this->getMock('Webmozart\Expression\Traversal\ExpressionVisitor'); $visitor1->expects($this->at(0)) ->method('enterExpression') ->with($this->identicalTo($expr)) ->willReturn($expr); $visitor1->expects($this->at(1)) ->method('leaveExpression') ->with($this->identicalTo($expr)) ->willReturn(null); $visitor2 = $this->getMock('Webmozart\Expression\Traversal\ExpressionVisitor'); $visitor2->expects($this->never()) ->method('enterExpression'); $visitor2->expects($this->never()) ->method('leaveExpression'); $this->traverser->addVisitor($visitor1); $this->traverser->addVisitor($visitor2); $this->assertNull($this->traverser->traverse($expr)); } public function testTraverseNot() { $expr = new Not($gt = new GreaterThan(10)); $visitor = $this->getMock('Webmozart\Expression\Traversal\ExpressionVisitor'); $visitor->expects($this->at(0)) ->method('enterExpression') ->with($this->identicalTo($expr)) ->willReturn($expr); $visitor->expects($this->at(1)) ->method('enterExpression') ->with($this->identicalTo($gt)) ->willReturn($gt); $visitor->expects($this->at(2)) ->method('leaveExpression') ->with($this->identicalTo($gt)) ->willReturn($gt); $visitor->expects($this->at(3)) ->method('leaveExpression') ->with($this->identicalTo($expr)) ->willReturn($expr); $this->traverser->addVisitor($visitor); $this->assertSame($expr, $this->traverser->traverse($expr)); } public function testModifyNotChildInEnterExpression() { $expr1 = new Not($gt1 = new GreaterThan(10)); $expr2 = new Not($gt2 = new GreaterThan(5)); $visitor = $this->getMock('Webmozart\Expression\Traversal\ExpressionVisitor'); $visitor->expects($this->at(0)) ->method('enterExpression') ->with($this->identicalTo($expr1)) ->willReturn($expr1); $visitor->expects($this->at(1)) ->method('enterExpression') ->with($this->identicalTo($gt1)) ->willReturn($gt2); $visitor->expects($this->at(2)) ->method('leaveExpression') ->with($this->identicalTo($gt2)) ->willReturn($gt2); $visitor->expects($this->at(3)) ->method('leaveExpression') ->with($this->equalTo($expr2)) ->willReturn($expr2); $this->traverser->addVisitor($visitor); $this->assertSame($expr2, $this->traverser->traverse($expr1)); } public function testModifyNotChildInLeaveExpression() { $expr1 = new Not($gt1 = new GreaterThan(10)); $expr2 = new Not($gt2 = new GreaterThan(5)); $visitor = $this->getMock('Webmozart\Expression\Traversal\ExpressionVisitor'); $visitor->expects($this->at(0)) ->method('enterExpression') ->with($this->identicalTo($expr1)) ->willReturn($expr1); $visitor->expects($this->at(1)) ->method('enterExpression') ->with($this->identicalTo($gt1)) ->willReturn($gt1); $visitor->expects($this->at(2)) ->method('leaveExpression') ->with($this->identicalTo($gt1)) ->willReturn($gt2); $visitor->expects($this->at(3)) ->method('leaveExpression') ->with($this->equalTo($expr2)) ->willReturn($expr2); $this->traverser->addVisitor($visitor); $this->assertSame($expr2, $this->traverser->traverse($expr1)); } public function testRemoveNotChild() { $expr1 = new Not($gt1 = new GreaterThan(10)); $visitor = $this->getMock('Webmozart\Expression\Traversal\ExpressionVisitor'); $visitor->expects($this->at(0)) ->method('enterExpression') ->with($this->identicalTo($expr1)) ->willReturn($expr1); $visitor->expects($this->at(1)) ->method('enterExpression') ->with($this->identicalTo($gt1)) ->willReturn($gt1); $visitor->expects($this->at(2)) ->method('leaveExpression') ->with($this->identicalTo($gt1)) ->willReturn(null); $this->traverser->addVisitor($visitor); $this->assertNull($this->traverser->traverse($expr1)); } public function testTraverseKey() { $expr = new Key('key', $gt = new GreaterThan(10)); $visitor = $this->getMock('Webmozart\Expression\Traversal\ExpressionVisitor'); $visitor->expects($this->at(0)) ->method('enterExpression') ->with($this->identicalTo($expr)) ->willReturn($expr); $visitor->expects($this->at(1)) ->method('enterExpression') ->with($this->identicalTo($gt)) ->willReturn($gt); $visitor->expects($this->at(2)) ->method('leaveExpression') ->with($this->identicalTo($gt)) ->willReturn($gt); $visitor->expects($this->at(3)) ->method('leaveExpression') ->with($this->identicalTo($expr)) ->willReturn($expr); $this->traverser->addVisitor($visitor); $this->assertSame($expr, $this->traverser->traverse($expr)); } public function testModifyKeyChildInEnterExpression() { $expr1 = new Key('key', $gt1 = new GreaterThan(10)); $expr2 = new Key('key', $gt2 = new GreaterThan(5)); $visitor = $this->getMock('Webmozart\Expression\Traversal\ExpressionVisitor'); $visitor->expects($this->at(0)) ->method('enterExpression') ->with($this->identicalTo($expr1)) ->willReturn($expr1); $visitor->expects($this->at(1)) ->method('enterExpression') ->with($this->identicalTo($gt1)) ->willReturn($gt2); $visitor->expects($this->at(2)) ->method('leaveExpression') ->with($this->identicalTo($gt2)) ->willReturn($gt2); $visitor->expects($this->at(3)) ->method('leaveExpression') ->with($this->equalTo($expr2)) ->willReturn($expr2); $this->traverser->addVisitor($visitor); $this->assertSame($expr2, $this->traverser->traverse($expr1)); } public function testModifyKeyChildInLeaveExpression() { $expr1 = new Key('key', $gt1 = new GreaterThan(10)); $expr2 = new Key('key', $gt2 = new GreaterThan(5)); $visitor = $this->getMock('Webmozart\Expression\Traversal\ExpressionVisitor'); $visitor->expects($this->at(0)) ->method('enterExpression') ->with($this->identicalTo($expr1)) ->willReturn($expr1); $visitor->expects($this->at(1)) ->method('enterExpression') ->with($this->identicalTo($gt1)) ->willReturn($gt1); $visitor->expects($this->at(2)) ->method('leaveExpression') ->with($this->identicalTo($gt1)) ->willReturn($gt2); $visitor->expects($this->at(3)) ->method('leaveExpression') ->with($this->equalTo($expr2)) ->willReturn($expr2); $this->traverser->addVisitor($visitor); $this->assertSame($expr2, $this->traverser->traverse($expr1)); } public function testRemoveKeyChild() { $expr1 = new Key('key', $gt1 = new GreaterThan(10)); $visitor = $this->getMock('Webmozart\Expression\Traversal\ExpressionVisitor'); $visitor->expects($this->at(0)) ->method('enterExpression') ->with($this->identicalTo($expr1)) ->willReturn($expr1); $visitor->expects($this->at(1)) ->method('enterExpression') ->with($this->identicalTo($gt1)) ->willReturn($gt1); $visitor->expects($this->at(2)) ->method('leaveExpression') ->with($this->identicalTo($gt1)) ->willReturn(null); $this->traverser->addVisitor($visitor); $this->assertNull($this->traverser->traverse($expr1)); } public function testTraverseConjunction() { $expr = new AndX(array( $gt = new GreaterThan(10), $same = new Same('5'), )); $visitor = $this->getMock('Webmozart\Expression\Traversal\ExpressionVisitor'); $visitor->expects($this->at(0)) ->method('enterExpression') ->with($this->identicalTo($expr)) ->willReturn($expr); $visitor->expects($this->at(1)) ->method('enterExpression') ->with($this->identicalTo($gt)) ->willReturn($gt); $visitor->expects($this->at(2)) ->method('leaveExpression') ->with($this->identicalTo($gt)) ->willReturn($gt); $visitor->expects($this->at(3)) ->method('enterExpression') ->with($this->identicalTo($same)) ->willReturn($same); $visitor->expects($this->at(4)) ->method('leaveExpression') ->with($this->identicalTo($same)) ->willReturn($same); $visitor->expects($this->at(5)) ->method('leaveExpression') ->with($this->identicalTo($expr)) ->willReturn($expr); $this->traverser->addVisitor($visitor); $this->assertSame($expr, $this->traverser->traverse($expr)); } public function testModifyConjunctInEnterExpression() { $expr1 = new AndX(array( $gt1 = new GreaterThan(10), $same = new Same('5'), )); $expr2 = new AndX(array( $gt2 = new GreaterThan(5), $same, )); $visitor = $this->getMock('Webmozart\Expression\Traversal\ExpressionVisitor'); $visitor->expects($this->at(0)) ->method('enterExpression') ->with($this->identicalTo($expr1)) ->willReturn($expr1); $visitor->expects($this->at(1)) ->method('enterExpression') ->with($this->identicalTo($gt1)) ->willReturn($gt2); $visitor->expects($this->at(2)) ->method('leaveExpression') ->with($this->identicalTo($gt2)) ->willReturn($gt2); $visitor->expects($this->at(3)) ->method('enterExpression') ->with($this->identicalTo($same)) ->willReturn($same); $visitor->expects($this->at(4)) ->method('leaveExpression') ->with($this->identicalTo($same)) ->willReturn($same); $visitor->expects($this->at(5)) ->method('leaveExpression') ->with($this->equalTo($expr2)) ->willReturn($expr2); $this->traverser->addVisitor($visitor); $this->assertSame($expr2, $this->traverser->traverse($expr1)); } public function testModifyConjunctInLeaveExpression() { $expr1 = new AndX(array( $gt1 = new GreaterThan(10), $same = new Same('5'), )); $expr2 = new AndX(array( $gt2 = new GreaterThan(5), $same, )); $visitor = $this->getMock('Webmozart\Expression\Traversal\ExpressionVisitor'); $visitor->expects($this->at(0)) ->method('enterExpression') ->with($this->identicalTo($expr1)) ->willReturn($expr1); $visitor->expects($this->at(1)) ->method('enterExpression') ->with($this->identicalTo($gt1)) ->willReturn($gt1); $visitor->expects($this->at(2)) ->method('leaveExpression') ->with($this->identicalTo($gt1)) ->willReturn($gt2); $visitor->expects($this->at(3)) ->method('enterExpression') ->with($this->identicalTo($same)) ->willReturn($same); $visitor->expects($this->at(4)) ->method('leaveExpression') ->with($this->identicalTo($same)) ->willReturn($same); $visitor->expects($this->at(5)) ->method('leaveExpression') ->with($this->equalTo($expr2)) ->willReturn($expr2); $this->traverser->addVisitor($visitor); $this->assertSame($expr2, $this->traverser->traverse($expr1)); } public function testRemoveConjunct() { $expr1 = new AndX(array( $gt1 = new GreaterThan(10), $same = new Same('5'), )); $expr2 = new AndX(array($same)); $visitor = $this->getMock('Webmozart\Expression\Traversal\ExpressionVisitor'); $visitor->expects($this->at(0)) ->method('enterExpression') ->with($this->identicalTo($expr1)) ->willReturn($expr1); $visitor->expects($this->at(1)) ->method('enterExpression') ->with($this->identicalTo($gt1)) ->willReturn($gt1); $visitor->expects($this->at(2)) ->method('leaveExpression') ->with($this->identicalTo($gt1)) ->willReturn(null); $visitor->expects($this->at(3)) ->method('enterExpression') ->with($this->identicalTo($same)) ->willReturn($same); $visitor->expects($this->at(4)) ->method('leaveExpression') ->with($this->identicalTo($same)) ->willReturn($same); $visitor->expects($this->at(5)) ->method('leaveExpression') ->with($this->equalTo($expr2)) ->willReturn($expr2); $this->traverser->addVisitor($visitor); $this->assertSame($expr2, $this->traverser->traverse($expr1)); } public function testRemoveAllConjuncts() { $expr1 = new AndX(array( $gt1 = new GreaterThan(10), $same = new Same('5'), )); $visitor = $this->getMock('Webmozart\Expression\Traversal\ExpressionVisitor'); $visitor->expects($this->at(0)) ->method('enterExpression') ->with($this->identicalTo($expr1)) ->willReturn($expr1); $visitor->expects($this->at(1)) ->method('enterExpression') ->with($this->identicalTo($gt1)) ->willReturn($gt1); $visitor->expects($this->at(2)) ->method('leaveExpression') ->with($this->identicalTo($gt1)) ->willReturn(null); $visitor->expects($this->at(3)) ->method('enterExpression') ->with($this->identicalTo($same)) ->willReturn($same); $visitor->expects($this->at(4)) ->method('leaveExpression') ->with($this->identicalTo($same)) ->willReturn(null); $this->traverser->addVisitor($visitor); $this->assertNull($this->traverser->traverse($expr1)); } public function testTraverseDisjunction() { $expr = new OrX(array( $gt = new GreaterThan(10), $same = new Same('5'), )); $visitor = $this->getMock('Webmozart\Expression\Traversal\ExpressionVisitor'); $visitor->expects($this->at(0)) ->method('enterExpression') ->with($this->identicalTo($expr)) ->willReturn($expr); $visitor->expects($this->at(1)) ->method('enterExpression') ->with($this->identicalTo($gt)) ->willReturn($gt); $visitor->expects($this->at(2)) ->method('leaveExpression') ->with($this->identicalTo($gt)) ->willReturn($gt); $visitor->expects($this->at(3)) ->method('enterExpression') ->with($this->identicalTo($same)) ->willReturn($same); $visitor->expects($this->at(4)) ->method('leaveExpression') ->with($this->identicalTo($same)) ->willReturn($same); $visitor->expects($this->at(5)) ->method('leaveExpression') ->with($this->identicalTo($expr)) ->willReturn($expr); $this->traverser->addVisitor($visitor); $this->assertSame($expr, $this->traverser->traverse($expr)); } public function testModifyDisjunctInEnterExpression() { $expr1 = new OrX(array( $gt1 = new GreaterThan(10), $same = new Same('5'), )); $expr2 = new OrX(array( $gt2 = new GreaterThan(5), $same, )); $visitor = $this->getMock('Webmozart\Expression\Traversal\ExpressionVisitor'); $visitor->expects($this->at(0)) ->method('enterExpression') ->with($this->identicalTo($expr1)) ->willReturn($expr1); $visitor->expects($this->at(1)) ->method('enterExpression') ->with($this->identicalTo($gt1)) ->willReturn($gt2); $visitor->expects($this->at(2)) ->method('leaveExpression') ->with($this->identicalTo($gt2)) ->willReturn($gt2); $visitor->expects($this->at(3)) ->method('enterExpression') ->with($this->identicalTo($same)) ->willReturn($same); $visitor->expects($this->at(4)) ->method('leaveExpression') ->with($this->identicalTo($same)) ->willReturn($same); $visitor->expects($this->at(5)) ->method('leaveExpression') ->with($this->equalTo($expr2)) ->willReturn($expr2); $this->traverser->addVisitor($visitor); $this->assertSame($expr2, $this->traverser->traverse($expr1)); } public function testModifyDisjunctInLeaveExpression() { $expr1 = new OrX(array( $gt1 = new GreaterThan(10), $same = new Same('5'), )); $expr2 = new OrX(array( $gt2 = new GreaterThan(5), $same, )); $visitor = $this->getMock('Webmozart\Expression\Traversal\ExpressionVisitor'); $visitor->expects($this->at(0)) ->method('enterExpression') ->with($this->identicalTo($expr1)) ->willReturn($expr1); $visitor->expects($this->at(1)) ->method('enterExpression') ->with($this->identicalTo($gt1)) ->willReturn($gt1); $visitor->expects($this->at(2)) ->method('leaveExpression') ->with($this->identicalTo($gt1)) ->willReturn($gt2); $visitor->expects($this->at(3)) ->method('enterExpression') ->with($this->identicalTo($same)) ->willReturn($same); $visitor->expects($this->at(4)) ->method('leaveExpression') ->with($this->identicalTo($same)) ->willReturn($same); $visitor->expects($this->at(5)) ->method('leaveExpression') ->with($this->equalTo($expr2)) ->willReturn($expr2); $this->traverser->addVisitor($visitor); $this->assertSame($expr2, $this->traverser->traverse($expr1)); } public function testRemoveDisjunct() { $expr1 = new OrX(array( $gt1 = new GreaterThan(10), $same = new Same('5'), )); $expr2 = new OrX(array($same)); $visitor = $this->getMock('Webmozart\Expression\Traversal\ExpressionVisitor'); $visitor->expects($this->at(0)) ->method('enterExpression') ->with($this->identicalTo($expr1)) ->willReturn($expr1); $visitor->expects($this->at(1)) ->method('enterExpression') ->with($this->identicalTo($gt1)) ->willReturn($gt1); $visitor->expects($this->at(2)) ->method('leaveExpression') ->with($this->identicalTo($gt1)) ->willReturn(null); $visitor->expects($this->at(3)) ->method('enterExpression') ->with($this->identicalTo($same)) ->willReturn($same); $visitor->expects($this->at(4)) ->method('leaveExpression') ->with($this->identicalTo($same)) ->willReturn($same); $visitor->expects($this->at(5)) ->method('leaveExpression') ->with($this->equalTo($expr2)) ->willReturn($expr2); $this->traverser->addVisitor($visitor); $this->assertSame($expr2, $this->traverser->traverse($expr1)); } public function testRemoveAllDisjuncts() { $expr1 = new OrX(array( $gt1 = new GreaterThan(10), $same = new Same('5'), )); $visitor = $this->getMock('Webmozart\Expression\Traversal\ExpressionVisitor'); $visitor->expects($this->at(0)) ->method('enterExpression') ->with($this->identicalTo($expr1)) ->willReturn($expr1); $visitor->expects($this->at(1)) ->method('enterExpression') ->with($this->identicalTo($gt1)) ->willReturn($gt1); $visitor->expects($this->at(2)) ->method('leaveExpression') ->with($this->identicalTo($gt1)) ->willReturn(null); $visitor->expects($this->at(3)) ->method('enterExpression') ->with($this->identicalTo($same)) ->willReturn($same); $visitor->expects($this->at(4)) ->method('leaveExpression') ->with($this->identicalTo($same)) ->willReturn(null); $this->traverser->addVisitor($visitor); $this->assertNull($this->traverser->traverse($expr1)); } }