Skip to content

Commit 6f46fe0

Browse files
authored
Added possibility to dump backtrace (#174)
* Added backtrace to all simple matchers * Added backtrace to xml/json/array matchers * Added backtrace tests and solved multiline error messages issue * Added name to chain matchers in order to identify them in backtrace * Json Matcher single line error message
1 parent 97d08f8 commit 6f46fe0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+1510
-207
lines changed

src/Backtrace.php

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Coduo\PHPMatcher;
6+
7+
use Coduo\PHPMatcher\Value\SingleLineString;
8+
use Coduo\ToString\StringConverter;
9+
10+
final class Backtrace
11+
{
12+
private $trace;
13+
14+
public function __construct()
15+
{
16+
$this->trace = [];
17+
}
18+
19+
public function matcherCanMatch(string $name, $value, bool $result) : void
20+
{
21+
$this->trace[] = \sprintf(
22+
'#%d Matcher %s %s match pattern "%s"',
23+
$this->entriesCount(),
24+
$name,
25+
$result ? 'can' : 'can\'t',
26+
new SingleLineString((string) new StringConverter($value))
27+
);
28+
}
29+
30+
public function matcherEntrance(string $name, $value, $pattern) : void
31+
{
32+
$this->trace[] = \sprintf(
33+
'#%d Matcher %s matching value "%s" with "%s" pattern',
34+
$this->entriesCount(),
35+
$name,
36+
new SingleLineString((string) new StringConverter($value)),
37+
new SingleLineString((string) new StringConverter($pattern))
38+
);
39+
}
40+
41+
public function matcherSucceed(string $name, $value, $pattern) : void
42+
{
43+
$this->trace[] = \sprintf(
44+
'#%d Matcher %s successfully matched value "%s" with "%s" pattern',
45+
$this->entriesCount(),
46+
$name,
47+
new SingleLineString((string) new StringConverter($value)),
48+
new SingleLineString((string) new StringConverter($pattern))
49+
);
50+
}
51+
52+
public function matcherFailed(string $name, $value, $pattern, string $error) : void
53+
{
54+
$this->trace[] = \sprintf(
55+
'#%d Matcher %s failed to match value "%s" with "%s" pattern',
56+
$this->entriesCount(),
57+
$name,
58+
new SingleLineString((string) new StringConverter($value)),
59+
new SingleLineString((string) new StringConverter($pattern))
60+
);
61+
62+
$this->trace[] = \sprintf(
63+
'#%d Matcher %s error: %s',
64+
$this->entriesCount(),
65+
$name,
66+
new SingleLineString($error)
67+
);
68+
}
69+
70+
public function __toString() : string
71+
{
72+
return \implode("\n", $this->trace);
73+
}
74+
75+
public function raw() : array
76+
{
77+
return $this->trace;
78+
}
79+
80+
private function entriesCount(): int
81+
{
82+
return \count($this->trace) + 1;
83+
}
84+
}

src/Factory.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,5 @@
66

77
interface Factory
88
{
9-
public function createMatcher() : Matcher;
9+
public function createMatcher(Backtrace $backtrace = null) : Matcher;
1010
}

src/Factory/MatcherFactory.php

Lines changed: 51 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,25 @@
44

55
namespace Coduo\PHPMatcher\Factory;
66

7+
use Coduo\PHPMatcher\Backtrace;
78
use Coduo\PHPMatcher\Factory;
89
use Coduo\PHPMatcher\Lexer;
910
use Coduo\PHPMatcher\Matcher;
1011
use Coduo\PHPMatcher\Parser;
1112

1213
final class MatcherFactory implements Factory
1314
{
14-
public function createMatcher() : Matcher
15+
public function createMatcher(Backtrace $backtrace = null) : Matcher
1516
{
16-
return new Matcher($this->buildMatchers($this->buildParser()));
17+
$matcherBacktrace = $backtrace ? $backtrace : new Backtrace();
18+
19+
return new Matcher($this->buildMatchers($this->buildParser(), $matcherBacktrace), $matcherBacktrace);
1720
}
1821

19-
protected function buildMatchers(Parser $parser) : Matcher\ChainMatcher
22+
protected function buildMatchers(Parser $parser, Backtrace $backtrace) : Matcher\ChainMatcher
2023
{
21-
$scalarMatchers = $this->buildScalarMatchers($parser);
22-
$arrayMatcher = $this->buildArrayMatcher($scalarMatchers, $parser);
24+
$scalarMatchers = $this->buildScalarMatchers($parser, $backtrace);
25+
$arrayMatcher = $this->buildArrayMatcher($scalarMatchers, $parser, $backtrace);
2326

2427
// Matchers are registered in order of matching
2528
// 1) all scalars
@@ -28,48 +31,61 @@ protected function buildMatchers(Parser $parser) : Matcher\ChainMatcher
2831
// 4) or "||"
2932
// 5) full text
3033

31-
$chainMatcher = new Matcher\ChainMatcher([
32-
$scalarMatchers,
33-
new Matcher\JsonMatcher($arrayMatcher),
34-
new Matcher\XmlMatcher($arrayMatcher),
35-
$arrayMatcher,
36-
new Matcher\OrMatcher($scalarMatchers),
37-
new Matcher\TextMatcher($scalarMatchers, $parser),
38-
]);
34+
$chainMatcher = new Matcher\ChainMatcher(
35+
'all',
36+
$backtrace,
37+
[
38+
$scalarMatchers,
39+
new Matcher\JsonMatcher($arrayMatcher, $backtrace),
40+
new Matcher\XmlMatcher($arrayMatcher, $backtrace),
41+
$arrayMatcher,
42+
new Matcher\OrMatcher($backtrace, $scalarMatchers),
43+
new Matcher\TextMatcher($scalarMatchers, $backtrace, $parser),
44+
]
45+
);
3946

4047
return $chainMatcher;
4148
}
4249

43-
protected function buildArrayMatcher(Matcher\ChainMatcher $scalarMatchers, Parser $parser) : Matcher\ArrayMatcher
50+
protected function buildArrayMatcher(Matcher\ChainMatcher $scalarMatchers, Parser $parser, Backtrace $backtrace) : Matcher\ArrayMatcher
4451
{
45-
$orMatcher = new Matcher\OrMatcher($scalarMatchers);
52+
$orMatcher = new Matcher\OrMatcher($backtrace, $scalarMatchers);
4653

4754
return new Matcher\ArrayMatcher(
48-
new Matcher\ChainMatcher([
49-
$orMatcher,
50-
$scalarMatchers,
51-
new Matcher\TextMatcher($scalarMatchers, $parser)
52-
]),
55+
new Matcher\ChainMatcher(
56+
'array',
57+
$backtrace,
58+
[
59+
$orMatcher,
60+
$scalarMatchers,
61+
new Matcher\TextMatcher($scalarMatchers, $backtrace, $parser)
62+
]
63+
),
64+
$backtrace,
5365
$parser
5466
);
5567
}
5668

57-
protected function buildScalarMatchers(Parser $parser) : Matcher\ChainMatcher
69+
protected function buildScalarMatchers(Parser $parser, Backtrace $backtrace) : Matcher\ChainMatcher
5870
{
59-
return new Matcher\ChainMatcher([
60-
new Matcher\CallbackMatcher(),
61-
new Matcher\ExpressionMatcher(),
62-
new Matcher\NullMatcher(),
63-
new Matcher\StringMatcher($parser),
64-
new Matcher\IntegerMatcher($parser),
65-
new Matcher\BooleanMatcher($parser),
66-
new Matcher\DoubleMatcher($parser),
67-
new Matcher\NumberMatcher($parser),
68-
new Matcher\ScalarMatcher(),
69-
new Matcher\WildcardMatcher(),
70-
new Matcher\UuidMatcher($parser),
71-
new Matcher\JsonObjectMatcher($parser)
72-
]);
71+
return new Matcher\ChainMatcher(
72+
'scalars',
73+
$backtrace,
74+
[
75+
new Matcher\CallbackMatcher($backtrace),
76+
new Matcher\ExpressionMatcher($backtrace),
77+
new Matcher\NullMatcher($backtrace),
78+
new Matcher\StringMatcher($backtrace, $parser),
79+
new Matcher\IntegerMatcher($backtrace, $parser),
80+
new Matcher\BooleanMatcher($backtrace, $parser),
81+
new Matcher\DoubleMatcher($backtrace, $parser),
82+
new Matcher\NumberMatcher($backtrace, $parser),
83+
new Matcher\ScalarMatcher($backtrace),
84+
new Matcher\WildcardMatcher($backtrace),
85+
new Matcher\UuidMatcher($backtrace, $parser),
86+
new Matcher\JsonObjectMatcher($backtrace, $parser)
87+
]
88+
);
7389
}
7490

7591
protected function buildParser() : Parser

src/Factory/SimpleFactory.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace Coduo\PHPMatcher\Factory;
66

7+
use Coduo\PHPMatcher\Backtrace;
78
use Coduo\PHPMatcher\Factory;
89
use Coduo\PHPMatcher\Matcher;
910

@@ -12,7 +13,7 @@
1213
*/
1314
class SimpleFactory implements Factory
1415
{
15-
public function createMatcher() : Matcher
16+
public function createMatcher(Backtrace $backtrace = null) : Matcher
1617
{
1718
return (new MatcherFactory())->createMatcher();
1819
}

src/Matcher.php

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,27 @@
88

99
final class Matcher
1010
{
11-
private $matcher;
11+
private $valueMatcher;
1212

13-
public function __construct(ValueMatcher $matcher)
13+
private $backtrace;
14+
15+
public function __construct(ValueMatcher $valueMatcher, Backtrace $backtrace)
1416
{
15-
$this->matcher = $matcher;
17+
$this->valueMatcher = $valueMatcher;
18+
$this->backtrace = $backtrace;
1619
}
1720

1821
public function match($value, $pattern) : bool
1922
{
20-
$result = $this->matcher->match($value, $pattern);
23+
$this->backtrace->matcherEntrance(self::class, $value, $pattern);
24+
25+
$result = $this->valueMatcher->match($value, $pattern);
2126

2227
if ($result === true) {
23-
$this->matcher->clearError();
28+
$this->backtrace->matcherSucceed(self::class, $value, $pattern);
29+
$this->valueMatcher->clearError();
30+
} else {
31+
$this->backtrace->matcherFailed(self::class, $value, $pattern, $this->valueMatcher->getError());
2432
}
2533

2634
return $result;
@@ -31,6 +39,11 @@ public function match($value, $pattern) : bool
3139
*/
3240
public function getError() : ?string
3341
{
34-
return $this->matcher->getError();
42+
return $this->valueMatcher->getError();
43+
}
44+
45+
public function backtrace(): Backtrace
46+
{
47+
return $this->backtrace;
3548
}
3649
}

src/Matcher/ArrayMatcher.php

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace Coduo\PHPMatcher\Matcher;
66

7+
use Coduo\PHPMatcher\Backtrace;
78
use Coduo\PHPMatcher\Exception\Exception;
89
use Coduo\PHPMatcher\Parser;
910
use Coduo\ToString\StringConverter;
@@ -18,25 +19,31 @@ final class ArrayMatcher extends Matcher
1819
const UNIVERSAL_KEY = '@*@';
1920

2021
private $propertyMatcher;
21-
2222
private $accessor;
23-
2423
private $parser;
24+
private $backtrace;
2525

26-
public function __construct(ValueMatcher $propertyMatcher, Parser $parser)
26+
public function __construct(ValueMatcher $propertyMatcher, Backtrace $backtrace, Parser $parser)
2727
{
2828
$this->propertyMatcher = $propertyMatcher;
2929
$this->parser = $parser;
30+
$this->backtrace = $backtrace;
3031
}
3132

3233
public function match($value, $pattern) : bool
3334
{
35+
$this->backtrace->matcherEntrance(self::class, $value, $pattern);
36+
3437
if (parent::match($value, $pattern)) {
38+
$this->backtrace->matcherSucceed(self::class, $value, $pattern);
39+
3540
return true;
3641
}
3742

3843
if (!\is_array($value)) {
3944
$this->error = \sprintf('%s "%s" is not a valid array.', \gettype($value), new StringConverter($value));
45+
$this->backtrace->matcherFailed(self::class, $value, $pattern, $this->error);
46+
4047
return false;
4148
}
4249

@@ -45,9 +52,13 @@ public function match($value, $pattern) : bool
4552
}
4653

4754
if (false === $this->iterateMatch($value, $pattern)) {
55+
$this->backtrace->matcherFailed(self::class, $value, $pattern, $this->error);
56+
4857
return false;
4958
}
5059

60+
$this->backtrace->matcherSucceed(self::class, $value, $pattern);
61+
5162
return true;
5263
}
5364

@@ -247,9 +258,13 @@ private function allExpandersMatch($value, $pattern) : bool
247258
$typePattern = $this->parser->parse($pattern);
248259
if (!$typePattern->matchExpanders($value)) {
249260
$this->error = $typePattern->getError();
261+
$this->backtrace->matcherFailed(self::class, $value, $pattern, $this->error);
262+
250263
return false;
251264
}
252265

266+
$this->backtrace->matcherSucceed(self::class, $value, $pattern);
267+
253268
return true;
254269
}
255270
}

0 commit comments

Comments
 (0)