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: use ReflectionProperty as InternalReflectionProperty, ReflectionClass as InternalReflectionClass;
20:
21: 22: 23:
24: class ReflectionProperty extends ReflectionElement implements IReflectionProperty
25: {
26: 27: 28: 29: 30: 31: 32: 33:
34: const ACCESS_LEVEL_CHANGED = 0x800;
35:
36: 37: 38: 39: 40:
41: private $declaringClassName;
42:
43: 44: 45: 46: 47:
48: private $modifiers = 0;
49:
50: 51: 52: 53: 54:
55: private $modifiersComplete = false;
56:
57: 58: 59: 60: 61:
62: private $defaultValue;
63:
64: 65: 66: 67: 68:
69: private $defaultValueDefinition = array();
70:
71: 72: 73: 74: 75:
76: private $accessible = false;
77:
78: 79: 80: 81: 82:
83: private $declaringTraitName;
84:
85: 86: 87: 88: 89:
90: public function getDeclaringClass()
91: {
92: return $this->getBroker()->getClass($this->declaringClassName);
93: }
94:
95: 96: 97: 98: 99:
100: public function getDeclaringClassName()
101: {
102: return $this->declaringClassName;
103: }
104:
105: 106: 107: 108: 109:
110: public function getDefaultValue()
111: {
112: if (is_array($this->defaultValueDefinition)) {
113: $this->defaultValue = Resolver::getValueDefinition($this->defaultValueDefinition, $this);
114: $this->defaultValueDefinition = Resolver::getSourceCode($this->defaultValueDefinition);
115: }
116:
117: return $this->defaultValue;
118: }
119:
120: 121: 122: 123: 124:
125: public function getDefaultValueDefinition()
126: {
127: return is_array($this->defaultValueDefinition) ? Resolver::getSourceCode($this->defaultValueDefinition) : $this->defaultValueDefinition;
128: }
129:
130: 131: 132: 133: 134: 135: 136:
137: public function getValue($object)
138: {
139: $declaringClass = $this->getDeclaringClass();
140: if (!$declaringClass->isInstance($object)) {
141: throw new Exception\RuntimeException('The given class is not an instance or subclass of the current class.', Exception\RuntimeException::INVALID_ARGUMENT, $this);
142: }
143:
144: if ($this->isPublic()) {
145: return $object->{$this->name};
146: } elseif ($this->isAccessible()) {
147: $refClass = new InternalReflectionClass($object);
148: $refProperty = $refClass->getProperty($this->name);
149:
150: $refProperty->setAccessible(true);
151: $value = $refProperty->getValue($object);
152: $refProperty->setAccessible(false);
153:
154: return $value;
155: }
156:
157: throw new Exception\RuntimeException('Only public and accessible properties can return their values.', Exception\RuntimeException::NOT_ACCESSBILE, $this);
158: }
159:
160: 161: 162: 163: 164: 165: 166:
167: public function isDefault()
168: {
169: return true;
170: }
171:
172: 173: 174: 175: 176:
177: public function getModifiers()
178: {
179: if (false === $this->modifiersComplete) {
180: $declaringClass = $this->getDeclaringClass();
181: $declaringClassParent = $declaringClass->getParentClass();
182:
183: if ($declaringClassParent && $declaringClassParent->hasProperty($this->name)) {
184: $property = $declaringClassParent->getProperty($this->name);
185: if (($this->isPublic() && !$property->isPublic()) || ($this->isProtected() && $property->isPrivate())) {
186: $this->modifiers |= self::ACCESS_LEVEL_CHANGED;
187: }
188: }
189:
190: $this->modifiersComplete = ($this->modifiers & self::ACCESS_LEVEL_CHANGED) || $declaringClass->isComplete();
191: }
192:
193: return $this->modifiers;
194: }
195:
196: 197: 198: 199: 200:
201: public function isPrivate()
202: {
203: return (bool) ($this->modifiers & InternalReflectionProperty::IS_PRIVATE);
204: }
205:
206: 207: 208: 209: 210:
211: public function isProtected()
212: {
213: return (bool) ($this->modifiers & InternalReflectionProperty::IS_PROTECTED);
214: }
215:
216: 217: 218: 219: 220:
221: public function isPublic()
222: {
223: return (bool) ($this->modifiers & InternalReflectionProperty::IS_PUBLIC);
224: }
225:
226: 227: 228: 229: 230:
231: public function isStatic()
232: {
233: return (bool) ($this->modifiers & InternalReflectionProperty::IS_STATIC);
234: }
235:
236: 237: 238: 239: 240:
241: public function __toString()
242: {
243: return sprintf(
244: "Property [ %s%s%s%s%s\$%s ]\n",
245: $this->isStatic() ? '' : '<default> ',
246: $this->isPublic() ? 'public ' : '',
247: $this->isPrivate() ? 'private ' : '',
248: $this->isProtected() ? 'protected ' : '',
249: $this->isStatic() ? 'static ' : '',
250: $this->getName()
251: );
252: }
253:
254: 255: 256: 257: 258: 259: 260: 261: 262: 263:
264: public static function export(Broker $broker, $class, $property, $return = false)
265: {
266: $className = is_object($class) ? get_class($class) : $class;
267: $propertyName = $property;
268:
269: $class = $broker->getClass($className);
270: if ($class instanceof Invalid\ReflectionClass) {
271: throw new Exception\RuntimeException('Class is invalid.', Exception\RuntimeException::UNSUPPORTED);
272: } elseif ($class instanceof Dummy\ReflectionClass) {
273: throw new Exception\RuntimeException(sprintf('Class %s does not exist.', $className), Exception\RuntimeException::DOES_NOT_EXIST);
274: }
275: $property = $class->getProperty($propertyName);
276:
277: if ($return) {
278: return $property->__toString();
279: }
280:
281: echo $property->__toString();
282: }
283:
284: 285: 286: 287: 288:
289: public function isAccessible()
290: {
291: return $this->accessible;
292: }
293:
294: 295: 296: 297: 298:
299: public function setAccessible($accessible)
300: {
301: $this->accessible = (bool) $accessible;
302: }
303:
304: 305: 306: 307: 308:
309: public function setDefaultValue($value)
310: {
311: $this->defaultValue = $value;
312: $this->defaultValueDefinition = var_export($value, true);
313: }
314:
315: 316: 317: 318: 319: 320: 321:
322: public function setValue($object, $value)
323: {
324: $declaringClass = $this->getDeclaringClass();
325: if (!$declaringClass->isInstance($object)) {
326: throw new Exception\RuntimeException('Instance of or subclass expected.', Exception\RuntimeException::INVALID_ARGUMENT, $this);
327: }
328:
329: if ($this->isPublic()) {
330: $object->{$this->name} = $value;
331: } elseif ($this->isAccessible()) {
332: $refClass = new InternalReflectionClass($object);
333: $refProperty = $refClass->getProperty($this->name);
334:
335: $refProperty->setAccessible(true);
336: $refProperty->setValue($object, $value);
337: $refProperty->setAccessible(false);
338:
339: if ($this->isStatic()) {
340: $this->setDefaultValue($value);
341: }
342: } else {
343: throw new Exception\RuntimeException('Only public and accessible properties can be set.', Exception\RuntimeException::NOT_ACCESSBILE, $this);
344: }
345: }
346:
347: 348: 349: 350: 351:
352: public function getNamespaceAliases()
353: {
354: return $this->getDeclaringClass()->getNamespaceAliases();
355: }
356:
357: 358: 359: 360: 361: 362:
363: public function alias(ReflectionClass $parent)
364: {
365: $property = clone $this;
366: $property->declaringClassName = $parent->getName();
367: return $property;
368: }
369:
370: 371: 372: 373: 374:
375: public function getDeclaringTrait()
376: {
377: return null === $this->declaringTraitName ? null : $this->getBroker()->getClass($this->declaringTraitName);
378: }
379:
380: 381: 382: 383: 384:
385: public function getDeclaringTraitName()
386: {
387: return $this->declaringTraitName;
388: }
389:
390: 391: 392: 393: 394:
395: public function getPrettyName()
396: {
397: return sprintf('%s::$%s', $this->declaringClassName ?: $this->declaringTraitName, $this->name);
398: }
399:
400: 401: 402: 403: 404: 405: 406: 407:
408: protected function processParent(IReflection $parent, Stream $tokenStream)
409: {
410: if (!$parent instanceof ReflectionClass) {
411: throw new Exception\ParseException($this, $tokenStream, 'The parent object has to be an instance of TokenReflection\ReflectionClass.', Exception\ParseException::INVALID_PARENT);
412: }
413:
414: $this->declaringClassName = $parent->getName();
415: if ($parent->isTrait()) {
416: $this->declaringTraitName = $parent->getName();
417: }
418: return parent::processParent($parent, $tokenStream);
419: }
420:
421: 422: 423: 424: 425: 426: 427:
428: protected function parse(Stream $tokenStream, IReflection $parent)
429: {
430: $this->parseModifiers($tokenStream, $parent);
431:
432: if (false === $this->docComment->getDocComment()) {
433: $this->parseDocComment($tokenStream, $parent);
434: }
435:
436: return $this->parseName($tokenStream)
437: ->parseDefaultValue($tokenStream);
438: }
439:
440: 441: 442: 443: 444: 445: 446: 447:
448: private function parseModifiers(Stream $tokenStream, ReflectionClass $class)
449: {
450: while (true) {
451: switch ($tokenStream->getType()) {
452: case T_PUBLIC:
453: case T_VAR:
454: $this->modifiers |= InternalReflectionProperty::IS_PUBLIC;
455: break;
456: case T_PROTECTED:
457: $this->modifiers |= InternalReflectionProperty::IS_PROTECTED;
458: break;
459: case T_PRIVATE:
460: $this->modifiers |= InternalReflectionProperty::IS_PRIVATE;
461: break;
462: case T_STATIC:
463: $this->modifiers |= InternalReflectionProperty::IS_STATIC;
464: break;
465: default:
466: break 2;
467: }
468:
469: $tokenStream->skipWhitespaces(true);
470: }
471:
472: if (InternalReflectionProperty::IS_STATIC === $this->modifiers) {
473: $this->modifiers |= InternalReflectionProperty::IS_PUBLIC;
474: } elseif (0 === $this->modifiers) {
475: $parentProperties = $class->getOwnProperties();
476: if (empty($parentProperties)) {
477: throw new Exception\ParseException($this, $tokenStream, 'No access level defined and no previous defining class property present.', Exception\ParseException::LOGICAL_ERROR);
478: }
479:
480: $sibling = array_pop($parentProperties);
481: if ($sibling->isPublic()) {
482: $this->modifiers = InternalReflectionProperty::IS_PUBLIC;
483: } elseif ($sibling->isPrivate()) {
484: $this->modifiers = InternalReflectionProperty::IS_PRIVATE;
485: } elseif ($sibling->isProtected()) {
486: $this->modifiers = InternalReflectionProperty::IS_PROTECTED;
487: } else {
488: throw new Exception\ParseException($this, $tokenStream, sprintf('Property sibling "%s" has no access level defined.', $sibling->getName()), Exception\Parse::PARSE_ELEMENT_ERROR);
489: }
490:
491: if ($sibling->isStatic()) {
492: $this->modifiers |= InternalReflectionProperty::IS_STATIC;
493: }
494: }
495:
496: return $this;
497: }
498:
499: 500: 501: 502: 503: 504: 505:
506: protected function parseName(Stream $tokenStream)
507: {
508: if (!$tokenStream->is(T_VARIABLE)) {
509: throw new Exception\ParseException($this, $tokenStream, 'The property name could not be determined.', Exception\ParseException::LOGICAL_ERROR);
510: }
511:
512: $this->name = substr($tokenStream->getTokenValue(), 1);
513:
514: $tokenStream->skipWhitespaces(true);
515:
516: return $this;
517: }
518:
519: 520: 521: 522: 523: 524: 525:
526: private function parseDefaultValue(Stream $tokenStream)
527: {
528: $type = $tokenStream->getType();
529:
530: if (';' === $type || ',' === $type) {
531:
532: return $this;
533: }
534:
535: if ('=' === $type) {
536: $tokenStream->skipWhitespaces(true);
537: }
538:
539: $level = 0;
540: while (null !== ($type = $tokenStream->getType())) {
541: switch ($type) {
542: case ',':
543: if (0 !== $level) {
544: break;
545: }
546: case ';':
547: break 2;
548: case ')':
549: case ']':
550: case '}':
551: $level--;
552: break;
553: case '(':
554: case '{':
555: case '[':
556: $level++;
557: break;
558: default:
559: break;
560: }
561:
562: $this->defaultValueDefinition[] = $tokenStream->current();
563: $tokenStream->next();
564: }
565:
566: if (',' !== $type && ';' !== $type) {
567: throw new Exception\ParseException($this, $tokenStream, 'The property default value is not terminated properly. Expected "," or ";".', Exception\ParseException::UNEXPECTED_TOKEN);
568: }
569:
570: return $this;
571: }
572: }
573: