Completed
Push — master ( c5cc72...7c6f56 )
by Jean-Christophe
02:02
created

SimpleSerialMatcher   A

Complexity

Total Complexity 2

Size/Duplication

Total Lines 11
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Importance

Changes 0
Metric Value
wmc 2
lcom 1
cbo 1
dl 0
loc 11
rs 10
c 0
b 0
f 0
1
<?php
2
	/**
3
	 * Addendum PHP Reflection Annotations
4
	 * http://code.google.com/p/addendum/
5
	 *
6
	 * Copyright (C) 2006-2009 Jan "johno Suchal <[email protected]>
7
8
	 * This library is free software; you can redistribute it and/or
9
	 * modify it under the terms of the GNU Lesser General Public
10
	 * License as published by the Free Software Foundation; either
11
	 * version 2.1 of the License, or (at your option) any later version.
12
13
	 * This library is distributed in the hope that it will be useful,
14
	 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
	 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16
	 * Lesser General Public License for more details.
17
18
	 * You should have received a copy of the GNU Lesser General Public
19
	 * License along with this library; if not, write to the Free Software
20
	 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
21
	**/
22
23
	class CompositeMatcher {
24
		protected $matchers = array();
25
		private $wasConstructed = false;
26
27
		public function add($matcher) {
28
			$this->matchers[] = $matcher;
29
		}
30
31
		public function matches($string, &$value) {
32
			if(!$this->wasConstructed) {
33
				$this->build();
34
				$this->wasConstructed = true;
35
			}
36
			return $this->match($string, $value);
37
		}
38
39
		protected function build() {}
40
	}
41
42
	class ParallelMatcher extends CompositeMatcher {
43
		protected function match($string, &$value) {
44
			$maxLength = false;
45
			$result = null;
46
			foreach($this->matchers as $matcher) {
47
				$length = $matcher->matches($string, $subvalue);
48
				if($maxLength === false || $length > $maxLength) {
49
					$maxLength = $length;
50
					$result = $subvalue;
51
				}
52
			}
53
			$value = $this->process($result);
54
			return $maxLength;
55
		}
56
57
		protected function process($value) {
58
			return $value;
59
		}
60
	}
61
62
	class SerialMatcher extends CompositeMatcher {
63
		protected function match($string, &$value) {
64
			$results = array();
65
			$total_length = 0;
66
			foreach($this->matchers as $matcher) {
67
				if(($length = $matcher->matches($string, $result)) === false) return false;
68
				$total_length += $length;
69
				$results[] = $result;
70
				$string = substr($string, $length);
71
			}
72
			$value = $this->process($results);
73
			return $total_length;
74
		}
75
76
		protected function process($results) {
77
			return implode('', $results);
78
		}
79
	}
80
81
	class SimpleSerialMatcher extends SerialMatcher {
82
		private $return_part_index;
83
84
		public function __construct($return_part_index = 0) {
85
			$this->return_part_index = $return_part_index;
86
		}
87
88
		public function process($parts) {
89
			return $parts[$this->return_part_index];
90
		}
91
	}
92
93
	class RegexMatcher {
94
		private $regex;
95
96
		public function __construct($regex) {
97
			$this->regex = $regex;
98
		}
99
100
		public function matches($string, &$value) {
101
			if(preg_match("/^{$this->regex}/", $string, $matches)) {
102
				$value = $this->process($matches);
103
				return strlen($matches[0]);
104
			}
105
			$value = false;
106
			return false;
107
		}
108
109
		protected function process($matches) {
110
			return $matches[0];
111
		}
112
	}
113
114
	class AnnotationsMatcher {
115
		public function matches($string, &$annotations) {
116
			$annotations = array();
117
			$annotation_matcher = new AnnotationMatcher;
118
			while(true) {
119
				if(preg_match('/\s(?=@)/', $string, $matches, PREG_OFFSET_CAPTURE)) {
120
					$offset = $matches[0][1] + 1;
121
					$string = substr($string, $offset);
122
				}  else {
123
					return; // no more annotations
124
				}
125
				if(($length = $annotation_matcher->matches($string, $data)) !== false) {
126
					$string = substr($string, $length);
127
					list($name, $params) = $data;
128
					$annotations[$name][] = $params;
129
				}
130
			}
131
		}
132
	}
133
134
	class AnnotationMatcher extends SerialMatcher {
135
		protected function build() {
136
			$this->add(new RegexMatcher('@'));
137
			$this->add(new RegexMatcher('[A-Z][a-zA-Z0-9_]*'));
138
			$this->add(new AnnotationParametersMatcher);
139
		}
140
141
		protected function process($results) {
142
			return array($results[1], $results[2]);
143
		}
144
	}
145
146
	class ConstantMatcher extends RegexMatcher {
147
		private $constant;
148
149
		public function __construct($regex, $constant) {
150
			parent::__construct($regex);
151
			$this->constant = $constant;
152
		}
153
154
		protected function process($matches) {
155
			return $this->constant;
156
		}
157
	}
158
159
	class AnnotationParametersMatcher extends ParallelMatcher {
160
		protected function build() {
161
			$this->add(new ConstantMatcher('', array()));
162
			$this->add(new ConstantMatcher('\(\)', array()));
163
			$params_matcher = new SimpleSerialMatcher(1);
164
			$params_matcher->add(new RegexMatcher('\(\s*'));
165
			$params_matcher->add(new AnnotationValuesMatcher);
166
			$params_matcher->add(new RegexMatcher('\s*\)'));
167
			$this->add($params_matcher);
168
		}
169
	}
170
171
	class AnnotationValuesMatcher extends ParallelMatcher {
172
		protected function build() {
173
			$this->add(new AnnotationTopValueMatcher);
174
			$this->add(new AnnotationHashMatcher);
175
		}
176
	}
177
178
	class AnnotationTopValueMatcher extends AnnotationValueMatcher {
179
		protected function process($value) {
180
			return array('value' => $value);
181
		}
182
	}
183
184
	class AnnotationValueMatcher extends ParallelMatcher {
185
		protected function build() {
186
			$this->add(new ConstantMatcher('true', true));
187
			$this->add(new ConstantMatcher('false', false));
188
			$this->add(new ConstantMatcher('TRUE', true));
189
			$this->add(new ConstantMatcher('FALSE', false));
190
			$this->add(new ConstantMatcher('NULL', null));
191
			$this->add(new ConstantMatcher('null', null));
192
			$this->add(new AnnotationStringMatcher);
193
			$this->add(new AnnotationNumberMatcher);
194
			$this->add(new AnnotationArrayMatcher);
195
			$this->add(new AnnotationStaticConstantMatcher);
196
			$this->add(new NestedAnnotationMatcher);
197
		}
198
	}
199
200
	class AnnotationKeyMatcher extends ParallelMatcher {
201
		protected function build() {
202
			$this->add(new RegexMatcher('[a-zA-Z][a-zA-Z0-9_]*'));
203
			$this->add(new AnnotationStringMatcher);
204
			$this->add(new AnnotationIntegerMatcher);
205
		}
206
	}
207
208
	class AnnotationPairMatcher extends SerialMatcher {
209
		protected function build() {
210
			$this->add(new AnnotationKeyMatcher);
211
			$this->add(new RegexMatcher('\s*=\s*'));
212
			$this->add(new AnnotationValueMatcher);
213
		}
214
215
		protected function process($parts) {
216
			return array($parts[0] => $parts[2]);
217
		}
218
	}
219
220
	class AnnotationHashMatcher extends ParallelMatcher {
221
		protected function build() {
222
			$this->add(new AnnotationPairMatcher);
223
			$this->add(new AnnotationMorePairsMatcher);
224
		}
225
	}
226
227
	class AnnotationMorePairsMatcher extends SerialMatcher {
228
		protected function build() {
229
			$this->add(new AnnotationPairMatcher);
230
			$this->add(new RegexMatcher('\s*,\s*'));
231
			$this->add(new AnnotationHashMatcher);
232
		}
233
234
		protected function match($string, &$value) {
235
			$result = parent::match($string, $value);
236
			return $result;
237
		}
238
239
		public function process($parts) {
240
			return array_merge($parts[0], $parts[2]);
241
		}
242
	}
243
244
	class AnnotationArrayMatcher extends ParallelMatcher {
245
		protected function build() {
246
			$this->add(new ConstantMatcher('{}', array()));
247
			$values_matcher = new SimpleSerialMatcher(1);
248
			$values_matcher->add(new RegexMatcher('\s*{\s*'));
249
			$values_matcher->add(new AnnotationArrayValuesMatcher);
250
			$values_matcher->add(new RegexMatcher('\s*}\s*'));
251
			$this->add($values_matcher);
252
		}
253
	}
254
255
	class AnnotationArrayValuesMatcher extends ParallelMatcher {
256
		protected function build() {
257
			$this->add(new AnnotationArrayValueMatcher);
258
			$this->add(new AnnotationMoreValuesMatcher);
259
		}
260
	}
261
262
	class AnnotationMoreValuesMatcher extends SimpleSerialMatcher {
263
		protected function build() {
264
			$this->add(new AnnotationArrayValueMatcher);
265
			$this->add(new RegexMatcher('\s*,\s*'));
266
			$this->add(new AnnotationArrayValuesMatcher);
267
		}
268
269
		protected function match($string, &$value) {
270
			$result = parent::match($string, $value);
271
			return $result;
272
		}
273
274
		public function process($parts) {
275
			return array_merge($parts[0], $parts[2]);
276
		}
277
	}
278
279
	class AnnotationArrayValueMatcher extends ParallelMatcher {
280
		protected function build() {
281
			$this->add(new AnnotationValueInArrayMatcher);
282
			$this->add(new AnnotationPairMatcher);
283
		}
284
	}
285
286
	class AnnotationValueInArrayMatcher extends AnnotationValueMatcher {
287
		public function process($value) {
288
			return array($value);
289
		}
290
	}
291
292
	class AnnotationStringMatcher extends ParallelMatcher {
293
		protected function build() {
294
			$this->add(new AnnotationSingleQuotedStringMatcher);
295
			$this->add(new AnnotationDoubleQuotedStringMatcher);
296
		}
297
	}
298
299
	class AnnotationNumberMatcher extends RegexMatcher {
300
		public function __construct() {
301
			parent::__construct("-?[0-9]*\.?[0-9]*");
302
		}
303
304
		protected function process($matches) {
305
			$isFloat = strpos($matches[0], '.') !== false;
306
			return $isFloat ? (float) $matches[0] : (int) $matches[0];
307
		}
308
	}
309
310
	class AnnotationIntegerMatcher extends RegexMatcher {
311
		public function __construct() {
312
			parent::__construct("-?[0-9]*");
313
		}
314
315
		protected function process($matches) {
316
			return (int) $matches[0];
317
		}
318
	}
319
320
	class AnnotationSingleQuotedStringMatcher extends RegexMatcher {
321
		public function __construct() {
322
			parent::__construct("'([^']*)'");
323
		}
324
325
		protected function process($matches) {
326
			return $matches[1];
327
		}
328
	}
329
330
	class AnnotationDoubleQuotedStringMatcher extends RegexMatcher {
331
		public function __construct() {
332
			parent::__construct('"([^"]*)"');
333
		}
334
335
		protected function process($matches) {
336
			return $matches[1];
337
		}
338
	}
339
340
	class AnnotationStaticConstantMatcher extends RegexMatcher {
341
		public function __construct() {
342
			parent::__construct('(\w+::\w+)');
343
		}
344
345
		protected function process($matches) {
346
			$name = $matches[1];
347
			if(!defined($name)) {
348
				trigger_error("Constant '$name' used in annotation was not defined.");
349
				return false;
350
			}
351
			return constant($name);
352
		}
353
354
	}
355
356
	class NestedAnnotationMatcher extends AnnotationMatcher {
357
		protected function process($result) {
358
			$builder = new AnnotationsBuilder;
359
			return $builder->instantiateAnnotation($result[1], $result[2]);
360
		}
361
	}
362