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: abstract class ReflectionFunctionBase extends ReflectionElement implements IReflectionFunctionBase
24: {
25: 26: 27: 28: 29:
30: protected $namespaceName;
31:
32: 33: 34: 35: 36:
37: private $returnsReference = false;
38:
39: 40: 41: 42: 43:
44: protected $parameters = array();
45:
46: 47: 48: 49: 50:
51: private $staticVariables = array();
52:
53: 54: 55: 56: 57:
58: private $staticVariablesDefinition = array();
59:
60: 61: 62: 63: 64:
65: public function getName()
66: {
67: if (null !== $this->namespaceName && ReflectionNamespace::NO_NAMESPACE_NAME !== $this->namespaceName) {
68: return $this->namespaceName . '\\' . $this->name;
69: }
70:
71: return $this->name;
72: }
73:
74: 75: 76: 77: 78:
79: public function getShortName()
80: {
81: return $this->name;
82: }
83:
84: 85: 86: 87: 88:
89: public function getNamespaceName()
90: {
91: return null === $this->namespaceName || $this->namespaceName === ReflectionNamespace::NO_NAMESPACE_NAME ? '' : $this->namespaceName;
92: }
93:
94: 95: 96: 97: 98:
99: public function inNamespace()
100: {
101: return '' !== $this->getNamespaceName();
102: }
103:
104: 105: 106: 107: 108:
109: public function isClosure()
110: {
111: return false;
112: }
113:
114: 115: 116: 117: 118:
119: public function getClosureThis()
120: {
121: return null;
122: }
123:
124: 125: 126: 127: 128:
129: public function getClosureScopeClass()
130: {
131: return null;
132: }
133:
134: 135: 136: 137: 138:
139: public function returnsReference()
140: {
141: return $this->returnsReference;
142: }
143:
144: 145: 146: 147: 148: 149: 150: 151:
152: public function getParameter($parameter)
153: {
154: if (is_numeric($parameter)) {
155: if (!isset($this->parameters[$parameter])) {
156: throw new Exception\RuntimeException(sprintf('There is no parameter at position "%d".', $parameter), Exception\RuntimeException::DOES_NOT_EXIST, $this);
157: }
158: return $this->parameters[$parameter];
159: } else {
160: foreach ($this->parameters as $reflection) {
161: if ($reflection->getName() === $parameter) {
162: return $reflection;
163: }
164: }
165:
166: throw new Exception\RuntimeException(sprintf('There is no parameter "%s".', $parameter), Exception\RuntimeException::DOES_NOT_EXIST, $this);
167: }
168: }
169:
170: 171: 172: 173: 174:
175: public function getParameters()
176: {
177: return $this->parameters;
178: }
179:
180: 181: 182: 183: 184:
185: public function getNumberOfParameters()
186: {
187: return count($this->parameters);
188: }
189:
190: 191: 192: 193: 194:
195: public function getNumberOfRequiredParameters()
196: {
197: $count = 0;
198: array_walk($this->parameters, function(ReflectionParameter $parameter) use (&$count) {
199: if (!$parameter->isOptional()) {
200: $count++;
201: }
202: });
203: return $count;
204: }
205:
206: 207: 208: 209: 210:
211: public function getStaticVariables()
212: {
213: if (empty($this->staticVariables) && !empty($this->staticVariablesDefinition)) {
214: foreach ($this->staticVariablesDefinition as $variableName => $variableDefinition) {
215: $this->staticVariables[$variableName] = Resolver::getValueDefinition($variableDefinition, $this);
216: }
217: }
218:
219: return $this->staticVariables;
220: }
221:
222: 223: 224: 225: 226:
227: public function getPrettyName()
228: {
229: return $this->name . '()';
230: }
231:
232: 233: 234: 235: 236:
237: protected final function aliasParameters()
238: {
239: if (!$this instanceof ReflectionMethod) {
240: throw new Exception\RuntimeException('Only method parameters can be aliased.', Exception\RuntimeException::UNSUPPORTED, $this);
241: }
242:
243: foreach ($this->parameters as $index => $parameter) {
244: $this->parameters[$index] = $parameter->alias($this);
245: }
246: }
247:
248: 249: 250: 251: 252: 253: 254:
255: final protected function parseReturnsReference(Stream $tokenStream)
256: {
257: if (!$tokenStream->is(T_FUNCTION)) {
258: throw new Exception\ParseException($this, $tokenStream, 'Could not find the function keyword.', Exception\ParseException::UNEXPECTED_TOKEN);
259: }
260:
261: $tokenStream->skipWhitespaces(true);
262:
263: $type = $tokenStream->getType();
264:
265: if ('&' === $type) {
266: $this->returnsReference = true;
267: $tokenStream->skipWhitespaces(true);
268: } elseif (T_STRING !== $type) {
269: throw new Exception\ParseException($this, $tokenStream, 'Unexpected token found.', Exception\ParseException::UNEXPECTED_TOKEN);
270: }
271:
272: return $this;
273: }
274:
275: 276: 277: 278: 279: 280: 281:
282: final protected function parseName(Stream $tokenStream)
283: {
284: $this->name = $tokenStream->getTokenValue();
285:
286: $tokenStream->skipWhitespaces(true);
287:
288: return $this;
289: }
290:
291: 292: 293: 294: 295: 296: 297:
298: final protected function parseChildren(Stream $tokenStream, IReflection $parent)
299: {
300: return $this
301: ->parseParameters($tokenStream)
302: ->parseStaticVariables($tokenStream);
303: }
304:
305: 306: 307: 308: 309: 310: 311:
312: final protected function parseParameters(Stream $tokenStream)
313: {
314: if (!$tokenStream->is('(')) {
315: throw new Exception\ParseException($this, $tokenStream, 'Could find the start token.', Exception\ParseException::UNEXPECTED_TOKEN);
316: }
317:
318: static $accepted = array(T_NS_SEPARATOR => true, T_STRING => true, T_ARRAY => true, T_CALLABLE => true, T_VARIABLE => true, '&' => true);
319:
320: $tokenStream->skipWhitespaces(true);
321:
322: while (null !== ($type = $tokenStream->getType()) && ')' !== $type) {
323: if (isset($accepted[$type])) {
324: $parameter = new ReflectionParameter($tokenStream, $this->getBroker(), $this);
325: $this->parameters[] = $parameter;
326: }
327:
328: if ($tokenStream->is(')')) {
329: break;
330: }
331:
332: $tokenStream->skipWhitespaces(true);
333: }
334:
335: $tokenStream->skipWhitespaces();
336:
337: return $this;
338: }
339:
340: 341: 342: 343: 344: 345: 346:
347: final protected function parseStaticVariables(Stream $tokenStream)
348: {
349: $type = $tokenStream->getType();
350: if ('{' === $type) {
351: if ($this->getBroker()->isOptionSet(Broker::OPTION_PARSE_FUNCTION_BODY)) {
352: $tokenStream->skipWhitespaces(true);
353:
354: while ('}' !== ($type = $tokenStream->getType())) {
355: switch ($type) {
356: case T_STATIC:
357: $type = $tokenStream->skipWhitespaces(true)->getType();
358: if (T_VARIABLE !== $type) {
359:
360: break;
361: }
362:
363: while (T_VARIABLE === $type) {
364: $variableName = $tokenStream->getTokenValue();
365: $variableDefinition = array();
366:
367: $type = $tokenStream->skipWhitespaces(true)->getType();
368: if ('=' === $type) {
369: $type = $tokenStream->skipWhitespaces(true)->getType();
370: $level = 0;
371: while ($tokenStream->valid()) {
372: switch ($type) {
373: case '(':
374: case '[':
375: case '{':
376: case T_CURLY_OPEN:
377: case T_DOLLAR_OPEN_CURLY_BRACES:
378: $level++;
379: break;
380: case ')':
381: case ']':
382: case '}':
383: $level--;
384: break;
385: case ';':
386: case ',':
387: if (0 === $level) {
388: break 2;
389: }
390: default:
391: break;
392: }
393:
394: $variableDefinition[] = $tokenStream->current();
395: $type = $tokenStream->skipWhitespaces(true)->getType();
396: }
397:
398: if (!$tokenStream->valid()) {
399: throw new Exception\ParseException($this, $tokenStream, 'Invalid end of token stream.', Exception\ParseException::READ_BEYOND_EOS);
400: }
401: }
402:
403: $this->staticVariablesDefinition[substr($variableName, 1)] = $variableDefinition;
404:
405: if (',' === $type) {
406: $type = $tokenStream->skipWhitespaces(true)->getType();
407: } else {
408: break;
409: }
410: }
411:
412: break;
413: case T_FUNCTION:
414:
415: if (!$tokenStream->find('{')) {
416: throw new Exception\ParseException($this, $tokenStream, 'Could not find beginning of the anonymous function.', Exception\ParseException::UNEXPECTED_TOKEN);
417: }
418:
419: case '{':
420: case '[':
421: case '(':
422: case T_CURLY_OPEN:
423: case T_DOLLAR_OPEN_CURLY_BRACES:
424: $tokenStream->findMatchingBracket()->skipWhitespaces(true);
425: break;
426: default:
427: $tokenStream->skipWhitespaces();
428: break;
429: }
430: }
431: } else {
432: $tokenStream->findMatchingBracket();
433: }
434: } elseif (';' !== $type) {
435: throw new Exception\ParseException($this, $tokenStream, 'Unexpected token found.', Exception\ParseException::UNEXPECTED_TOKEN);
436: }
437:
438: return $this;
439: }
440: }
441: