From 17646b895b95136b85fe137f5c918eb21c595a25 Mon Sep 17 00:00:00 2001 From: Michal Kruczek Date: Fri, 21 Jul 2017 05:56:37 +0200 Subject: [PATCH 1/3] JSON matcher --- src/AST/Node.php | 1 - src/Factory/SimpleFactory.php | 19 +- src/Matcher/JsonObjectMatcher.php | 91 ++++++++ src/Matcher/OrMatcher.php | 2 +- src/Matcher/Pattern/Expander/Match.php | 63 ++++++ src/Parser/ExpanderInitializer.php | 2 +- tests/Matcher/JsonObjectMatcherTest.php | 267 ++++++++++++++++++++++++ 7 files changed, 440 insertions(+), 5 deletions(-) create mode 100644 src/Matcher/JsonObjectMatcher.php create mode 100644 src/Matcher/Pattern/Expander/Match.php create mode 100644 tests/Matcher/JsonObjectMatcherTest.php diff --git a/src/AST/Node.php b/src/AST/Node.php index cae2fc8f..c0751306 100644 --- a/src/AST/Node.php +++ b/src/AST/Node.php @@ -4,5 +4,4 @@ interface Node { - } diff --git a/src/Factory/SimpleFactory.php b/src/Factory/SimpleFactory.php index 54cdfc63..1c18d373 100644 --- a/src/Factory/SimpleFactory.php +++ b/src/Factory/SimpleFactory.php @@ -30,7 +30,7 @@ protected function buildMatchers() $orMatcher, new Matcher\JsonMatcher($orMatcher), new Matcher\XmlMatcher($orMatcher), - new Matcher\TextMatcher($scalarMatchers, $this->buildParser()) + new Matcher\TextMatcher($scalarMatchers, $this->buildParser()), )); return $chainMatcher; @@ -46,7 +46,8 @@ protected function buildOrMatcher() $arrayMatcher = new Matcher\ArrayMatcher( new Matcher\ChainMatcher(array( $orMatcher, - $scalarMatchers + $scalarMatchers, + $this->buildJsonObjectMatcher(), )), $this->buildParser() ); @@ -59,6 +60,20 @@ protected function buildOrMatcher() return $chainMatcher; } + protected function buildJsonObjectMatcher() + { + $parser = $this->buildParser(); + $scalarMatchers = $this->buildScalarMatchers(); + $orMatcher = new Matcher\OrMatcher($scalarMatchers); + $arrayMatcher = new Matcher\ArrayMatcher($orMatcher, $parser); + + return new Matcher\JsonObjectMatcher(new Matcher\ChainMatcher([ + $orMatcher, + $scalarMatchers, + $arrayMatcher, + ]), $parser); + } + /** * @return Matcher\ChainMatcher */ diff --git a/src/Matcher/JsonObjectMatcher.php b/src/Matcher/JsonObjectMatcher.php new file mode 100644 index 00000000..4d2ab934 --- /dev/null +++ b/src/Matcher/JsonObjectMatcher.php @@ -0,0 +1,91 @@ +propertyMatcher = $propertyMatcher; + $this->parser = $parser; + } + + /** + * {@inheritDoc} + */ + public function match($value, $pattern) + { + if (!$this->isJsonPattern($pattern)) { + return false; + } + + if (!is_array($value)) { + $this->error = sprintf("%s \"%s\" is not a valid array.", gettype($value), new StringConverter($value)); + return false; + } + + if ($this->isJsonPattern($pattern)) { + return $this->allExpandersMatch($value, $pattern); + } + + return true; + } + + /** + * {@inheritDoc} + */ + public function canMatch($pattern) + { + return is_string($pattern) && $this->isJsonPattern($pattern); + } + + private function isJsonPattern($pattern) + { + if (!is_string($pattern)) { + return false; + } + + return $this->parser->hasValidSyntax($pattern) && $this->parser->parse($pattern)->is('json'); + } + + /** + * @param $value + * @param $pattern + * @return bool + * @throws \Coduo\PHPMatcher\Exception\UnknownExpanderException + */ + private function allExpandersMatch($value, $pattern) + { + $typePattern = $this->parser->parse($pattern); + if (!$typePattern->matchExpanders($value)) { + $this->error = $typePattern->getError(); + return false; + } + + return true; + } +} diff --git a/src/Matcher/OrMatcher.php b/src/Matcher/OrMatcher.php index 5eea2958..8618c61e 100644 --- a/src/Matcher/OrMatcher.php +++ b/src/Matcher/OrMatcher.php @@ -26,7 +26,7 @@ public function match($value, $pattern) { $patterns = explode('||', $pattern); foreach ($patterns as $childPattern) { - if ($this->matchChild($value, $childPattern)){ + if ($this->matchChild($value, $childPattern)) { return true; } } diff --git a/src/Matcher/Pattern/Expander/Match.php b/src/Matcher/Pattern/Expander/Match.php new file mode 100644 index 00000000..836a8a91 --- /dev/null +++ b/src/Matcher/Pattern/Expander/Match.php @@ -0,0 +1,63 @@ +value = $value; + $this->matcher = (new SimpleFactory)->createMatcher(); + } + + /** + * @param $value + * @return boolean + */ + public function match($value) + { + if (!is_array($value)) { + $this->error = sprintf("Match expander require \"array\", got \"%s\".", new StringConverter($value)); + return false; + } + + $match = true; + foreach ($value as $singleRowValue) { + if (!$this->matcher->match($singleRowValue, $this->value)) { + $match = false; + $this->error = $this->matcher->getError(); + } + } + + unset($this->matcher); + + return $match; + } + + /** + * @return string|null + */ + public function getError() + { + return $this->error; + } +} diff --git a/src/Parser/ExpanderInitializer.php b/src/Parser/ExpanderInitializer.php index 9f21bcd7..5adfd60a 100644 --- a/src/Parser/ExpanderInitializer.php +++ b/src/Parser/ExpanderInitializer.php @@ -28,7 +28,7 @@ final class ExpanderInitializer "count" => "Coduo\\PHPMatcher\\Matcher\\Pattern\\Expander\\Count", "contains" => "Coduo\\PHPMatcher\\Matcher\\Pattern\\Expander\\Contains", "matchRegex" => "Coduo\\PHPMatcher\\Matcher\\Pattern\\Expander\\MatchRegex", - + "match" => "Coduo\\PHPMatcher\\Matcher\\Pattern\\Expander\\Match", "oneOf" => "Coduo\\PHPMatcher\\Matcher\\Pattern\\Expander\\OneOf" ); diff --git a/tests/Matcher/JsonObjectMatcherTest.php b/tests/Matcher/JsonObjectMatcherTest.php new file mode 100644 index 00000000..5143e546 --- /dev/null +++ b/tests/Matcher/JsonObjectMatcherTest.php @@ -0,0 +1,267 @@ +matcher = $factory->createMatcher(); + } + + + /** + * @dataProvider positiveMatchData + */ + public function test_positive_match_arrays($value, $pattern) + { + $this->assertTrue($this->matcher->match($value, $pattern), $this->matcher->getError()); + } + +// /** +// * @dataProvider negativeMatchData +// */ +// public function test_negative_match_arrays($value, $pattern) +// { +// $this->assertFalse($this->matcher->match($value, $pattern)); +// } +// +// public function test_negative_match_when_cant_find_matcher_that_can_match_array_element() +// { +// $matcher = new Matcher\ArrayMatcher( +// new Matcher\ChainMatcher(array( +// new Matcher\WildcardMatcher() +// )), +// $parser = new Parser(new Lexer(), new Parser\ExpanderInitializer()) +// ); +// +// $this->assertTrue($matcher->match(array('test' => 1), array('test' => 1))); +// } +// +// public function test_error_when_path_in_pattern_does_not_exist() +// { +// $this->assertFalse($this->matcher->match(array('foo' => 'foo value'), array('bar' => 'bar value'))); +// $this->assertEquals($this->matcher->getError(), 'There is no element under path [foo] in pattern.'); +// } +// +// public function test_error_when_path_in_nested_pattern_does_not_exist() +// { +// $array = array('foo' => array('bar' => array('baz' => 'bar value'))); +// $pattern = array('foo' => array('bar' => array('faz' => 'faz value'))); +// +// $this->assertFalse($this->matcher->match($array, $pattern)); +// +// $this->assertEquals($this->matcher->getError(), 'There is no element under path [foo][bar][baz] in pattern.'); +// } +// +// public function test_error_when_path_in_value_does_not_exist() +// { +// $array = array('foo' => 'foo'); +// $pattern = array('foo' => 'foo', 'bar' => 'bar'); +// +// $this->assertFalse($this->matcher->match($array, $pattern)); +// +// $this->assertEquals($this->matcher->getError(), 'There is no element under path [bar] in value.'); +// } +// +// public function test_error_when_path_in_nested_value_does_not_exist() +// { +// $array = array('foo' => array('bar' => array())); +// $pattern = array('foo' => array('bar' => array('faz' => 'faz value'))); +// +// $this->assertFalse($this->matcher->match($array, $pattern)); +// +// $this->assertEquals($this->matcher->getError(), 'There is no element under path [foo][bar][faz] in value.'); +// } +// +// public function test_error_when_matching_fail() +// { +// $this->assertFalse($this->matcher->match(array('foo' => 'foo value'), array('foo' => 'bar value'))); +// $this->assertEquals($this->matcher->getError(), '"foo value" does not match "bar value".'); +// } +// +// public function test_error_message_when_matching_non_array_value() +// { +// $this->assertFalse($this->matcher->match(new \DateTime(), "@array@")); +// $this->assertEquals($this->matcher->getError(), "object "\\DateTime" is not a valid array."); +// } +// +// public function test_matching_array_to_array_pattern() +// { +// $this->assertTrue($this->matcher->match(array("foo", "bar"), "@array@")); +// $this->assertTrue($this->matcher->match(array("foo"), "@array@.inArray("foo")")); +// $this->assertTrue($this->matcher->match( +// array("foo", array("bar")), +// array( +// "@string@", +// "@array@.inArray("bar")" +// ) +// )); +// } + + public static function positiveMatchData() + { + return array( + [ + [ + [ + 'firstName' => 'Norbert', + 'lastName' => 'Orzechowicz' + ] + ], + '@null@||@json@.match({ + "firstName": "@string@", + "lastName": "@string@" + }) + ' + ], +// [ +// [[ +// 'firstName' => 'Norbert', +// 'lastName' => 'Orzechowicz' +// ]], +// '@json@.match({ +// "firstName": "@string@", +// "lastName": "@string@" +// }) +// ' +// ], +// [ +// [ +// "id" => 1, +// "groups" => [ +// ["name" => 'asdas'], +// ["name" => 1], +// ] +// ], +// [ +// "id" => "@integer@", +// "groups" => ' +// @json@.match({ +// "name": "@string@||@integer@" +// }) +// ' +// ], +// ], +// [ +// [ +// "id" => 1, +// "gallery" => [ +// "id" => 1, +// "images" => [['id' => 1], ['id' => 2]], +// ] +// ], +// [ +// "id" => "@integer@", +// "gallery" => [ +// 'id' => '@integer@', +// 'images' => ' +// @json@.match({ +// "id": "@integer@" +// }) +// ' +// ] +// ], +// ], +// [ +// [ +// "id" => 1, +// "galleries" => [[ +// "id" => 1, +// "cover" => null, +// "images" => [['id' => 1], ['id' => 2]], +// ]] +// ], +// [ +// "id" => "@integer@", +// "galleries" => ' +// @json@.match({ +// "id": "@integer@", +// "cover": \' +// @null@||@json@.match({ +// "id": "@integer@" +// }) +// \', +// "images": \' +// @json@.match({ +// "id": "@integer@" +// }) +// \' +// }) +// ' +// ], +// ] + ); + } + + public static function negativeMatchData() + { + $simpleArr = array( + 'users' => array( + array( + 'firstName' => 'Norbert', + 'lastName' => 'Orzechowicz' + ), + array( + 'firstName' => 'Michał', + 'lastName' => 'Dąbrowski' + ) + ), + true, + false, + 1, + 6.66 + ); + + $simpleDiff = array( + 'users' => array( + array( + 'firstName' => 'Norbert', + 'lastName' => 'Orzechowicz' + ), + array( + 'firstName' => 'Pablo', + 'lastName' => 'Dąbrowski' + ) + ), + true, + false, + 1, + 6.66 + ); + + return array( + array($simpleArr, $simpleDiff), + array(array("status" => "ok", "data" => array(array('foo'))), array("status" => "ok", "data" => array())), + array(array(1), array()), + array(array('key' => 'val'), array('key' => 'val2')), + array(array(1), array(2)), + array(array('foo', 1, 3), array('foo', 2, 3)), + array(array(), array('foo' => 'bar')), + 'unbound array should match one or none elements' => array( + array( + 'users' => array( + array( + 'firstName' => 'Norbert', + 'lastName' => 'Foobar', + ), + ), + true, + false, + 1, + 6.66, + ), + $simpleDiff, + ), + ); + } +} From afb65f16050ac9e335490261b364a0777778b788 Mon Sep 17 00:00:00 2001 From: Michal Kruczek Date: Fri, 21 Jul 2017 19:40:58 +0200 Subject: [PATCH 2/3] Improvements --- README.md | 67 +++- src/Factory/SimpleFactory.php | 79 ++-- src/Matcher/OrMatcher.php | 2 + src/Matcher/Pattern/Expander/Match.php | 11 +- tests/Matcher/JsonObjectMatcherTest.php | 391 +++++++++---------- tests/Matcher/Pattern/Expander/MatchTest.php | 44 +++ 6 files changed, 332 insertions(+), 262 deletions(-) create mode 100644 tests/Matcher/Pattern/Expander/MatchTest.php diff --git a/README.md b/README.md index fbd48050..34158bc8 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,7 @@ $matcher->getError(); // returns null or error message * ``inArray($value)`` * ``oneOf(...$expanders)`` - example usage ``"@string@.oneOf(contains('foo'), contains('bar'), contains('baz'))"`` * ``matchRegex($regex)`` - example usage ``"@string@.matchRegex('/^lorem.+/')"`` +* ``match($subPattern)`` - example usage ``"@json@.match({"id": "@integer@"})"`` ##Example usage @@ -97,7 +98,7 @@ $factory = new SimpleFactory(); $matcher = $factory->createMatcher(); $matcher->match(1, 1); -$matcher->match('string', 'string') +$matcher->match('string', 'string'); ``` ### String matching @@ -111,7 +112,7 @@ $factory = new SimpleFactory(); $matcher = $factory->createMatcher(); $matcher->match('Norbert', '@string@'); -$matcher->match("lorem ipsum dolor", "@string@.startsWith('lorem').contains('ipsum').endsWith('dolor')") +$matcher->match("lorem ipsum dolor", "@string@.startsWith('lorem').contains('ipsum').endsWith('dolor')"); ``` @@ -185,12 +186,12 @@ use Coduo\PHPMatcher\Factory\SimpleFactory; $factory = new SimpleFactory(); $matcher = $factory->createMatcher(); -$matcher->match("@integer@", "@*@"), -$matcher->match("foobar", "@*@"), -$matcher->match(true, "@*@"), -$matcher->match(6.66, "@*@"), -$matcher->match(array("bar"), "@wildcard@"), -$matcher->match(new \stdClass, "@wildcard@"), +$matcher->match("@integer@", "@*@"); +$matcher->match("foobar", "@*@"); +$matcher->match(true, "@*@"); +$matcher->match(6.66, "@*@"); +$matcher->match(array("bar"), "@wildcard@"); +$matcher->match(new \stdClass, "@wildcard@"); ``` ### Expression matching @@ -274,7 +275,7 @@ $matcher->match( '@boolean@', '@double@' ) -) +); ``` ### Json matching @@ -307,7 +308,7 @@ $matcher->match( } ] }' -) +); ``` @@ -355,6 +356,52 @@ XML ); ``` +### Nested Object matching + +```php + +$factory = new SimpleFactory(); +$matcher = $factory->createMatcher(); + +$matcher->match(<<buildParser(); $scalarMatchers = $this->buildScalarMatchers(); - $orMatcher = $this->buildOrMatcher(); + $orMatcher = $this->buildOrMatcher($scalarMatchers); + $jsonMatcher = $this->buildJsonObjectMatcher($scalarMatchers); + $arrayMatcher = $this->buildArrayMatcher($scalarMatchers, $orMatcher, $jsonMatcher, $parser); $chainMatcher = new Matcher\ChainMatcher(array( - $scalarMatchers, $orMatcher, - new Matcher\JsonMatcher($orMatcher), - new Matcher\XmlMatcher($orMatcher), - new Matcher\TextMatcher($scalarMatchers, $this->buildParser()), + $jsonMatcher, + $scalarMatchers, + $arrayMatcher, )); - return $chainMatcher; + $decoratedMatcher = new Matcher\ChainMatcher([ + new Matcher\JsonMatcher($chainMatcher), + new Matcher\XmlMatcher($chainMatcher), + new Matcher\TextMatcher($chainMatcher, $parser), + $chainMatcher, + ]); + + return $decoratedMatcher; + } + + protected function buildArrayMatcher( + Matcher\ValueMatcher $scalarMatchers, + Matcher\ValueMatcher $orMatcher, + Matcher\ValueMatcher $jsonMatcher, + Parser $parser + ) + { + return new Matcher\ArrayMatcher(new Matcher\ChainMatcher([ + $orMatcher, + $scalarMatchers, + $jsonMatcher + ]), $parser); } /** - * @return Matcher\ChainMatcher + * @return Matcher\ValueMatcher */ - protected function buildOrMatcher() + protected function buildOrMatcher(Matcher\ChainMatcher $scalarMatchers) { - $scalarMatchers = $this->buildScalarMatchers(); - $orMatcher = new Matcher\OrMatcher($scalarMatchers); - $arrayMatcher = new Matcher\ArrayMatcher( - new Matcher\ChainMatcher(array( - $orMatcher, - $scalarMatchers, - $this->buildJsonObjectMatcher(), - )), - $this->buildParser() - ); - - $chainMatcher = new Matcher\ChainMatcher(array( - $orMatcher, - $arrayMatcher, - )); + $chainMatcher = new Matcher\ChainMatcher([ + $scalarMatchers, + $this->buildJsonObjectMatcher($scalarMatchers) + ]); - return $chainMatcher; + return new Matcher\OrMatcher($chainMatcher); } - protected function buildJsonObjectMatcher() + protected function buildJsonObjectMatcher(Matcher\ValueMatcher $scalarMatchers) { $parser = $this->buildParser(); - $scalarMatchers = $this->buildScalarMatchers(); - $orMatcher = new Matcher\OrMatcher($scalarMatchers); - $arrayMatcher = new Matcher\ArrayMatcher($orMatcher, $parser); - return new Matcher\JsonObjectMatcher(new Matcher\ChainMatcher([ - $orMatcher, - $scalarMatchers, - $arrayMatcher, - ]), $parser); + return new Matcher\JsonObjectMatcher($scalarMatchers, $parser); } /** @@ -101,6 +108,10 @@ protected function buildScalarMatchers() */ protected function buildParser() { - return new Parser(new Lexer(), new Parser\ExpanderInitializer()); + if ($this->parser) { + return $this->parser; + } + + return $this->parser = new Parser(new Lexer(), new Parser\ExpanderInitializer()); } } diff --git a/src/Matcher/OrMatcher.php b/src/Matcher/OrMatcher.php index 8618c61e..5d61c3e0 100644 --- a/src/Matcher/OrMatcher.php +++ b/src/Matcher/OrMatcher.php @@ -25,6 +25,8 @@ public function __construct(ChainMatcher $chainMatcher) public function match($value, $pattern) { $patterns = explode('||', $pattern); + $patterns = array_map('trim', $patterns); + foreach ($patterns as $childPattern) { if ($this->matchChild($value, $childPattern)) { return true; diff --git a/src/Matcher/Pattern/Expander/Match.php b/src/Matcher/Pattern/Expander/Match.php index 836a8a91..38335f1a 100644 --- a/src/Matcher/Pattern/Expander/Match.php +++ b/src/Matcher/Pattern/Expander/Match.php @@ -18,15 +18,12 @@ final class Match implements PatternExpander */ private $value; - private $matcher; - /** * @param $value */ public function __construct($value) { $this->value = $value; - $this->matcher = (new SimpleFactory)->createMatcher(); } /** @@ -41,14 +38,14 @@ public function match($value) } $match = true; + $matcher = (new SimpleFactory)->createMatcher(); foreach ($value as $singleRowValue) { - if (!$this->matcher->match($singleRowValue, $this->value)) { + if (!$matcher->match($singleRowValue, $this->value)) { $match = false; - $this->error = $this->matcher->getError(); + $this->error = $matcher->getError(); } } - - unset($this->matcher); + unset($matcher); return $match; } diff --git a/tests/Matcher/JsonObjectMatcherTest.php b/tests/Matcher/JsonObjectMatcherTest.php index 5143e546..b5183533 100644 --- a/tests/Matcher/JsonObjectMatcherTest.php +++ b/tests/Matcher/JsonObjectMatcherTest.php @@ -27,86 +27,13 @@ public function test_positive_match_arrays($value, $pattern) $this->assertTrue($this->matcher->match($value, $pattern), $this->matcher->getError()); } -// /** -// * @dataProvider negativeMatchData -// */ -// public function test_negative_match_arrays($value, $pattern) -// { -// $this->assertFalse($this->matcher->match($value, $pattern)); -// } -// -// public function test_negative_match_when_cant_find_matcher_that_can_match_array_element() -// { -// $matcher = new Matcher\ArrayMatcher( -// new Matcher\ChainMatcher(array( -// new Matcher\WildcardMatcher() -// )), -// $parser = new Parser(new Lexer(), new Parser\ExpanderInitializer()) -// ); -// -// $this->assertTrue($matcher->match(array('test' => 1), array('test' => 1))); -// } -// -// public function test_error_when_path_in_pattern_does_not_exist() -// { -// $this->assertFalse($this->matcher->match(array('foo' => 'foo value'), array('bar' => 'bar value'))); -// $this->assertEquals($this->matcher->getError(), 'There is no element under path [foo] in pattern.'); -// } -// -// public function test_error_when_path_in_nested_pattern_does_not_exist() -// { -// $array = array('foo' => array('bar' => array('baz' => 'bar value'))); -// $pattern = array('foo' => array('bar' => array('faz' => 'faz value'))); -// -// $this->assertFalse($this->matcher->match($array, $pattern)); -// -// $this->assertEquals($this->matcher->getError(), 'There is no element under path [foo][bar][baz] in pattern.'); -// } -// -// public function test_error_when_path_in_value_does_not_exist() -// { -// $array = array('foo' => 'foo'); -// $pattern = array('foo' => 'foo', 'bar' => 'bar'); -// -// $this->assertFalse($this->matcher->match($array, $pattern)); -// -// $this->assertEquals($this->matcher->getError(), 'There is no element under path [bar] in value.'); -// } -// -// public function test_error_when_path_in_nested_value_does_not_exist() -// { -// $array = array('foo' => array('bar' => array())); -// $pattern = array('foo' => array('bar' => array('faz' => 'faz value'))); -// -// $this->assertFalse($this->matcher->match($array, $pattern)); -// -// $this->assertEquals($this->matcher->getError(), 'There is no element under path [foo][bar][faz] in value.'); -// } -// -// public function test_error_when_matching_fail() -// { -// $this->assertFalse($this->matcher->match(array('foo' => 'foo value'), array('foo' => 'bar value'))); -// $this->assertEquals($this->matcher->getError(), '"foo value" does not match "bar value".'); -// } -// -// public function test_error_message_when_matching_non_array_value() -// { -// $this->assertFalse($this->matcher->match(new \DateTime(), "@array@")); -// $this->assertEquals($this->matcher->getError(), "object "\\DateTime" is not a valid array."); -// } -// -// public function test_matching_array_to_array_pattern() -// { -// $this->assertTrue($this->matcher->match(array("foo", "bar"), "@array@")); -// $this->assertTrue($this->matcher->match(array("foo"), "@array@.inArray("foo")")); -// $this->assertTrue($this->matcher->match( -// array("foo", array("bar")), -// array( -// "@string@", -// "@array@.inArray("bar")" -// ) -// )); -// } + /** + * @dataProvider negativeMatchData + */ + public function test_negative_match_arrays($value, $pattern) + { + $this->assertFalse($this->matcher->match($value, $pattern)); + } public static function positiveMatchData() { @@ -124,144 +51,186 @@ public static function positiveMatchData() }) ' ], -// [ -// [[ -// 'firstName' => 'Norbert', -// 'lastName' => 'Orzechowicz' -// ]], -// '@json@.match({ -// "firstName": "@string@", -// "lastName": "@string@" -// }) -// ' -// ], -// [ -// [ -// "id" => 1, -// "groups" => [ -// ["name" => 'asdas'], -// ["name" => 1], -// ] -// ], -// [ -// "id" => "@integer@", -// "groups" => ' -// @json@.match({ -// "name": "@string@||@integer@" -// }) -// ' -// ], -// ], -// [ -// [ -// "id" => 1, -// "gallery" => [ -// "id" => 1, -// "images" => [['id' => 1], ['id' => 2]], -// ] -// ], -// [ -// "id" => "@integer@", -// "gallery" => [ -// 'id' => '@integer@', -// 'images' => ' -// @json@.match({ -// "id": "@integer@" -// }) -// ' -// ] -// ], -// ], -// [ -// [ -// "id" => 1, -// "galleries" => [[ -// "id" => 1, -// "cover" => null, -// "images" => [['id' => 1], ['id' => 2]], -// ]] -// ], -// [ -// "id" => "@integer@", -// "galleries" => ' -// @json@.match({ -// "id": "@integer@", -// "cover": \' -// @null@||@json@.match({ -// "id": "@integer@" -// }) -// \', -// "images": \' -// @json@.match({ -// "id": "@integer@" -// }) -// \' -// }) -// ' -// ], -// ] + [ + [[ + 'firstName' => 'Norbert', + 'lastName' => 'Orzechowicz' + ]], + '@json@.match({ + "firstName": "@string@", + "lastName": "@string@" + }) + ' + ], + [ + [ + "id" => 1, + "groups" => [ + ["name" => 'asdas'], + ["name" => 1], + ] + ], + [ + "id" => "@integer@", + "groups" => ' + @json@.match({ + "name": "@string@||@integer@" + }) + ' + ], + ], + [ + [ + "id" => 1, + "gallery" => [ + "id" => 1, + "images" => [['id' => 1], ['id' => 2]], + ] + ], + [ + "id" => "@integer@", + "gallery" => [ + 'id' => '@integer@', + 'images' => ' + @json@.match({ + "id": "@integer@" + }) + ' + ] + ], + ], + [ + [ + "id" => 1, + "galleries" => [[ + "id" => 1, + "cover" => null, + "images" => [['id' => 1], ['id' => 2]], + ]] + ], + [ + "id" => "@integer@", + "galleries" => ' + @json@.match({ + "id": "@integer@", + "cover": \' + @null@||@json@.match({ + "id": "@integer@" + }) + \', + "images": \' + @json@.match({ + "id": "@integer@" + }) + \' + }) + ' + ], + ], + [ + ["photos" => [1, 2, 2, null]], + ["photos" => '@json@.match("@null@||@integer@")'] + ] ); } public static function negativeMatchData() { - $simpleArr = array( - 'users' => array( - array( - 'firstName' => 'Norbert', - 'lastName' => 'Orzechowicz' - ), - array( - 'firstName' => 'Michał', - 'lastName' => 'Dąbrowski' - ) - ), - true, - false, - 1, - 6.66 - ); - - $simpleDiff = array( - 'users' => array( - array( + return array( + [ + [ + [ + 'firstName' => 'Norbert', + 'lastName' => 'Orzechowicz' + ] + ], + '@null@||@json@.match({ + "avatar": "@null@", + "firstName": "@string@", + "lastName": "@string@" + }) + ' + ], + [ + [[ 'firstName' => 'Norbert', 'lastName' => 'Orzechowicz' - ), - array( - 'firstName' => 'Pablo', - 'lastName' => 'Dąbrowski' - ) - ), - true, - false, - 1, - 6.66 - ); - - return array( - array($simpleArr, $simpleDiff), - array(array("status" => "ok", "data" => array(array('foo'))), array("status" => "ok", "data" => array())), - array(array(1), array()), - array(array('key' => 'val'), array('key' => 'val2')), - array(array(1), array(2)), - array(array('foo', 1, 3), array('foo', 2, 3)), - array(array(), array('foo' => 'bar')), - 'unbound array should match one or none elements' => array( - array( - 'users' => array( - array( - 'firstName' => 'Norbert', - 'lastName' => 'Foobar', - ), - ), - true, - false, - 1, - 6.66, - ), - $simpleDiff, - ), + ]], + '@json@.match({ + "firstName": "@string@", + "lastName": "@string@", + "email": "@string@.isEmail()" + }) + ' + ], + [ + [ + "id" => 1, + "groups" => [ + ["name" => 'asdas'], + ["name" => 1], + ] + ], + [ + "id" => "@integer@", + "groups" => ' + @json@.match("@array@.count(0)") + ' + ], + ], + [ + [ + "id" => 1, + "gallery" => [ + "id" => 1, + "images" => [['id' => 1], ['id' => 2]], + ] + ], + [ + "id" => "@integer@", + "gallery" => [ + 'id' => '@integer@', + 'images' => ' + @json@.match({ + "id": "@integer@", + "url": "@string@.isUrl()" + }) + ' + ] + ], + ], + [ + [ + "id" => 1, + "galleries" => [[ + "id" => 1, + "cover" => null, + "images" => [['id' => 1], ['id' => 2]], + ]] + ], + [ + "id" => "@integer@", + "galleries" => ' + @json@.match({ + "id": "@integer@", + "cover": \' + @json@.match({ + "id": "@integer@" + }) + \', + "images": \' + @json@.match({ + "id": "@integer@" + }) + \' + }) + ' + ], + ], + [ + ["photos" => [1, 2, 2.0, null]], + ["photos" => '@json@.match("@null@||@double@")'] + ] ); } } diff --git a/tests/Matcher/Pattern/Expander/MatchTest.php b/tests/Matcher/Pattern/Expander/MatchTest.php new file mode 100644 index 00000000..bb65f4e7 --- /dev/null +++ b/tests/Matcher/Pattern/Expander/MatchTest.php @@ -0,0 +1,44 @@ +assertEquals($expectedResult, $expander->match($haystack)); + } + + public static function examplesProvider() + { + return array( + array("ipsum", array("ipsum"), true), + array(1, array(1, 1, 1), true), + array(array("foo" => "bar"), array(array("foo" => "bar")), true), + ); + } + + /** + * @dataProvider invalidCasesProvider + */ + public function test_error_when_matching_fail($boundary, $value, $errorMessage) + { + $expander = new Match($boundary); + $this->assertFalse($expander->match($value)); + $this->assertEquals($errorMessage, $expander->getError()); + } + + public static function invalidCasesProvider() + { + return array( + array("ipsum", array("ipsum lorem"), "\"ipsum lorem\" does not match \"ipsum\" pattern"), + array("lorem", new \DateTime(), "Match expander require \"array\", got \"\DateTime\"."), + ); + } +} From a54ad5d937ef16d1e9f2bed57d9eba53d21bc4a7 Mon Sep 17 00:00:00 2001 From: Michal Kruczek Date: Fri, 21 Jul 2017 19:46:51 +0200 Subject: [PATCH 3/3] Changed order of matchers --- src/Factory/SimpleFactory.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Factory/SimpleFactory.php b/src/Factory/SimpleFactory.php index cc1ebba9..afed0484 100644 --- a/src/Factory/SimpleFactory.php +++ b/src/Factory/SimpleFactory.php @@ -38,10 +38,10 @@ protected function buildMatchers() )); $decoratedMatcher = new Matcher\ChainMatcher([ + $chainMatcher, new Matcher\JsonMatcher($chainMatcher), new Matcher\XmlMatcher($chainMatcher), new Matcher\TextMatcher($chainMatcher, $parser), - $chainMatcher, ]); return $decoratedMatcher;