1: <?php
2:
3: 4: 5: 6: 7: 8: 9: 10:
11:
12: namespace Symfony\Component\Finder\Expression;
13:
14: 15: 16:
17: class Regex implements ValueInterface
18: {
19: const START_FLAG = '^';
20: const END_FLAG = '$';
21: const BOUNDARY = '~';
22: const JOKER = '.*';
23: const ESCAPING = '\\';
24:
25: 26: 27:
28: private $pattern;
29:
30: 31: 32:
33: private $options;
34:
35: 36: 37:
38: private $startFlag;
39:
40: 41: 42:
43: private $endFlag;
44:
45: 46: 47:
48: private $startJoker;
49:
50: 51: 52:
53: private $endJoker;
54:
55: 56: 57: 58: 59: 60: 61:
62: public static function create($expr)
63: {
64: if (preg_match('/^(.{3,}?)([imsxuADU]*)$/', $expr, $m)) {
65: $start = substr($m[1], 0, 1);
66: $end = substr($m[1], -1);
67:
68: if (($start === $end && !preg_match('/[*?[:alnum:] \\\\]/', $start)) || ($start === '{' && $end === '}')) {
69: return new self(substr($m[1], 1, -1), $m[2], $end);
70: }
71: }
72:
73: throw new \InvalidArgumentException('Given expression is not a regex.');
74: }
75:
76: 77: 78: 79: 80:
81: public function __construct($pattern, $options = '', $delimiter = null)
82: {
83: if (null !== $delimiter) {
84:
85: $pattern = str_replace('\\'.$delimiter, $delimiter, $pattern);
86: }
87:
88: $this->parsePattern($pattern);
89: $this->options = $options;
90: }
91:
92: 93: 94:
95: public function __toString()
96: {
97: return $this->render();
98: }
99:
100: 101: 102:
103: public function render()
104: {
105: return self::BOUNDARY
106: .$this->renderPattern()
107: .self::BOUNDARY
108: .$this->options;
109: }
110:
111: 112: 113:
114: public function renderPattern()
115: {
116: return ($this->startFlag ? self::START_FLAG : '')
117: .($this->startJoker ? self::JOKER : '')
118: .str_replace(self::BOUNDARY, '\\'.self::BOUNDARY, $this->pattern)
119: .($this->endJoker ? self::JOKER : '')
120: .($this->endFlag ? self::END_FLAG : '');
121: }
122:
123: 124: 125:
126: public function isCaseSensitive()
127: {
128: return !$this->hasOption('i');
129: }
130:
131: 132: 133:
134: public function getType()
135: {
136: return Expression::TYPE_REGEX;
137: }
138:
139: 140: 141:
142: public function prepend($expr)
143: {
144: $this->pattern = $expr.$this->pattern;
145:
146: return $this;
147: }
148:
149: 150: 151:
152: public function append($expr)
153: {
154: $this->pattern .= $expr;
155:
156: return $this;
157: }
158:
159: 160: 161: 162: 163:
164: public function hasOption($option)
165: {
166: return false !== strpos($this->options, $option);
167: }
168:
169: 170: 171: 172: 173:
174: public function addOption($option)
175: {
176: if (!$this->hasOption($option)) {
177: $this->options.= $option;
178: }
179:
180: return $this;
181: }
182:
183: 184: 185: 186: 187:
188: public function removeOption($option)
189: {
190: $this->options = str_replace($option, '', $this->options);
191:
192: return $this;
193: }
194:
195: 196: 197: 198: 199:
200: public function setStartFlag($startFlag)
201: {
202: $this->startFlag = $startFlag;
203:
204: return $this;
205: }
206:
207: 208: 209:
210: public function hasStartFlag()
211: {
212: return $this->startFlag;
213: }
214:
215: 216: 217: 218: 219:
220: public function setEndFlag($endFlag)
221: {
222: $this->endFlag = (bool) $endFlag;
223:
224: return $this;
225: }
226:
227: 228: 229:
230: public function hasEndFlag()
231: {
232: return $this->endFlag;
233: }
234:
235: 236: 237: 238: 239:
240: public function setStartJoker($startJoker)
241: {
242: $this->startJoker = $startJoker;
243:
244: return $this;
245: }
246:
247: 248: 249:
250: public function hasStartJoker()
251: {
252: return $this->startJoker;
253: }
254:
255: 256: 257: 258: 259:
260: public function setEndJoker($endJoker)
261: {
262: $this->endJoker = (bool) $endJoker;
263:
264: return $this;
265: }
266:
267: 268: 269:
270: public function hasEndJoker()
271: {
272: return $this->endJoker;
273: }
274:
275: 276: 277: 278: 279:
280: public function replaceJokers($replacement)
281: {
282: $replace = function ($subject) use ($replacement) {
283: $subject = $subject[0];
284: $replace = 0 === substr_count($subject, '\\') % 2;
285:
286: return $replace ? str_replace('.', $replacement, $subject) : $subject;
287: };
288:
289: $this->pattern = preg_replace_callback('~[\\\\]*\\.~', $replace, $this->pattern);
290:
291: return $this;
292: }
293:
294: 295: 296:
297: private function parsePattern($pattern)
298: {
299: if ($this->startFlag = self::START_FLAG === substr($pattern, 0, 1)) {
300: $pattern = substr($pattern, 1);
301: }
302:
303: if ($this->startJoker = self::JOKER === substr($pattern, 0, 2)) {
304: $pattern = substr($pattern, 2);
305: }
306:
307: if ($this->endFlag = (self::END_FLAG === substr($pattern, -1) && self::ESCAPING !== substr($pattern, -2, -1))) {
308: $pattern = substr($pattern, 0, -1);
309: }
310:
311: if ($this->endJoker = (self::JOKER === substr($pattern, -2) && self::ESCAPING !== substr($pattern, -3, -2))) {
312: $pattern = substr($pattern, 0, -2);
313: }
314:
315: $this->pattern = $pattern;
316: }
317: }
318: