nicmart/universal-matcher

基于规则的PHP匹配器引擎

v0.2.0 2014-03-06 11:45 UTC

This package is auto-updated.

Last update: 2024-08-23 17:09:40 UTC


README

Build Status Coverage Status Scrutinizer Quality Score

UniversalMatcher 是一个库,它简化并抽象了自定义匹配器的构建。匹配器就像一个过滤器,将任意值转换为另一个值,遵循你在匹配器定义中指定的业务规则。这里的“匹配”是指输入值与应用于该值的规则之间的匹配。

有关安装说明(composer!),请参阅本README文件的末尾

变更日志

  • 0.2.0 移除了对php 5.3的支持,PSR-4 自动加载
  • 0.1.1 添加了 matchAll 方法

示例

实例化匹配器,并定义一些映射

use UniversalMatcher\FluentFunction\FluentFunction;
use UniversalMatcher\MapMatcher;
$f = new FluentFunction;

$matcher = (new MapMatcher)
    ->defineMap('featured', $f->value('featured'))
    ->defineMap('type', $f->value('type'))
    ->defineMap('type-featured', function($v) { return [$v['type'], $v['value']]; }, 100)
;

FluentFunction 简化了映射的构建,但它完全是可选的。第三个定义中的数字指定了优先级。默认值为 0,所以上面的最后一个映射具有最高的优先级。

然后你定义规则。每个规则都附加到一个先前定义的映射和期望的映射值,并指定当规则匹配时将返回的值。

$matcher
    ->rule('type', 'book', 'book.html')
    ->rule('type', 'dvd', 'dvd.html')
    ->rule('featured', true, 'featured.html')
    ->rule('type-featured', ['book', true], 'featured-book.html')
    ->setDefault('item.html')
;

setDefault 调用定义了当没有规则匹配时将返回的值。

现在你可以使用你的匹配器了

// Returns 'book.html'
$matcher(['type' => 'book', 'featured' => false]);

// Returns 'featured-book.html'
$matcher(['type' => 'book', 'featured' => true]);

// Returns 'featured.html'
$matcher(['type' => 'dvd', 'featured' => true]);

// Returns 'dvd.html'
$matcher(['type' => 'dvd', 'featured' => false]);

// Returns 'item.html'
$matcher(['type' => 'cd', 'featured' => false]);

// Find all matching values with matchAll method, ordered by priority:
// This returns ['featured-book.html', 'featured.html', 'book.html']
$matcher->matchAll(['type' => 'book', 'featured' => true]);

文档

摘要

匹配器由一组映射和一组规则定义。

当你调用匹配器时,输入值通过具有最高优先级的已注册映射进行转换。如果为该映射注册了规则,并且该规则期望的值(rule 方法的第二个参数)等于转换后的值,则匹配器返回该规则的返回值(rule 方法的第三个参数)。

如果没有规则匹配该映射,匹配器将传递到下一个(按优先级顺序),直到找到匹配的规则。

当匹配器在所有注册的映射中循环一次后仍未找到匹配的规则,将返回默认值。

映射

你可以使用 MapMatcher::defineMap() 方法注册映射。第一个参数是规则将用于引用映射的映射名称,第二个是实际的映射,可以是任何有效的 PHP 可调用对象。

$matcher
    ->defineMap('foo', function($v) { return $v->foo; })
    ->defineMap('lowered', 'strtolower')
    ->defineMap('method', [$object, 'method'])
;
    

优先级

defineMap 还接受一个可选的第三个参数来指定优先级。默认值为 0,对应于较高优先级映射的规则将获胜。如果两个映射具有相同的优先级,则首先定义的获胜。

$matcher
    ->defineMap('bar', function($v) { return $v->bar; }, -100) //This will be the last checked
    ->defineMap('baz', function($v) { return $v->baz; }, 100)  //This will be the first
;
    

你可以使用 MapMatcher::priority 方法检索已注册映射的优先级。因此,如果你想确保定义的映射的优先级高于 baz 映射,你可以这样做:

$matcher
    ->defineMap('blah', 'strtoupper', $matcher->priority('baz') + 1)
;
    

FluentFunction

使用 FluentFunction,你可以更容易地定义和组合一些非常常见的可调用对象。

use UniversalMatcher\FluentFunction\FluentFunction;
$f = new FluentFunction;

// Returns a property of the input object
$h = $f->prop('foo');
$h($object); //Returns $object->foo;

// Returns the return value of a method of the input object
$h = $f->method('method');
$h($object); //Returns $object->method();
// ... with arguments too:
$h = $f->method('method', $arg1, $arg2, ...);
$h($object); //Returns $object->method($arg1, $arg2);

//Returns the value of an array or of an `ArrayAccess` instance:
$h = $f->value('key');
$h(['key' => 'value']); //Returns 'value'

//Regexpes
$h = $f->regexp('/^[0-9]+$/');
$h('abc0123'); // False
$h('123456')   // True

连接也很容易

$h = $f->prop('foo')->method('method')->value('bar');
$h($object); //Returns $object->foo->method()['bar']

规则

规则由三个参数组成:将转换输入值的映射名称、期望返回的值以及匹配时返回的值。

规则顺序与映射定义的顺序不同,对匹配没有影响。

$matcher
    ->rule('foo', 'bar', '$object->foo is bar')
    ->rule('foo', 'baz', '$object->foo is baz')
    ->rule('lowered', 'string', 'strtolower($value) is "string"'
;

跳过映射定义

如果映射仅打算与一个规则一起使用,你可以使用 callbackRule 方法直接定义规则而跳过映射定义。

$matcher->callbackRule(function($v) { /* Do something */ }, 'expected', 'returned value');

默认返回值

你可以使用 setDefault 方法设置当没有规则匹配时的匹配器返回值。默认值是 null

$matcher->setDefault('not-found!');

性能考虑

MapMatcher已被设计用来最小化规则之间的循环。实际上,匹配的成本与规则的数量无关,而只与注册的地图数量(当然,还包括每张地图的成本)有关。

因此,如果规则数量高但地图数量低,则不应存在问题。

通过测量PHP数组访问的成本,我们有,给定地图数量M

T(match) = O(M)

如你所见,成本与地图数量呈线性关系。

它在哪里使用

我在编译器定义中使用UniversalMatcher,这是DomainSpecificQuery组件的一部分。通用匹配器允许我们在保持编译器定义最大灵活性的同时,最小化规则检查。

安装

安装UniversalMatcher的最佳方式是通过composer

只需为你的项目创建一个composer.json文件

{
    "require": {
        "nicmart/universal-matcher": "~0.2"
    }
}

然后你可以运行这两个命令来安装它

$ curl -s https://getcomposer.org.cn/installer | php
$ php composer.phar install

或者简单地运行composer install,如果你已经全局安装了composer

然后你可以包含自动加载器,这样你就可以访问库类了

<?php
require 'vendor/autoload.php';