Skip to content

Commit 6b92dc9

Browse files
committed
ValueMatcherModifier, prototyped modifiers parsing
1 parent 4ccbedb commit 6b92dc9

38 files changed

+557
-41
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Coduo\PHPMatcher\Exception;
6+
7+
class UnknownModifierClassException extends \Exception
8+
{
9+
public function __construct(string $name, string $class)
10+
{
11+
parent::__construct(\sprintf(
12+
'Unknown class %s for modifier %s',
13+
$class,
14+
$name
15+
));
16+
}
17+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Coduo\PHPMatcher\Exception;
6+
7+
class UnknownModifierException extends Exception
8+
{
9+
}

src/Factory/SimpleFactory.php

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,14 @@ protected function buildMatchers() : Matcher\ChainMatcher
2121
$scalarMatchers = $this->buildScalarMatchers();
2222
$orMatcher = $this->buildOrMatcher();
2323

24+
$parser = $this->buildParser();
25+
2426
$chainMatcher = new Matcher\ChainMatcher([
2527
$scalarMatchers,
2628
$orMatcher,
27-
new Matcher\JsonMatcher($orMatcher),
29+
new Matcher\JsonMatcher($orMatcher, $parser),
2830
new Matcher\XmlMatcher($orMatcher),
29-
new Matcher\TextMatcher($scalarMatchers, $this->buildParser())
31+
new Matcher\TextMatcher($scalarMatchers, $parser)
3032
]);
3133

3234
return $chainMatcher;
@@ -76,6 +78,6 @@ protected function buildScalarMatchers() : Matcher\ChainMatcher
7678

7779
protected function buildParser() : Parser
7880
{
79-
return new Parser(new Lexer(), new Parser\ExpanderInitializer());
81+
return new Parser(new Lexer(), new Parser\ExpanderInitializer(), new Parser\ModifiersRegistry());
8082
}
8183
}

src/Lexer.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ final class Lexer extends AbstractLexer
2020
const T_COMMA = 10;
2121
const T_COLON = 11;
2222
const T_TYPE_PATTERN = 12;
23+
const T_MODIFIERS_NAMES = 13;
2324

2425
/**
2526
* Lexical catchable patterns.
@@ -33,6 +34,7 @@ protected function getCatchablePatterns() : array
3334
"'(?:[^']|'')*'", // string between ' character
3435
'"(?:[^"]|"")*"', // string between " character,
3536
'@[a-zA-Z0-9\\*]+@', // type pattern
37+
Parser::MODIFIERS_REGEX, // modifiers names
3638
];
3739
}
3840

@@ -102,6 +104,10 @@ protected function getType(&$value) : int
102104
return self::T_EXPANDER_NAME;
103105
}
104106

107+
if ($this->isModifierNamesToken($value)) {
108+
return self::T_MODIFIERS_NAMES;
109+
}
110+
105111
return $type;
106112
}
107113

@@ -134,4 +140,9 @@ protected function isTypePatternToken(string $value) : bool
134140
{
135141
return \substr($value, 0, 1) === '@' && \substr($value, \strlen($value) - 1, 1) === '@' && \strlen($value) > 1;
136142
}
143+
144+
protected function isModifierNamesToken(string $value) : bool
145+
{
146+
return \substr($value, 0, 1) === '|' && \substr($value, \strlen($value) - 1, 1) === '|' && \strlen($value) > 2;
147+
}
137148
}

src/Matcher/ArrayMatcher.php

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,27 +5,42 @@
55
namespace Coduo\PHPMatcher\Matcher;
66

77
use Coduo\PHPMatcher\Exception\Exception;
8+
use Coduo\PHPMatcher\Matcher\Modifier\CaseInsensitive;
9+
use Coduo\PHPMatcher\Matcher\Modifier\IgnoreExtraKeys;
10+
use Coduo\PHPMatcher\Matcher\Modifier\MatcherModifier;
811
use Coduo\PHPMatcher\Parser;
912
use Coduo\ToString\StringConverter;
1013
use Symfony\Component\PropertyAccess\PropertyAccess;
1114
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
1215
use Symfony\Component\PropertyAccess\PropertyPath;
1316

14-
final class ArrayMatcher extends Matcher
17+
final class ArrayMatcher extends ModifiableMatcher
1518
{
1619
const PATTERN = 'array';
1720
const UNBOUNDED_PATTERN = '@...@';
1821

22+
private const SUPPORTED_MODIFIERS = [
23+
IgnoreExtraKeys::NAME,
24+
CaseInsensitive::NAME
25+
];
26+
1927
private $propertyMatcher;
2028

2129
private $accessor;
2230

2331
private $parser;
2432

33+
/**
34+
* @var bool
35+
*/
36+
private $ignoreExtraKeys;
37+
2538
public function __construct(ValueMatcher $propertyMatcher, Parser $parser)
2639
{
2740
$this->propertyMatcher = $propertyMatcher;
2841
$this->parser = $parser;
42+
43+
$this->ignoreExtraKeys = false;
2944
}
3045

3146
public function match($value, $pattern) : bool
@@ -55,13 +70,32 @@ public function canMatch($pattern) : bool
5570
return \is_array($pattern) || $this->isArrayPattern($pattern);
5671
}
5772

73+
public function supportedModifiers(): array
74+
{
75+
return \array_keys(self::SUPPORTED_MODIFIERS);
76+
}
77+
78+
public function getMatchers(): iterable
79+
{
80+
return [$this->propertyMatcher];
81+
}
82+
83+
public function applyModifier(MatcherModifier $modifier): void
84+
{
85+
switch ($modifier->getName()) {
86+
case IgnoreExtraKeys::NAME:
87+
$this->ignoreExtraKeys = true;
88+
break;
89+
}
90+
}
91+
5892
private function isArrayPattern($pattern) : bool
5993
{
6094
if (!\is_string($pattern)) {
6195
return false;
6296
}
6397

64-
return $this->parser->hasValidSyntax($pattern) && $this->parser->parse($pattern)->is(self::PATTERN);
98+
return $this->parser->hasValidSyntax($pattern) && $this->parser->parseTypePattern($pattern)->is(self::PATTERN);
6599
}
66100

67101
private function iterateMatch(array $values, array $patterns, string $parentPath = '') : bool
@@ -126,7 +160,7 @@ function ($item) use ($skipPattern) {
126160
);
127161

128162
$notExistingKeys = $this->findNotExistingKeys($pattern, $values);
129-
if (\count($notExistingKeys) > 0) {
163+
if (\count($notExistingKeys) > 0 && !$this->ignoreExtraKeys) {
130164
$keyNames = \array_keys($notExistingKeys);
131165
$path = $this->formatFullPath($parentPath, $this->formatAccessPath($keyNames[0]));
132166
$this->setMissingElementInError('value', $path);
@@ -148,7 +182,7 @@ private function findNotExistingKeys(array $pattern, array $values) : array
148182
}
149183

150184
try {
151-
$typePattern = $this->parser->parse($pattern);
185+
$typePattern = $this->parser->parseTypePattern($pattern);
152186
} catch (Exception $e) {
153187
return true;
154188
} catch (\Throwable $t) {
@@ -237,7 +271,7 @@ private function shouldSkippValueMatchingFor($lastPattern) : bool
237271

238272
private function allExpandersMatch($value, $pattern) : bool
239273
{
240-
$typePattern = $this->parser->parse($pattern);
274+
$typePattern = $this->parser->parseTypePattern($pattern);
241275
if (!$typePattern->matchExpanders($value)) {
242276
$this->error = $typePattern->getError();
243277
return false;

src/Matcher/BooleanMatcher.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,6 @@ public function canMatch($pattern) : bool
3434
return false;
3535
}
3636

37-
return $this->parser->hasValidSyntax($pattern) && $this->parser->parse($pattern)->is(self::PATTERN);
37+
return $this->parser->hasValidSyntax($pattern) && $this->parser->parseTypePattern($pattern)->is(self::PATTERN);
3838
}
3939
}

src/Matcher/ChainMatcher.php

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@
44

55
namespace Coduo\PHPMatcher\Matcher;
66

7+
use Coduo\PHPMatcher\Matcher\Modifier\MatcherModifier;
8+
use Coduo\PHPMatcher\Parser\ModifiersRegistry;
79
use Coduo\ToString\StringConverter;
810

9-
final class ChainMatcher extends Matcher
11+
final class ChainMatcher extends ModifiableMatcher
1012
{
1113
/**
1214
* @var ValueMatcher[]
@@ -53,4 +55,19 @@ public function canMatch($pattern) : bool
5355
{
5456
return true;
5557
}
58+
59+
public function supportedModifiers(): array
60+
{
61+
return \array_keys(ModifiersRegistry::BUILT_IN_MODIFIERS);
62+
}
63+
64+
public function getMatchers(): iterable
65+
{
66+
return $this->matchers;
67+
}
68+
69+
public function applyModifier(MatcherModifier $modifier): void
70+
{
71+
72+
}
5673
}

src/Matcher/DoubleMatcher.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public function match($value, $pattern) : bool
2525
return false;
2626
}
2727

28-
$typePattern = $this->parser->parse($pattern);
28+
$typePattern = $this->parser->parseTypePattern($pattern);
2929
if (!$typePattern->matchExpanders($value)) {
3030
$this->error = $typePattern->getError();
3131
return false;
@@ -40,6 +40,6 @@ public function canMatch($pattern) : bool
4040
return false;
4141
}
4242

43-
return $this->parser->hasValidSyntax($pattern) && $this->parser->parse($pattern)->is(self::PATTERN);
43+
return $this->parser->hasValidSyntax($pattern) && $this->parser->parseTypePattern($pattern)->is(self::PATTERN);
4444
}
4545
}

src/Matcher/IntegerMatcher.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public function match($value, $pattern) : bool
2525
return false;
2626
}
2727

28-
$typePattern = $this->parser->parse($pattern);
28+
$typePattern = $this->parser->parseTypePattern($pattern);
2929
if (!$typePattern->matchExpanders($value)) {
3030
$this->error = $typePattern->getError();
3131
return false;
@@ -40,6 +40,6 @@ public function canMatch($pattern) : bool
4040
return false;
4141
}
4242

43-
return $this->parser->hasValidSyntax($pattern) && $this->parser->parse($pattern)->is(self::PATTERN);
43+
return $this->parser->hasValidSyntax($pattern) && $this->parser->parseTypePattern($pattern)->is(self::PATTERN);
4444
}
4545
}

src/Matcher/JsonMatcher.php

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,33 @@
44

55
namespace Coduo\PHPMatcher\Matcher;
66

7+
use Coduo\PHPMatcher\Matcher\Modifier\CaseInsensitive;
8+
use Coduo\PHPMatcher\Matcher\Modifier\IgnoreExtraKeys;
9+
use Coduo\PHPMatcher\Matcher\Modifier\MatcherModifier;
710
use Coduo\PHPMatcher\Matcher\Pattern\Assert\Json;
11+
use Coduo\PHPMatcher\Parser;
812

9-
final class JsonMatcher extends Matcher
13+
final class JsonMatcher extends ModifiableMatcher
1014
{
15+
private const SUPPORTED_MODIFIERS = [
16+
IgnoreExtraKeys::NAME,
17+
CaseInsensitive::NAME
18+
];
19+
20+
/**
21+
* @var ValueMatcher
22+
*/
1123
private $matcher;
1224

13-
public function __construct(ValueMatcher $matcher)
25+
/**
26+
* @var Parser
27+
*/
28+
private $parser;
29+
30+
public function __construct(ValueMatcher $matcher, Parser $parser)
1431
{
1532
$this->matcher = $matcher;
33+
$this->parser = $parser;
1634
}
1735

1836
public function match($value, $pattern) : bool
@@ -21,6 +39,12 @@ public function match($value, $pattern) : bool
2139
return true;
2240
}
2341

42+
foreach ($this->parser->parseModifiers($pattern) as $modifier) {
43+
$this->modify($modifier);
44+
}
45+
46+
$pattern = $this->parser->trimModifiers($pattern);
47+
2448
if (!Json::isValid($value)) {
2549
$this->error = \sprintf('Invalid given JSON of value. %s', $this->getErrorMessage());
2650
return false;
@@ -43,9 +67,27 @@ public function match($value, $pattern) : bool
4367

4468
public function canMatch($pattern) : bool
4569
{
70+
if (\is_string($pattern)) {
71+
$pattern = $this->parser->trimModifiers($pattern);
72+
}
73+
4674
return Json::isValidPattern($pattern);
4775
}
4876

77+
public function supportedModifiers(): array
78+
{
79+
return \array_keys(self::SUPPORTED_MODIFIERS);
80+
}
81+
82+
public function getMatchers(): iterable
83+
{
84+
return [$this->matcher];
85+
}
86+
87+
public function applyModifier(MatcherModifier $modifier): void
88+
{
89+
}
90+
4991
private function getErrorMessage()
5092
{
5193
switch (\json_last_error()) {

0 commit comments

Comments
 (0)