1 | <?php |
||
14 | class Matcher |
||
15 | { |
||
16 | /** |
||
17 | * @var string |
||
18 | */ |
||
19 | protected $pattern; |
||
20 | |||
21 | /** |
||
22 | * @var string |
||
23 | */ |
||
24 | protected $subject; |
||
25 | |||
26 | /** |
||
27 | * @var array |
||
28 | */ |
||
29 | protected $groups = array(); |
||
30 | |||
31 | private $searchIndex = 0; |
||
32 | |||
33 | /** |
||
34 | * @param string $pattern |
||
35 | * @param string $subject |
||
36 | */ |
||
37 | 3109 | public function __construct($pattern, $subject) |
|
38 | { |
||
39 | 3109 | $this->pattern = str_replace('/', '\/', $pattern); |
|
40 | 3109 | $this->subject = $subject; |
|
41 | 3109 | } |
|
42 | |||
43 | 3109 | protected function doMatch($type = 'find', $offset = 0) |
|
44 | { |
||
45 | 3109 | $final_pattern = '(?:' . $this->pattern . ')'; |
|
46 | switch ($type) { |
||
47 | 3109 | case 'matches': |
|
48 | 3082 | $final_pattern = '^' . $final_pattern . '$'; |
|
49 | 3082 | break; |
|
50 | 3065 | case 'lookingAt': |
|
51 | 3039 | $final_pattern = '^' . $final_pattern; |
|
52 | 3039 | break; |
|
53 | 3045 | case 'find': |
|
54 | default: |
||
55 | // no changes |
||
56 | 3045 | break; |
|
57 | } |
||
58 | 3109 | $final_pattern = '/' . $final_pattern . '/ui'; |
|
59 | |||
60 | 3109 | $search = mb_substr($this->subject, $offset); |
|
61 | |||
62 | 3109 | $result = preg_match($final_pattern, $search, $groups, PREG_OFFSET_CAPTURE); |
|
63 | |||
64 | 3109 | if ($result === 1) { |
|
65 | // Expand $groups into $this->groups, but being multi-byte aware |
||
66 | |||
67 | 2695 | $positions = array(); |
|
68 | |||
69 | 2695 | foreach ($groups as $group) { |
|
70 | 2695 | $positions[] = array( |
|
71 | 2695 | $group[0], |
|
72 | 2695 | $offset + mb_strlen(mb_strcut($search, 0, $group[1])) |
|
73 | ); |
||
74 | } |
||
75 | |||
76 | 2695 | $this->groups = $positions; |
|
77 | } |
||
78 | |||
79 | 3109 | return ($result === 1); |
|
80 | } |
||
81 | |||
82 | /** |
||
83 | * @return bool |
||
84 | */ |
||
85 | 3082 | public function matches() |
|
86 | { |
||
87 | 3082 | return $this->doMatch('matches'); |
|
88 | } |
||
89 | |||
90 | /** |
||
91 | * @return bool |
||
92 | */ |
||
93 | 3039 | public function lookingAt() |
|
94 | { |
||
95 | 3039 | return $this->doMatch('lookingAt'); |
|
96 | } |
||
97 | |||
98 | /** |
||
99 | * @return bool |
||
100 | */ |
||
101 | 3045 | public function find($offset = null) |
|
102 | { |
||
103 | 3045 | if ($offset === null) { |
|
104 | 3044 | $offset = $this->searchIndex; |
|
105 | } |
||
106 | |||
107 | // Increment search index for the next time we call this |
||
108 | 3045 | $this->searchIndex++; |
|
109 | 3045 | return $this->doMatch('find', $offset); |
|
110 | } |
||
111 | |||
112 | /** |
||
113 | * @return int |
||
114 | */ |
||
115 | 141 | public function groupCount() |
|
123 | |||
124 | /** |
||
125 | * @param int $group |
||
126 | * @return string |
||
127 | */ |
||
128 | 85 | public function group($group = null) |
|
129 | { |
||
130 | 85 | if (!isset($group) || $group === null) { |
|
131 | 31 | $group = 0; |
|
132 | } |
||
133 | 85 | return (isset($this->groups[$group][0])) ? $this->groups[$group][0] : null; |
|
134 | } |
||
135 | |||
136 | /** |
||
137 | * @param int|null $group |
||
138 | * @return int |
||
139 | */ |
||
140 | 303 | public function end($group = null) |
|
150 | |||
151 | 231 | public function start($group = null) |
|
162 | |||
163 | /** |
||
164 | * @param string $replacement |
||
165 | * @return string |
||
166 | */ |
||
167 | 64 | public function replaceFirst($replacement) |
|
171 | |||
172 | /** |
||
173 | * @param string $replacement |
||
174 | * @return string |
||
175 | */ |
||
176 | 126 | public function replaceAll($replacement) |
|
180 | |||
181 | /** |
||
182 | * @param string $input |
||
183 | * @return Matcher |
||
184 | */ |
||
185 | 57 | public function reset($input = "") |
|
191 | } |
||
192 |