Passed
Pull Request — master (#102)
by Alexander
04:18 queued 02:09
created

CombinedRegexp::compilePatterns()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 2

Importance

Changes 4
Bugs 3 Features 0
Metric Value
cc 2
eloc 8
c 4
b 3
f 0
nc 2
nop 1
dl 0
loc 13
ccs 9
cts 9
cp 1
crap 2
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Strings;
6
7
/**
8
 * `CombinedRegexp` optimizes matching of multiple regular expressions.
9
 * Read more about the concept in
10
 * {@see https://nikic.github.io/2014/02/18/Fast-request-routing-using-regular-expressions.html}.
11
 */
12
final class CombinedRegexp
13
{
14
    private const REGEXP_DELIMITER = '/';
15
    private const QUOTE_REPLACER = '\\/';
16
17
    private string $compiledPattern;
18
19 17
    public function __construct(
20
        /**
21
         * @var string[]
22
         */
23
        private array $patterns,
24
        string $flags = ''
25
    ) {
26 17
        $this->compiledPattern = $this->compilePatterns($this->patterns) . $flags;
27
    }
28
29
    /**
30
     * @return string The compiled pattern.
31
     */
32 17
    public function getCompiledPattern(): string
33
    {
34 17
        return $this->compiledPattern;
35
    }
36
37
    /**
38
     * Returns `true` whether the given string matches any of the patterns, `false` - otherwise.
39
     */
40 11
    public function matches(string $string): bool
41
    {
42 11
        return preg_match($this->compiledPattern, $string) === 1;
43
    }
44
45
    /**
46
     * Returns pattern that matches the given string.
47
     * @throws \Exception if the string does not match any of the patterns.
48
     */
49 5
    public function matchPattern(string $string): string
50
    {
51 5
        return $this->patterns[$this->matchPatternPosition($string)];
52
    }
53
54
    /**
55
     * Returns position of the pattern that matches the given string.
56
     * @throws \Exception if the string does not match any of the patterns.
57
     */
58 8
    public function matchPatternPosition(string $string): int
59
    {
60 8
        $match = preg_match($this->compiledPattern, $string, $matches);
61 8
        if ($match !== 1) {
62
            throw new \Exception(
63
                sprintf(
64
                    'Failed to match pattern "%s" with string "%s".',
65
                    $this->getCompiledPattern(),
66
                    $string,
67
                )
68
            );
69
        }
70
71 8
        return count($matches) - 1;
72
    }
73
74
    /**
75
     * @param string[] $patterns
76
     */
77 17
    private function compilePatterns(array $patterns): string
78
    {
79 17
        $quotedPatterns = [];
80
81 17
        for ($i = 0; $i < count($patterns); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
82 17
            $quotedPatterns[] = $patterns[$i] . str_repeat('()', $i);
83
        }
84 17
        $combinedRegexps = '(?|' . strtr(
85 17
            implode('|', $quotedPatterns),
86 17
            [self::REGEXP_DELIMITER => self::QUOTE_REPLACER]
87 17
        ) . ')';
88
89 17
        return self::REGEXP_DELIMITER . $combinedRegexps . self::REGEXP_DELIMITER;
90
    }
91
}
92