定制 webmozart/expression 二次开发

按需修改功能、优化性能、对接业务系统,提供一站式技术支持

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

webmozart/expression

Composer 安装命令:

composer require webmozart/expression

包简介

Implementation of the Specification pattern and logical expressions for PHP.

README 文档

README

Build Status Build status Latest Stable Version Total Downloads Dependency Status

Latest release: 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:

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:

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

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

class PersonRepositoryImpl implements PersonRepository
{
    private $persons = [];
    
    public function findPersons(Expression $expr)
    {
        return Expr::filter($this->persons, $expr);
    }
}

Visitors 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:

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:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

The following table lists all available selectors:

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:

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

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

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

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

The same is possible for the "or" operator:

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

You can use and/or inside selectors:

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

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

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

Testing

To make sure that PHPUnit compares Expression objects correctly, you should register the ExpressionComparator with PHPUnit in your PHPUnit bootstrap file:

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

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

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

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

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

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

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

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

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:

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

Authors

Contribute

Contributions to the package are always welcome!

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.

webmozart/expression 适用场景与选型建议

webmozart/expression 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 398.83k 次下载、GitHub Stars 达 214, 最近一次更新时间为 2015 年 03 月 10 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「filter」 「formula」 「expression」 「criteria」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 webmozart/expression 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 webmozart/expression 我们能提供哪些服务?
定制开发 / 二次开发

基于 webmozart/expression 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 398.83k
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 215
  • 点击次数: 27
  • 依赖项目数: 14
  • 推荐数: 0

GitHub 信息

  • Stars: 214
  • Watchers: 14
  • Forks: 10
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2015-03-10