Repository navigation
Expand file tree
/
Copy pathFlagRewrites.php
More file actions
111 lines (95 loc) · 3.14 KB
/
Copy pathFlagRewrites.php
File metadata and controls
111 lines (95 loc) · 3.14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
<?php
declare(strict_types=1);
/*
* This file is part of the PHPRegex package.
*
* (c) Younes ENNAJI <younes.ennaji.pro@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPRegex\Optimizer;
use PHPRegex\Parser\Node\AnchorNode;
use PHPRegex\Parser\Node\DotNode;
use PHPRegex\Parser\Node\GroupNode;
use PHPRegex\Parser\Node\NodeInterface;
/**
* The flags of a pattern for Rewriter: the "s" and "m" nothing in the
* pattern reads, which it removes, and whether an inline flag stands
* anywhere, which keeps the rewrites that read the outer flags only out of
* the pattern.
*
* It reads the tree it is handed and keeps nothing between two calls.
*
* @internal
*/
final class FlagRewrites
{
/**
* Removes useless flags from the flags string.
*/
public function removeUselessFlags(string $flags, NodeInterface $pattern): string
{
// Remove 's' flag if there are no dots in the pattern
if (str_contains($flags, 's') && !$this->patternContainsDots($pattern)) {
$flags = str_replace('s', '', $flags);
}
// Remove 'm' flag if there are no ^ or $ anchors
if (str_contains($flags, 'm') && !$this->patternContainsMultilineAnchors($pattern)) {
$flags = str_replace('m', '', $flags);
}
return $flags;
}
/**
* Whether an inline flag stands anywhere under the node. The charset
* analyzer reads the outer flags only, so an inline flag can change
* what a node matches without the disjointness check seeing it, from
* where it stands to the end of its group: possessification then
* stays out of the whole pattern.
*/
public function containsInlineFlags(NodeInterface $node): bool
{
if ($node instanceof GroupNode && null !== $node->flags) {
return true;
}
foreach ($node->getChildren() as $child) {
if ($this->containsInlineFlags($child)) {
return true;
}
}
return false;
}
/**
* Whether a dot stands anywhere in the pattern. Every node is walked
* through its children, so a dot inside a DEFINE body, a script run or
* any other container still reads the flag.
*/
private function patternContainsDots(NodeInterface $node): bool
{
if ($node instanceof DotNode) {
return true;
}
foreach ($node->getChildren() as $child) {
if ($this->patternContainsDots($child)) {
return true;
}
}
return false;
}
/**
* Whether a ^ or $ anchor, the two that depend on multiline mode,
* stands anywhere in the pattern.
*/
private function patternContainsMultilineAnchors(NodeInterface $node): bool
{
if ($node instanceof AnchorNode) {
return '^' === $node->value || '$' === $node->value;
}
foreach ($node->getChildren() as $child) {
if ($this->patternContainsMultilineAnchors($child)) {
return true;
}
}
return false;
}
}