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.

宿題

  • goingsner.net/articles/JsonPath/が私のニーズをカバーできる可能性があることがわかりました(Xpathのような式を使用しているため、完全に一致するわけではありません)。注意点は、正規表現と文字列の解析に大きく依存しているため、間違いなく遅くなります。配列のみ(JSONのような)の実装と比較してダウンしています。

  • また、ここで同様の質問を見つけました。@stackoverflowPHPで のMongoDBのようなJSONクエリの評価。結果として得られた答えは、ほとんどの場合回避するために使用されるいくつかのSPL関数を使用することでした。
    作者が機能を思いついたのだろうか、彼は開発しようとしていた。

  • 可能性のあるarrayPath実装は、thereisamoduleforthat.com / content /dealing-deep-arrays-phpで見つかりました。したがって、この実装がないのは、ポインターに依存していることです。

私はそれがワンライナーの答えで些細な質問ではないことを知っています、それで私は自分のクラスの実際の開発を始める前にそれを尋ねている理由です。

アーキテクチャのヒント、関連または類似のコードに感謝します。これは、php"if..else"式をその場で構築するための良い実践例かもしれません。強調されたテキスト

非SPLバージョンを作成する方法は?

@Babaは、SPLを使用して作成された優れたクラスを提供しました。SPLなしでこのコードを書き直す方法はどうだろうか。

これには2つの理由があります

  • クラスを複数回呼び出すと、関数のオーバーヘッドが発生します。これにより、生のPHPでクラスを書き直すことを回避できます。
  • SPLが利用できない生のJavascriptに簡単に移植できるため、両方のプラットフォームでのコードのメンテナンスが容易になります。

結果

作成されたArrayQueryクラスはGithubで公開されています。リポジトリをチェックアウトして、更新を確認することを検討してください。

SPL、生のPHPバージョンおよびChequer2FORPプロファイラー出力

簡単に-

  1. 生のPHPバージョンはSPLバージョンよりも10倍高速に実行され、20%少ないメモリを消費します。
  2. Chequer2クラスのパフォーマンスはPHPSPLクラスより40%遅く、生のPHPバージョンよりもほぼ20倍遅くなります。
  3. MongoDbは最速です(生のPHP実装よりも10倍高速で、消費するメモリも5倍少なくなります)。MongoDbとの対話を避けたいことが確実でない限り、これらのクラスを使用しないでください。

MongoDbバージョン

MongoDbリファレンスプロファイリングの結果

SPLバージョン

SPLクラスプロファイリング結果を使用したPHP

生のPHP(最新のArrayQueryクラス)バージョン

生のPHPArrayQueryクラスのプロファイリング結果

Chequer2バージョン

Chequer2PHPクラスのプロファイリング結果

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 );

chequer2PHPプロファイリングコード

<?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

序章

PHPでMongoDBのようなJSONクエリを評価することで、必要なすべての情報が得られたと思います。必要なのは、ソリューションを創造的にすることであり、あなたはあなたが望むものを達成します

アレイ

フォロー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 yearます2013

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

Callableを使用する

$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は、MongoDBのようなクエリ式オブジェクト評価を実装するクラスの優れた生のPHPバージョンを提供しましたが、出力構造は少し異なります。つまり、ネストされた配列output([release.arch] => x86)のドット表記です。通常のarrays([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て機能するので..少し時間がかかりましたが、柔軟性があり使いやすい代替クラスを思いつくことができました

配列を複数回ロードしないようにするには、1回宣言します。

$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.初めて、 findwhere =および=のような複合$andまたは$orステートメントを実行できます。release.archx86release.year2012

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

)

新しいModifiedクラス

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 に答える