Skip to content

Commit

Permalink
2.3 - Add repeat expander feature (#109)
Browse files Browse the repository at this point in the history
* RepeatExpander : feature + tests

* RepeatExpander: update README

* RepeatExpander : Fix readme and new failure test
  • Loading branch information
Leakless authored and norberttech committed Oct 15, 2017
1 parent 79a27d1 commit 7b2cb77
Show file tree
Hide file tree
Showing 4 changed files with 228 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
147 changes: 147 additions & 0 deletions src/Matcher/Pattern/Expander/Repeat.php
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);

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)
{
$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;
}
}
1 change: 1 addition & 0 deletions src/Parser/ExpanderInitializer.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ final class ExpanderInitializer
Expander\OneOf::NAME => Expander\OneOf::class,
Expander\Optional::NAME => Expander\Optional::class,
Expander\StartsWith::NAME => Expander\StartsWith::class,
Expander\Repeat::NAME => Expander\Repeat::class,
];

/**
Expand Down
79 changes: 79 additions & 0 deletions tests/Matcher/Pattern/Expander/RepeatTest.php
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(
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 "".')
);
}
}

0 comments on commit 7b2cb77

Please sign in to comment.