PregCollection::compile()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 0
dl 0
loc 7
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace Popy\Calendar\Parser\DateLexer;
4
5
use BadMethodCallException;
6
use Popy\Calendar\Parser\DateLexerResult;
7
use Popy\Calendar\Parser\PregDateLexerInterface;
8
9
/**
10
 * Collection/Chain implementation of Preg lexer. It differs from a standard
11
 * collection on few points :
12
 *  - accept only PregDateLexerInterface lexers
13
 *  - will not call tokenizeDate on lexers
14
 *  - will integrate sub lexers' patterns into it's own, in order to perform
15
 *      a single preg_match
16
 * At the end, this collection will perform 3x faster than the standard
17
 * Collection implementation with the same internal lexers.
18
 */
19
class PregCollection extends AbstractPreg
20
{
21
    /**
22
     * Currently built expression.
23
     *
24
     * @var string
25
     */
26
    protected $regexp = '';
27
28
    /**
29
     * Registered lexers.
30
     *
31
     * @var array<PregDateLexerInterface>
32
     */
33
    protected $lexers = [];
34
35
    /**
36
     * Built expressions.
37
     *
38
     * @var array<string>
39
     */
40
    protected $expressions = [];
41
42
    /**
43
     * Final compiled regular expression.
44
     *
45
     * @var string|null
46
     */
47
    protected $compiled;
48
49
    /**
50
     * Registers a pattern in the collection.
51
     *
52
     * @param PregDateLexerInterface $lexer
53
     */
54
    public function register(PregDateLexerInterface $lexer)
55
    {
56
        $this->regexp .= $lexer->getExpression();
57
        $this->lexers[] = $lexer;
58
    }
59
60
    /**
61
     * Closes/finishes the current expression, by adding it to the list of
62
     * finished expressions and resetting it in case another expression has to
63
     * be built.
64
     */
65
    public function close()
66
    {
67
        if ($this->regexp) {
68
            $this->expressions[] = $this->regexp;
69
            $this->regexp = '';
70
        }
71
    }
72
73
    /**
74
     * Compile internal expression into the final one.
75
     */
76
    public function compile()
77
    {
78
        if ($this->compiled !== null) {
79
            return;
80
        }
81
82
        $this->compiled = $this->compileExpressions();
83
    }
84
85
    /**
86
     * @inheritDoc
87
     */
88
    public function getExpression()
89
    {
90
        $this->compile();
91
92
        return $this->compiled;
93
    }
94
95
    /**
96
     * @inheritDoc
97
     */
98
    public function hydrateResult(DateLexerResult $result, $match, $offset = 1)
99
    {
100
        foreach ($this->lexers as $lexer) {
101
            // No more matches : useless to continue
102
            if (!isset($match[$offset])) {
103
                return $offset;
104
            }
105
106
            $offset = $lexer->hydrateResult($result, $match, $offset);
107
        }
108
109
        return $offset;
110
    }
111
112
    /**
113
     * Compile expression list.
114
     *
115
     * @return string
116
     */
117
    protected function compileExpressions()
118
    {
119
        if (count($this->expressions) === 0) {
120
            throw new BadMethodCallException('Can\'t compile an empty expression');
121
        }
122
        if (count($this->expressions) === 1) {
123
            return reset($this->expressions);
124
        }
125
126
        $parts = [];
127
128
        foreach ($this->expressions as $expr) {
129
            $expr = '(?:' . $expr . ')';
130
131
            $parts[] = $expr;
132
        }
133
134
        return '(?:' . implode('|', $parts) . ')';
135
    }
136
}
137