-
-
Notifications
You must be signed in to change notification settings - Fork 86
2.3 - Add repeat expander feature #109
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -84,6 +84,7 @@ $matcher->getError(); // returns null or error message | |
* ``oneOf(...$expanders)`` - example usage ``"@[email protected](contains('foo'), contains('bar'), contains('baz'))"`` | ||
* ``matchRegex($regex)`` - example usage ``"@[email protected]('/^lorem.+/')"`` | ||
* ``optional()`` - work's only with ``ArrayMatcher``, ``JsonMatcher`` and ``XmlMatcher`` | ||
* ``repeat($pattern, $isStrict = true)`` - example usage ``'@[email protected]({"name": "foe"})'`` or ``"@[email protected]('@string@')"`` | ||
|
||
##Example usage | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
<?php | ||
|
||
namespace Coduo\PHPMatcher\Matcher\Pattern\Expander; | ||
|
||
use Coduo\PHPMatcher\Factory\SimpleFactory; | ||
use Coduo\PHPMatcher\Matcher; | ||
use Coduo\PHPMatcher\Matcher\Pattern\PatternExpander; | ||
use Coduo\ToString\StringConverter; | ||
|
||
final class Repeat implements PatternExpander | ||
{ | ||
const NAME = 'repeat'; | ||
|
||
/** | ||
* @var null|string | ||
*/ | ||
private $error; | ||
|
||
/** | ||
* @var string | ||
*/ | ||
private $pattern; | ||
|
||
/** | ||
* @var bool | ||
*/ | ||
private $isStrict; | ||
|
||
/** | ||
* @var bool | ||
*/ | ||
private $isScalar; | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public static function is($name) | ||
{ | ||
return self::NAME === $name; | ||
} | ||
|
||
/** | ||
* @param $value | ||
*/ | ||
public function __construct($pattern, $isStrict = true) | ||
{ | ||
if (!is_string($pattern)) { | ||
throw new \InvalidArgumentException("Repeat pattern must be a string."); | ||
} | ||
|
||
$this->pattern = $pattern; | ||
$this->isStrict = $isStrict; | ||
$this->isScalar = true; | ||
|
||
$json = json_decode($pattern, true); | ||
|
||
if ($json !== null && json_last_error() === JSON_ERROR_NONE) { | ||
$this->pattern = $json; | ||
$this->isScalar = false; | ||
} | ||
} | ||
|
||
/** | ||
* @param $values | ||
* @return bool | ||
*/ | ||
public function match($values) | ||
{ | ||
if (!is_array($values)) { | ||
$this->error = sprintf("Repeat expander require \"array\", got \"%s\".", new StringConverter($values)); | ||
return false; | ||
} | ||
|
||
$factory = new SimpleFactory(); | ||
$matcher = $factory->createMatcher(); | ||
|
||
if ($this->isScalar) { | ||
return $this->matchScalar($values, $matcher); | ||
} | ||
|
||
return $this->matchJson($values, $matcher); | ||
} | ||
|
||
/** | ||
* @return string|null | ||
*/ | ||
public function getError() | ||
{ | ||
return $this->error; | ||
} | ||
|
||
/** | ||
* @param array $values | ||
* @param Matcher $matcher | ||
* @return bool | ||
*/ | ||
private function matchScalar(array $values, Matcher $matcher) | ||
{ | ||
foreach ($values as $index => $value) { | ||
$match = $matcher->match($value, $this->pattern); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we only match value, is following pattern failing ?
[
{
"foo": "bar"
},
{
"test": "bar"
}
]
`` There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure to understand what you're talking about here ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just ignore this comment |
||
|
||
if (!$match) { | ||
$this->error = sprintf("Repeat expander, entry n�%d, find error : %s", $index, $matcher->getError()); | ||
return false; | ||
} | ||
} | ||
|
||
return true; | ||
} | ||
|
||
/** | ||
* @param array $values | ||
* @param Matcher $matcher | ||
* @return bool | ||
*/ | ||
private function matchJson(array $values, Matcher $matcher) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should maybe allow this method to be recursive to match nested objects ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. {
"offers": "@[email protected]({\"subscription\": \"@[email protected](\"date\": \"@[email protected]()\")\"})"
} This case will work with actual implementation ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This case should work with the nested array.repeat syntax 'cause I just make a call to php-matcher factory. But you're right, maybe allowing nested objects would be more clever. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is not yet supported, right? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes I'm sorry, didn't have the time to do it |
||
{ | ||
$patternKeys = array_keys($this->pattern); | ||
$patternKeysLength = count($patternKeys); | ||
|
||
foreach ($values as $index => $value) { | ||
$valueKeys = array_keys($value); | ||
$valueKeysLength = count($valueKeys); | ||
|
||
if ($this->isStrict && $patternKeysLength !== $valueKeysLength) { | ||
$this->error = sprintf("Repeat expander expect to have %d keys in array but get : %d", $patternKeysLength, $valueKeysLength); | ||
return false; | ||
} | ||
|
||
foreach ($patternKeys as $key) { | ||
if (!array_key_exists($key, $value)) { | ||
$this->error = sprintf("Repeat expander, entry n�%d, require \"array\" to have key \"%s\".", $index, $key); | ||
return false; | ||
} | ||
|
||
$match = $matcher->match($value[$key], $this->pattern[$key]); | ||
|
||
if (!$match) { | ||
$this->error = sprintf("Repeat expander, entry n�%d, key \"%s\", find error : %s", $index, $key, $matcher->getError()); | ||
return false; | ||
} | ||
} | ||
} | ||
|
||
return true; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
<?php | ||
|
||
namespace Coduo\PHPMatcher\Tests\Matcher\Pattern\Expander; | ||
|
||
use Coduo\PHPMatcher\Matcher\Pattern\Expander\Repeat; | ||
|
||
class RepeatTest extends \PHPUnit_Framework_TestCase | ||
{ | ||
/** | ||
* @dataProvider examplesProvider | ||
*/ | ||
public function test_matching_values($needle, $haystack, $expectedResult, $isStrict = true) | ||
{ | ||
$expander = new Repeat($needle, $isStrict); | ||
$this->assertEquals($expectedResult, $expander->match($haystack)); | ||
} | ||
|
||
public static function examplesProvider() | ||
{ | ||
$jsonPattern = '{"name": "@string@", "activated": "@boolean@"}'; | ||
|
||
$jsonTest = array( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You could use "new" PHP short array syntax There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes probably, but the whole project seems to use the older There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. On master branch we are using |
||
array("name" => "toto", "activated" => true), | ||
array("name" => "titi", "activated" => false), | ||
array("name" => "tate", "activated" => true) | ||
); | ||
|
||
$scalarPattern = "@string@"; | ||
$scalarTest = array( | ||
"toto", | ||
"titi", | ||
"tata" | ||
); | ||
|
||
$strictTest = array( | ||
array("name" => "toto", "activated" => true, "offset" => "offset") | ||
); | ||
|
||
return array( | ||
array($jsonPattern, $jsonTest, true), | ||
array($scalarPattern, $scalarTest, true), | ||
array($jsonPattern, $strictTest, true, false) | ||
); | ||
} | ||
|
||
/** | ||
* @dataProvider invalidCasesProvider | ||
*/ | ||
public function test_error_when_matching_fail($boundary, $value, $errorMessage) | ||
{ | ||
$expander = new Repeat($boundary); | ||
$this->assertFalse($expander->match($value)); | ||
$this->assertEquals($errorMessage, $expander->getError()); | ||
} | ||
|
||
public static function invalidCasesProvider() | ||
{ | ||
$pattern = '{"name": "@string@", "activated": "@boolean@"}'; | ||
|
||
$valueTest = array( | ||
array("name" => 1, "activated" => "yes") | ||
); | ||
|
||
$keyTest = array( | ||
array("offset" => true, "foe" => "bar") | ||
); | ||
|
||
$strictTest = array( | ||
array("name" => 1, "activated" => "yes", "offset" => true) | ||
); | ||
|
||
return array( | ||
array($pattern, $valueTest, 'Repeat expander, entry n�0, key "name", find error : integer "1" is not a valid string.'), | ||
array($pattern, $keyTest, 'Repeat expander, entry n�0, require "array" to have key "name".'), | ||
array($pattern, $strictTest, 'Repeat expander expect to have 2 keys in array but get : 3'), | ||
array($pattern, "", 'Repeat expander require "array", got "".') | ||
); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This exception is not covered by tests
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will fix that ASAP.