1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14:
15:
16: namespace TokenReflection;
17:
18: use TokenReflection\Exception, TokenReflection\Stream\StreamBase as Stream;
19:
20: 21: 22:
23: class ReflectionFileNamespace extends ReflectionElement
24: {
25: 26: 27: 28: 29:
30: private $classes = array();
31:
32: 33: 34: 35: 36:
37: private $constants = array();
38:
39: 40: 41: 42: 43:
44: private $functions = array();
45:
46: 47: 48: 49: 50:
51: private $aliases = array();
52:
53: 54: 55: 56: 57:
58: public function getClasses()
59: {
60: return $this->classes;
61: }
62:
63: 64: 65: 66: 67:
68: public function getConstants()
69: {
70: return $this->constants;
71: }
72:
73: 74: 75: 76: 77:
78: public function getFunctions()
79: {
80: return $this->functions;
81: }
82:
83: 84: 85: 86: 87:
88: public function getNamespaceAliases()
89: {
90: return $this->aliases;
91: }
92:
93: 94: 95: 96: 97: 98: 99: 100:
101: protected function processParent(IReflection $parent, Stream $tokenStream)
102: {
103: if (!$parent instanceof ReflectionFile) {
104: throw new Exception\ParseException($this, $tokenStream, 'The parent object has to be an instance of TokenReflection\ReflectionFile.', Exception\ParseException::INVALID_PARENT);
105: }
106:
107: return parent::processParent($parent, $tokenStream);
108: }
109:
110: 111: 112: 113: 114: 115: 116:
117: protected function parse(Stream $tokenStream, IReflection $parent)
118: {
119: return $this->parseName($tokenStream);
120: }
121:
122: 123: 124: 125: 126: 127: 128:
129: protected function (Stream $tokenStream, IReflection $parent)
130: {
131: if (!$tokenStream->is(T_NAMESPACE)) {
132: $this->docComment = new ReflectionAnnotation($this);
133: return $this;
134: } else {
135: return parent::parseDocComment($tokenStream, $parent);
136: }
137: }
138:
139: 140: 141: 142: 143: 144: 145:
146: protected function parseName(Stream $tokenStream)
147: {
148: if (!$tokenStream->is(T_NAMESPACE)) {
149: $this->name = ReflectionNamespace::NO_NAMESPACE_NAME;
150: return $this;
151: }
152:
153: $tokenStream->skipWhitespaces();
154:
155: $name = '';
156:
157: while (true) {
158: switch ($tokenStream->getType()) {
159:
160: case T_STRING:
161: case T_NS_SEPARATOR:
162: $name .= $tokenStream->getTokenValue();
163: break;
164: default:
165:
166: break 2;
167: }
168:
169: $tokenStream->skipWhitespaces(true);
170: }
171:
172: $name = ltrim($name, '\\');
173:
174: if (empty($name)) {
175: $this->name = ReflectionNamespace::NO_NAMESPACE_NAME;
176: } else {
177: $this->name = $name;
178: }
179:
180: if (!$tokenStream->is(';') && !$tokenStream->is('{')) {
181: throw new Exception\ParseException($this, $tokenStream, 'Invalid namespace name end, expecting ";" or "{".', Exception\ParseException::UNEXPECTED_TOKEN);
182: }
183:
184: $tokenStream->skipWhitespaces();
185:
186: return $this;
187: }
188:
189: 190: 191: 192: 193: 194: 195: 196:
197: protected function parseChildren(Stream $tokenStream, IReflection $parent)
198: {
199: static $skipped = array(T_WHITESPACE => true, T_COMMENT => true, T_DOC_COMMENT => true);
200: $depth = 0;
201:
202: $firstChild = null;
203:
204: while (true) {
205: switch ($tokenStream->getType()) {
206: case T_USE:
207: while (true) {
208: $namespaceName = '';
209: $alias = null;
210:
211: $tokenStream->skipWhitespaces(true);
212:
213: while (true) {
214: switch ($tokenStream->getType()) {
215: case T_STRING:
216: case T_NS_SEPARATOR:
217: $namespaceName .= $tokenStream->getTokenValue();
218: break;
219: default:
220: break 2;
221: }
222: $tokenStream->skipWhitespaces(true);
223: }
224: $namespaceName = ltrim($namespaceName, '\\');
225:
226: if (empty($namespaceName)) {
227: throw new Exception\ParseException($this, $tokenStream, 'Imported namespace name could not be determined.', Exception\ParseException::LOGICAL_ERROR);
228: } elseif ('\\' === substr($namespaceName, -1)) {
229: throw new Exception\ParseException($this, $tokenStream, sprintf('Invalid namespace name "%s".', $namespaceName), Exception\ParseException::LOGICAL_ERROR);
230: }
231:
232: if ($tokenStream->is(T_AS)) {
233:
234: $tokenStream->skipWhitespaces(true);
235:
236: if (!$tokenStream->is(T_STRING)) {
237: throw new Exception\ParseException($this, $tokenStream, sprintf('The imported namespace "%s" seems aliased but the alias name could not be determined.', $namespaceName), Exception\ParseException::LOGICAL_ERROR);
238: }
239:
240: $alias = $tokenStream->getTokenValue();
241:
242: $tokenStream->skipWhitespaces(true);
243: } else {
244:
245: if (false !== ($pos = strrpos($namespaceName, '\\'))) {
246: $alias = substr($namespaceName, $pos + 1);
247: } else {
248: $alias = $namespaceName;
249: }
250: }
251:
252: if (isset($this->aliases[$alias])) {
253: throw new Exception\ParseException($this, $tokenStream, sprintf('Namespace alias "%s" already defined.', $alias), Exception\ParseException::LOGICAL_ERROR);
254: }
255:
256: $this->aliases[$alias] = $namespaceName;
257:
258: $type = $tokenStream->getType();
259: if (';' === $type) {
260: $tokenStream->skipWhitespaces();
261: break 2;
262: } elseif (',' === $type) {
263:
264: continue;
265: }
266:
267: throw new Exception\ParseException($this, $tokenStream, 'Unexpected token found.', Exception\ParseException::UNEXPECTED_TOKEN);
268: }
269:
270: case T_COMMENT:
271: case T_DOC_COMMENT:
272: $docblock = $tokenStream->getTokenValue();
273: if (preg_match('~^' . preg_quote(self::DOCBLOCK_TEMPLATE_START, '~') . '~', $docblock)) {
274: array_unshift($this->docblockTemplates, new ReflectionAnnotation($this, $docblock));
275: } elseif (self::DOCBLOCK_TEMPLATE_END === $docblock) {
276: array_shift($this->docblockTemplates);
277: }
278: $tokenStream->next();
279: break;
280: case '{':
281: $tokenStream->next();
282: $depth++;
283: break;
284: case '}':
285: if (0 === $depth--) {
286: break 2;
287: }
288:
289: $tokenStream->next();
290: break;
291: case null:
292: case T_NAMESPACE:
293: break 2;
294: case T_ABSTRACT:
295: case T_FINAL:
296: case T_CLASS:
297: case T_TRAIT:
298: case T_INTERFACE:
299: $class = new ReflectionClass($tokenStream, $this->getBroker(), $this);
300: $firstChild = $firstChild ?: $class;
301:
302: $className = $class->getName();
303: if (isset($this->classes[$className])) {
304: if (!$this->classes[$className] instanceof Invalid\ReflectionClass) {
305: $this->classes[$className] = new Invalid\ReflectionClass($className, $this->classes[$className]->getFileName(), $this->getBroker());
306: }
307:
308: if (!$this->classes[$className]->hasReasons()) {
309: $this->classes[$className]->addReason(new Exception\ParseException(
310: $this,
311: $tokenStream,
312: sprintf('Class %s is defined multiple times in the file.', $className),
313: Exception\ParseException::ALREADY_EXISTS
314: ));
315: }
316: } else {
317: $this->classes[$className] = $class;
318: }
319: $tokenStream->next();
320: break;
321: case T_CONST:
322: $tokenStream->skipWhitespaces(true);
323: do {
324: $constant = new ReflectionConstant($tokenStream, $this->getBroker(), $this);
325: $firstChild = $firstChild ?: $constant;
326:
327: $constantName = $constant->getName();
328: if (isset($this->constants[$constantName])) {
329: if (!$this->constants[$constantName] instanceof Invalid\ReflectionConstant) {
330: $this->constants[$constantName] = new Invalid\ReflectionConstant($constantName, $this->constants[$constantName]->getFileName(), $this->getBroker());
331: }
332:
333: if (!$this->constants[$constantName]->hasReasons()) {
334: $this->constants[$constantName]->addReason(new Exception\ParseException(
335: $this,
336: $tokenStream,
337: sprintf('Constant %s is defined multiple times in the file.', $constantName),
338: Exception\ParseException::ALREADY_EXISTS
339: ));
340: }
341: } else {
342: $this->constants[$constantName] = $constant;
343: }
344: if ($tokenStream->is(',')) {
345: $tokenStream->skipWhitespaces(true);
346: } else {
347: $tokenStream->next();
348: }
349: } while ($tokenStream->is(T_STRING));
350: break;
351: case T_FUNCTION:
352: $position = $tokenStream->key() + 1;
353: while (isset($skipped[$type = $tokenStream->getType($position)])) {
354: $position++;
355: }
356: if ('(' === $type) {
357:
358:
359: $tokenStream
360: ->seek($position)
361: ->findMatchingBracket()
362: ->skipWhiteSpaces(true);
363:
364: if ($tokenStream->is(T_USE)) {
365: $tokenStream
366: ->skipWhitespaces(true)
367: ->findMatchingBracket()
368: ->skipWhitespaces(true);
369: }
370:
371: $tokenStream
372: ->findMatchingBracket()
373: ->next();
374:
375: continue;
376: }
377:
378: $function = new ReflectionFunction($tokenStream, $this->getBroker(), $this);
379: $firstChild = $firstChild ?: $function;
380:
381: $functionName = $function->getName();
382: if (isset($this->functions[$functionName])) {
383: if (!$this->functions[$functionName] instanceof Invalid\ReflectionFunction) {
384: $this->functions[$functionName] = new Invalid\ReflectionFunction($functionName, $this->functions[$functionName]->getFileName(), $this->getBroker());
385: }
386:
387: if (!$this->functions[$functionName]->hasReasons()) {
388: $this->functions[$functionName]->addReason(new Exception\ParseException(
389: $this,
390: $tokenStream,
391: sprintf('Function %s is defined multiple times in the file.', $functionName),
392: Exception\ParseException::ALREADY_EXISTS
393: ));
394: }
395: } else {
396: $this->functions[$functionName] = $function;
397: }
398: $tokenStream->next();
399: break;
400: default:
401: $tokenStream->next();
402: break;
403: }
404: }
405:
406: if ($firstChild) {
407: $this->startPosition = min($this->startPosition, $firstChild->getStartPosition());
408: }
409:
410: return $this;
411: }
412: }
413: