14

我一直在寻找类似 MongoDb 的http://docs.mongodb.org/manual/applications/read/#find、docs.mongodb.org/manual/reference/operators/查询表达式对象评估函数实现或一类。它可能不会涵盖所有高级功能,并且应该具有可扩展的架构。

类似 MongoDB 的查询表达式对象易于理解和使用,提供编写干净、自解释代码的能力,因为查询和要搜索的对象都是关联数组。

基本上说它是从 php 数组中提取信息的便捷功能。知道数组结构(arrayPath),它将允许对多维数组数据执行操作,而无需多个嵌套循环。

如果您不熟悉 MongoDb,请查看要搜索的给定表达式对象和数组。

为简单起见,我将其写为 JSON 字符串。对象内容没有意义,只是显示了 MongoDb 查询语法。

类似 MongoDb 的查询表达式对象

{
    "name": "Mongo",
    "type": "db",
    "arch": {
        "$in": [
            "x86",
            "x64"
        ]
    },
    "version": {
        "$gte": 22
    },
    "released": {
        "$or": {
            "$lt": 2013,
            "$gt": 2012
        }
    }
}

要搜索的数组

[
    {
        "name": "Mongo",
        "type": "db",
        "release": {
            "arch": "x86",
            "version": 22,
            "year": 2012
        }
    },
    {
        "name": "Mongo",
        "type": "db",
        "release": {
            "arch": "x64",
            "version": 21,
            "year": 2012
        }
    },
    {
        "name": "Mongo",
        "type": "db",
        "release": {
            "arch": "x86",
            "version": 23,
            "year": 2013
        }
    }
]

使用类似 Mongo 的查询表达式进行查找

因此,借助该函数,我们应该能够向目标数组发出以下查询。

$found=findLikeMongo($array, $queryExpr); //resulting in a $array[0] value;
//@return found array

使用类似 Mongo 的查询表达式获取数组路径

$arrayPath=getPathFromMongo($array, $queryExpr);// resulting in array("0")
//@return array path, represented as an array where entries are consecutive keys.

家庭作业

  • 我发现 gosner.net/articles/JsonPath/ 可能满足我的需求(不是完全匹配,因为它使用类似 Xpath 的表达式),需要注意的是,它严重依赖正则表达式和字符串解析,这肯定会慢与仅数组(类似 JSON)的实现相比,它下降了。

  • 我也在这里找到了一个类似的问题,@stackoverflow Evaluating MongoDB-like JSON Queries in PHP。得到的答案是使用一些 SPL 函数,我习惯于在大多数情况下避免使用这些函数。
    不知道作者是否提出了功能,他一直在努力开发。

  • 在 thereisamoduleforthat.com/content/dealing-deep-arrays-php 上找到了可能的 arrayPath 实现,因此缺少此实现是因为它依赖于指针。

我知道这不是一个简单的问题,这就是为什么我在开始我自己的课程的实际开发之前问它的原因。

我很欣赏架构技巧、相关或类似的代码,这可能是动态构建 php“if..else”表达式的一个很好的实践示例。强调文本

如何编写非 SPL 版本?

@Baba 提供了一个优秀的类,它是使用 SPL 编写的。我想知道如何在没有 SPL 的情况下重写此代码。

有两个原因

  • 多次调用该类会产生函数开销,这可以避免在原始 PHP 中重写它。
  • 它可以很容易地移植到 SPL 不可用的原始 Javascript,从而更容易在两个平台上进行代码维护。

结果

创建的ArrayQuery 类发布在 Github 上,请考虑签出存储库以获取更新。

SPL、原始 PHP 版本和 Chequer2 FORP分析器输出

简单来说-

  1. 原始 PHP 版本的执行速度比 SPL 版本快 10 倍,消耗的内存减少了 20%。
  2. Chequer2 类的执行速度比 PHP SPL 类慢 40%,比原始 PHP 版本慢近 20 倍。
  3. MongoDb 是最快的(比原始 PHP 实现快 10 倍,消耗的内存少 5 倍),除非您确定要避免与 MongoDb 交互,否则不要使用这些类。

MongoDb 版本

MongoDb 参考分析结果

声压级版本

带有 SPL 类分析结果的 PHP

原始 PHP(最新的 ArrayQuery 类)版本

原始 PHP ArrayQuery 类分析结果

Checker2 版本

Chequer2 PHP 类分析结果

MongoDb 参考测试分析代码

$m = new MongoClient(); // connect
$db = $m->testmongo; // select a database
$collection = $db->data;
$loops=100;
for ($i=0; $i<$loops; $i++) {
    $d = $collection->find(array("release.year" => 2013));
}
print_r( iterator_to_array($d) );

带有 SPL 类分析代码的 PHP

include('data.php');
include('phpmongo-spl.php');
$s = new ArrayCollection($array, array("release.year" => 2013),false);
$loops=100;
for ($i=0; $i<$loops; $i++) {
    $d = $s->parse();
}
print_r( $d );

SPL 类parse()函数已稍作修改以在执行后返回值,也可以对其进行修改以接受表达式,但它对于分析目的并不是必需的,因为每次都会重新评估表达式。

原始 PHP(最新的 ArrayQuery 类)分析代码

include('data.php');
include('phpmongo-raw.php');
$s = new ArrayStandard($array);
$loops=100;
for ($i=0; $i<$loops; $i++) {
    $d = $s->find(array("release.year" => 2013));
}
print_r( $d );

checker2 PHP 分析代码

<?php
include('data.php');
include('../chequer2/Chequer.php');
$query=array("release.year" => 2013);

$loops=100;
for ($i=0; $i<$loops; $i++) {
    $result=Chequer::shorthand('(.release.year > 2012) ? (.) : NULL')
        ->walk($array);

}
print_r($result);
?>

使用的数据(与他的回答中提供的@baba 相同)

$json = '[{
    "name":"Mongo",
    "type":"db",
    "release":{
        "arch":"x86",
        "version":22,
        "year":2012
    }
},
{
    "name":"Mongo",
    "type":"db",
    "release":{
        "arch":"x64",
        "version":21,
        "year":2012
    }
},
{
    "name":"Mongo",
    "type":"db",
    "release":{
        "arch":"x86",
        "version":23,
        "year":2013
    }
},      
{
    "key":"Diffrent",
    "value":"cool",
    "children":{
        "tech":"json",
        "lang":"php",
        "year":2013
    }
}
]';

$array = json_decode($json, true);

forp-ui稍作修改的示例 ui 加载器(使用 ?profile=FILE_TO_PROFILE 调用)

<!doctype html>
<html>
    <head>
        <style>
            body {margin : 0px}
        </style>
    </head>
    <body>
        <div class="forp"></div>
<?php
register_shutdown_function(
    function() {
        // next code can be append to PHP scripts in dev mode
        ?>
        <script src="../forp-ui/js/forp.min.js"></script>
        <script>
        (function(f) {
            f.find(".forp")
             .each(
                function(el) {
                    el.css('margin:50px;height:300px;border:1px solid #333');
                }
             )
             .forp({
                stack : <?php echo json_encode(forp_dump()); ?>,
                //mode : "fixed"
             })
        })(forp);
        </script>
        <?php
    }
);

// start forp
forp_start();

// our PHP script to profile
include($_GET['profile']);

// stop forp
forp_end();
?>
</body>
</html>
4

2 回答 2

10

介绍

我认为Evaluating MongoDB-like JSON Queries in PHP已经提供了您需要的所有信息。您所需要的只是对解决方案具有创造性并实现您想要的

阵列

假设我们将以下内容json转换为数组

$json = '[{
    "name":"Mongo",
    "type":"db",
    "release":{
        "arch":"x86",
        "version":22,
        "year":2012
    }
},
{
    "name":"Mongo",
    "type":"db",
    "release":{
        "arch":"x64",
        "version":21,
        "year":2012
    }
},
{
    "name":"Mongo",
    "type":"db",
    "release":{
        "arch":"x86",
        "version":23,
        "year":2013
    }
},      
{
    "key":"Diffrent",
    "value":"cool",
    "children":{
        "tech":"json",
        "lang":"php",
        "year":2013
    }
}
]';

$array = json_decode($json, true);

示例 1

检查key-Different是否会像

echo new ArrayCollection($array, array("key" => "Diffrent"));

输出

{"3":{"key":"Diffrent","value":"cool","children":{"tech":"json","lang":"php","year":2013}}}

示例 2 检查是否release year2013

echo new ArrayCollection($array, array("release.year" => 2013));

输出

{"2":{"name":"Mongo","type":"db","release":{"arch":"x86","version":23,"year":2013}}}

示例 3

Year数一数在哪里2012

$c = new ArrayCollection($array, array("release.year" => 2012));
echo count($c); // output 2 

示例 4

让我们从您要检查的示例中version获取grater than 22

$c = new ArrayCollection($array, array("release.version" => array('$gt'=>22)));
echo $c;

输出

{"2":{"name":"Mongo","type":"db","release":{"arch":"x86","version":23,"year":2013}}}

示例 5

检查release.arch值是否是IN一个集合,例如[x86,x100](示例)

$c = new ArrayCollection($array, array("release.arch" => array('$in'=>array("x86","x100"))));
foreach($c as $var)
{
    print_r($var);
}

输出

Array
(
    [name] => Mongo
    [type] => db
    [release] => Array
        (
            [arch] => x86
            [version] => 22
            [year] => 2012
        )

)
Array
(
    [name] => Mongo
    [type] => db
    [release] => Array
        (
            [arch] => x86
            [version] => 23
            [year] => 2013
        )

)

例 6

使用可调用

$year = 2013;
$expression = array("release.year" => array('$func' => function ($value) use($year) {
    return $value === 2013;
}));

$c = new ArrayCollection($array, $expression);

foreach ( $c as $var ) {
    print_r($var);
}

输出

Array
(
    [name] => Mongo
    [type] => db
    [release] => Array
        (
            [arch] => x86
            [version] => 23
            [year] => 2013
        )

)

例 7

注册自己的表情名称

$c = new ArrayCollection($array, array("release.year" => array('$baba' => 3)), false);
$c->register('$baba', function ($a, $b) {
    return substr($a, - 1) == $b;
});
$c->parse();
echo $c;

输出

{"2":{"name":"Mongo","type":"db","release":{"arch":"x86","version":23,"year":2013}}}

使用的类

class ArrayCollection implements IteratorAggregate, Countable, JsonSerializable {
    private $array;
    private $found = array();
    private $log;
    private $expression;
    private $register;

    function __construct(array $array, array $expression, $parse = true) {
        $this->array = $array;
        $this->expression = $expression;
        $this->registerDefault();
        $parse === true and $this->parse();
    }

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

    public function jsonSerialize() {
        return json_encode($this->found);
    }

    public function getIterator() {
        return new ArrayIterator($this->found);
    }

    public function count() {
        return count($this->found);
    }

    public function getLog() {
        return $this->log;
    }

    public function register($offset, $value) {
        if (strpos($offset, '$') !== 0)
            throw new InvalidArgumentException('Expresiion name must always start with "$" sign');

        if (isset($this->register[$offset]))
            throw new InvalidArgumentException(sprintf('Expression %s already registred .. Please unregister It first'));

        if (! is_callable($value)) {
            throw new InvalidArgumentException(sprintf('Only callable value can be registred'));
        }

        $this->register[$offset] = $value;
    }

    public function unRegister($offset) {
        unset($this->register[$offset]);
    }

    public function parse() {
        $it = new RecursiveIteratorIterator(new RecursiveArrayIterator($this->array));
        foreach ( $it as $k => $items ) {
            if ($this->evaluate($this->getPath($it), $items)) {
                $this->found[$it->getSubIterator(0)->key()] = $this->array[$it->getSubIterator(0)->key()];
            }
        }
    }

    private function registerDefault() {
        $this->register['$eq'] = array($this,"evaluateEqal");
        $this->register['$not'] = array($this,"evaluateNotEqual");

        $this->register['$gte'] = array($this,"evaluateGreater");
        $this->register['$gt'] = array($this,"evaluateGreater");

        $this->register['$lte'] = array($this,"evaluateLess");
        $this->register['$lt'] = array($this,"evaluateLess");

        $this->register['$in'] = array($this,"evalueateInset");

        $this->register['$func'] = array($this,"evalueateFunction");
        $this->register['$fn'] = array($this,"evalueateFunction");
        $this->register['$f'] = array($this,"evalueateFunction");
    }

    private function log($log) {
        $this->log[] = $log;
    }

    private function getPath(RecursiveIteratorIterator $it) {
        $keyPath = array();
        foreach ( range(1, $it->getDepth()) as $depth ) {
            $keyPath[] = $it->getSubIterator($depth)->key();
        }
        return implode(".", $keyPath);
    }

    private function checkType($a, $b) {
        if (gettype($a) != gettype($b)) {
            $this->log(sprintf("%s - %s  is not same type of %s - %s", json_encode($a), gettype($a), json_encode($b), gettype($b)));
            return false;
        }
        return true;
    }

    private function evaluate($key, $value) {
        $o = $r = 0; // Obigation & Requirement
        foreach ( $this->expression as $k => $options ) {
            if ($k !== $key)
                continue;

            if (is_array($options)) {
                foreach ( $options as $eK => $eValue ) {
                    if (strpos($eK, '$') === 0) {
                        $r ++;
                        $callable = $this->register[$eK];
                        $callable($value, $eValue) and $o ++;
                    } else {
                        throw new InvalidArgumentException('Missing "$" in expession key');
                    }
                }
            } else {

                $r ++;
                $this->evaluateEqal($value, $options) and $o ++;
            }
        }
        return $r > 0 && $o === $r;
    }

    private function evaluateEqal($a, $b) {
        return $a == $b;
    }

    private function evaluateNotEqual($a, $b) {
        return $a != $b;
    }

    private function evaluateLess($a, $b) {
        return $this->checkType($a, $b) and $a < $b;
    }

    private function evaluateGreater($a, $b) {
        return $this->checkType($a, $b) and $a > $b;
    }

    private function evalueateInset($a, array $b) {
        return in_array($a, $b);
    }

    private function evalueateFunction($a, callable $b) {
        return $b($a);
    }
}

概括

它可能不会涵盖所有高级功能,并且应该具有可扩展的架构

上面 的 类 展示 了 你 想要 的 一个 典型 例子 .. 你 可以 轻松decouple, 扩展 它 以 支持 复合 表达式 , 如$and$or

类似 MongoDB 的查询表达式对象易于理解和使用,提供编写干净、自解释代码的能力,因为查询和要搜索的对象都是关联数组。

为什么不直接将数组写入MongoDB数据库而不是使用数组?它更高效,它会为你省去很多麻烦

我还必须提到,使用最好的工具做最好的工作......你想要的基本上是一个数据库的功能

基本上说它是从 php 数组中提取信息的便捷功能。知道数组结构(arrayPath),它将允许对多维数组数据执行操作,而无需多个嵌套循环。

该示例显示了如何使用路径来搜索值,但您仍然依赖于将数组加载到内存以及您的类执行多个递归 ans 循环,这不如数据库高效。

我很欣赏架构技巧、相关或类似的代码,这可能是动态构建 php“if..else”表达式的一个很好的实践示例。

你真的是说你想要所有的人都在这里???

于 2013-02-28T20:34:09.760 回答
8

最近更新

@baba 提供了一个很棒的原始 PHP 版本的类,它实现了类似 MongoDB 的查询表达式对象评估,但是输出结构有点不同,我的意思是嵌套数组输出中的点表示法( [release.arch] => x86 ),而不是常规数组( [release] => Array([arch] => x86) )。我将不胜感激您提示如何按此顺序使该类与 mongoDB 完全兼容,因为它似乎与原始 PHP 类实现严格相关。

==================================================== ======================

回答:

你想要的很简单,你只需要2 corrections在当前的代码输入和输出循环中,你就会得到你的新格式。

我是什么意思?

A.改变

  foreach ( $array as $part ) {
        $this->flatten[] = $this->convert($part);
    }

    foreach ( $array as $k => $part ) {
        $this->flatten[$k] = $this->convert($part);
    }

B.改变

    foreach ( $this->flatten as $data ) {
        $this->check($find, $data, $type) and $f[] = $data;
    }

至:

    foreach ( $this->flatten as $k => $data ) {
        $this->check($find, $data, $type) and $f[] = $this->array[$k];
    }

用于休息的新阵列 

$json = '[
  {
    "name": "Mongo",
    "release": {
      "arch": "x86",
      "version": 22,
      "year": 2012
    },
    "type": "db"
  },
  {
    "name": "Mongo",
    "release": {
      "arch": "x64",
      "version": 21,
      "year": 2012
    },
    "type": "db"
  },
  {
    "name": "Mongo",
    "release": {
      "arch": "x86",
      "version": 23,
      "year": 2013
    },
    "type": "db"
  },
  {
    "name": "MongoBuster",
    "release": {
      "arch": [
        "x86",
        "x64"
      ],
      "version": 23,
      "year": 2013
    },
    "type": "db"
  },
  {
    "children": {
      "dance": [
        "one",
        "two",
        {
          "three": {
            "a": "apple",
            "b": 700000,
            "c": 8.8
          }
        }
      ],
      "lang": "php",
      "tech": "json",
      "year": 2013
    },
    "key": "Diffrent",
    "value": "cool"
  }
]';

$array = json_decode($json, true);

简单测试

$s = new ArrayStandard($array);
print_r($s->find(array("release.arch"=>"x86")));

输出

Array
(
    [0] => Array
        (
            [name] => Mongo
            [type] => db
            [release] => Array
                (
                    [arch] => x86
                    [version] => 22
                    [year] => 2012
                )

        )

    [1] => Array
        (
            [name] => Mongo
            [type] => db
            [release] => Array
                (
                    [arch] => x86
                    [version] => 23
                    [year] => 2013
                )

        )

)

如果您还想保留原件array key position,您可以拥有

    foreach ( $this->flatten as $k => $data ) {
        $this->check($find, $data, $type) and $f[$k] = $this->array[$k];
    }

只是为了好玩

A.支持regex

只是为了好玩,我添加了对$regex别名的支持,$preg或者$match这意味着你可以拥有

print_r($s->find(array("release.arch" => array('$regex' => "/4$/"))));

或者

print_r($s->find(array("release.arch" => array('$regex' => "/4$/"))));

输出

Array
(
    [1] => Array
        (
            [name] => Mongo
            [type] => db
            [release] => Array
                (
                    [arch] => x64
                    [version] => 21
                    [year] => 2012
                )

        )

)

B.使用简单数组,如queries

$queryArray = array(
        "release" => array(
                "arch" => "x86"
        )
);
$d = $s->find($s->convert($queryArray));

$s->convert($queryArray)已转换

Array
(
    [release] => Array
        (
            [arch] => x86
        )

)

Array
(
    [release.arch] => x86
)

C.模数$mod

print_r($s->find(array(
        "release.version" => array(
                '$mod' => array(
                        23 => 0
                )
        )
)));

 //Checks release.version % 23 == 0 ;

D.计算元素$size

print_r($s->find(array(
        "release.arch" => array(
                '$size' => 2
        )
)));

// returns count(release.arch) == 2;

E.检查它是否匹配数组中的所有元素$all

print_r($s->find(array(
        "release.arch" => array(
                '$all' => array(
                        "x86",
                        "x64"
                )
        )
)));

输出

Array
(
    [3] => Array
        (
            [name] => MongoBuster
            [release] => Array
                (
                    [arch] => Array
                        (
                            [0] => x86
                            [1] => x64
                        )

                    [version] => 23
                    [year] => 2013
                )

            [type] => db
        )

)

F.如果您不确定元素键名,那么您可以使用$has类似opposite$in

print_r($s->find(array(
        "release" => array(
                '$has' => "x86"
        )
)));

==================================================== ======================

旧更新

@Baba 提供了一个优秀的类,它是使用 SPL 编写的。我想知道如何在没有 SPL 的情况下重写此代码。原因是多次调用这个类会产生函数开销,这可以避免在原始 PHP 中重写它,并且可能在最终版本中使用 goto 语句来避免递归函数调用。

==================================================== ======================

由于您不想要SPL和功能..花了一段时间,但我能够想出替代类,它也灵活且易于使用

为避免多次加载数组,您声明一次:

$array = json_decode($json, true);
$s = new ArrayStandard($array);

A.找到在release.year哪里2013

$d = $s->find(array(
        "release.year" => "2013"
));
print_r($d);

输出

Array
(
    [0] => Array
        (
            [name] => Mongo
            [type] => db
            [release.arch] => x86
            [release.version] => 23
            [release.year] => 2013
        )

)

B.你第一次可以运行复杂的$and语句$or,比如 find where release.arch=x86release.year=2012

$d = $s->find(array(
        "release.arch" => "x86",
        "release.year" => "2012"
), ArrayStandard::COMPLEX_AND);

print_r($d);

输出

Array
(
    [0] => Array
        (
            [name] => Mongo
            [type] => db
            [release.arch] => x86
            [release.version] => 22
            [release.year] => 2012
        )

)

C.想象一个更复杂的查询

$d = $s->find(array(
        "release.year" => array(
                '$in' => array(
                        "2012",
                        "2013"
                )
        ),
        "release.version" => array(
                '$gt' => 22
        ),
        "release.arch" => array(
                '$func' => function ($a) {
                    return $a == "x86";
                }
        )
), ArrayStandard::COMPLEX_AND);

print_r($d);

输出

Array
(
    [0] => Array
        (
            [name] => Mongo
            [type] => db
            [release.arch] => x86
            [release.version] => 23
            [release.year] => 2013
        )

)

新的修改类

class ArrayStandard {
    const COMPLEX_OR = 1;
    const COMPLEX_AND = 2;
    private $array;
    private $tokens;
    private $found;

    function __construct(array $array) {
        $this->array = $array;
        foreach ( $array as $k => $item ) {
            $this->tokens[$k] = $this->tokenize($item);
        }   
    }

    public function getTokens() {
        return $this->tokens;
    }

    public function convert($part) {
        return $this->tokenize($part, null, false);
    }

    public function find(array $find, $type = 1) {
        $f = array();
        foreach ( $this->tokens as $k => $data ) {
            $this->check($find, $data, $type) and $f[$k] = $this->array[$k];
        }
        return $f;
    }

    private function check($find, $data, $type) {
        $o = $r = 0; // Obigation & Requirement
        foreach ( $data as $key => $value ) {
            if (isset($find[$key])) {
                $r ++;
                $options = $find[$key];
                if (is_array($options)) {
                    reset($options);
                    $eK = key($options);
                    $eValue = current($options);
                    if (strpos($eK, '$') === 0) {
                        $this->evaluate($eK, $value, $eValue) and $o ++;
                    } else {
                        throw new InvalidArgumentException('Missing "$" in expession key');
                    }
                } else {
                    $this->evaluate('$eq', $value, $options) and $o ++;
                }
            }
        }

        if ($o === 0)
            return false;

        if ($type == self::COMPLEX_AND and $o !== $r)
            return false;

        return true;
    }

    private function getValue(array $path) {
        return count($path) > 1 ? $this->getValue(array_slice($path, 1), $this->array[$path[0]]) : $this->array[$path[0]];
    }

    private function tokenize($array, $prefix = '', $addParent = true) {
        $paths = array();
        $px = empty($prefix) ? null : $prefix . ".";
        foreach ( $array as $key => $items ) {
            if (is_array($items)) {
                $addParent && $paths[$px . $key] = json_encode($items);
                foreach ( $this->tokenize($items, $px . $key) as $k => $path ) {
                    $paths[$k] = $path;
                }
            } else {
                $paths[$px . $key] = $items;
            }
        }
        return $paths;
    }

    private function evaluate($func, $a, $b) {
        $r = false;

        switch ($func) {
            case '$eq' :
                $r = $a == $b;
                break;
            case '$not' :
                $r = $a != $b;
                break;
            case '$gte' :
            case '$gt' :
                if ($this->checkType($a, $b)) {
                    $r = $a > $b;
                }
                break;

            case '$lte' :
            case '$lt' :
                if ($this->checkType($a, $b)) {
                    $r = $a < $b;
                }
                break;
            case '$in' :
                if (! is_array($b))
                    throw new InvalidArgumentException('Invalid argument for $in option must be array');
                $r = in_array($a, $b);
                break;

            case '$has' :
                if (is_array($b))
                    throw new InvalidArgumentException('Invalid argument for $has array not supported');
                $a = @json_decode($a, true) ?  : array();
                $r = in_array($b, $a);
                break;

            case '$all' :
                $a = @json_decode($a, true) ?  : array();
                if (! is_array($b))
                    throw new InvalidArgumentException('Invalid argument for $all option must be array');
                $r = count(array_intersect_key($a, $b)) == count($b);
                break;

            case '$regex' :
            case '$preg' :
            case '$match' :

                $r = (boolean) preg_match($b, $a, $match);
                break;

            case '$size' :
                $a = @json_decode($a, true) ?  : array();
                $r = (int) $b == count($a);
                break;

            case '$mod' :
                if (! is_array($b))
                    throw new InvalidArgumentException('Invalid argument for $mod option must be array');
                list($x, $y) = each($b);
                $r = $a % $x == 0;
                break;

            case '$func' :
            case '$fn' :
            case '$f' :
                if (! is_callable($b))
                    throw new InvalidArgumentException('Function should be callable');
                $r = $b($a);
                break;

            default :
                throw new ErrorException("Condition not valid ... Use \$fn for custom operations");
                break;
        }

        return $r;
    }

    private function checkType($a, $b) {
        if (is_numeric($a) && is_numeric($b)) {
            $a = filter_var($a, FILTER_SANITIZE_NUMBER_FLOAT);
            $b = filter_var($b, FILTER_SANITIZE_NUMBER_FLOAT);
        }

        if (gettype($a) != gettype($b)) {
            return false;
        }
        return true;
    }
}
于 2013-03-19T20:28:44.470 回答