Overview

Namespaces

  • TokenReflection
    • Broker
      • Backend
    • Dummy
    • Exception
    • Invalid
    • Php
    • Stream

Classes

  • Broker
  • ReflectionAnnotation
  • ReflectionBase
  • ReflectionClass
  • ReflectionConstant
  • ReflectionElement
  • ReflectionFile
  • ReflectionFileNamespace
  • ReflectionFunction
  • ReflectionFunctionBase
  • ReflectionMethod
  • ReflectionNamespace
  • ReflectionParameter
  • ReflectionProperty
  • Resolver

Interfaces

  • IReflection
  • IReflectionClass
  • IReflectionConstant
  • IReflectionExtension
  • IReflectionFunction
  • IReflectionFunctionBase
  • IReflectionMethod
  • IReflectionNamespace
  • IReflectionParameter
  • IReflectionProperty
  • Overview
  • Namespace
  • Class
  • Tree
  • Download
  1: <?php
  2: /**
  3:  * PHP Token Reflection
  4:  *
  5:  * Version 1.3.1
  6:  *
  7:  * LICENSE
  8:  *
  9:  * This source file is subject to the new BSD license that is bundled
 10:  * with this library in the file LICENSE.
 11:  *
 12:  * @author Ondřej Nešpor
 13:  * @author Jaroslav Hanslík
 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:  * Tokenized class property reflection.
 23:  */
 24: class ReflectionProperty extends ReflectionElement implements IReflectionProperty
 25: {
 26:     /**
 27:      * Access level of this property has changed from the original implementation.
 28:      *
 29:      * @see http://svn.php.net/viewvc/php/php-src/branches/PHP_5_3/Zend/zend_compile.h?revision=306939&view=markup#l134
 30:      * ZEND_ACC_CHANGED
 31:      *
 32:      * @var integer
 33:      */
 34:     const ACCESS_LEVEL_CHANGED = 0x800;
 35: 
 36:     /**
 37:      * Name of the declaring class.
 38:      *
 39:      * @var string
 40:      */
 41:     private $declaringClassName;
 42: 
 43:     /**
 44:      * Property modifiers.
 45:      *
 46:      * @var integer
 47:      */
 48:     private $modifiers = 0;
 49: 
 50:     /**
 51:      * Determines if modifiers are complete.
 52:      *
 53:      * @var boolean
 54:      */
 55:     private $modifiersComplete = false;
 56: 
 57:     /**
 58:      * Property default value.
 59:      *
 60:      * @var mixed
 61:      */
 62:     private $defaultValue;
 63: 
 64:     /**
 65:      * Property default value definition (part of the source code).
 66:      *
 67:      * @var array|string
 68:      */
 69:     private $defaultValueDefinition = array();
 70: 
 71:     /**
 72:      * Determined if the property value is accessible.
 73:      *
 74:      * @var boolean
 75:      */
 76:     private $accessible = false;
 77: 
 78:     /**
 79:      * Declaring trait name.
 80:      *
 81:      * @var string
 82:      */
 83:     private $declaringTraitName;
 84: 
 85:     /**
 86:      * Returns a reflection of the declaring class.
 87:      *
 88:      * @return \TokenReflection\ReflectionClass
 89:      */
 90:     public function getDeclaringClass()
 91:     {
 92:         return $this->getBroker()->getClass($this->declaringClassName);
 93:     }
 94: 
 95:     /**
 96:      * Returns the name of the declaring class.
 97:      *
 98:      * @return string
 99:      */
100:     public function getDeclaringClassName()
101:     {
102:         return $this->declaringClassName;
103:     }
104: 
105:     /**
106:      * Returns the property default value.
107:      *
108:      * @return mixed
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:      * Returns the part of the source code defining the property default value.
122:      *
123:      * @return string
124:      */
125:     public function getDefaultValueDefinition()
126:     {
127:         return is_array($this->defaultValueDefinition) ? Resolver::getSourceCode($this->defaultValueDefinition) : $this->defaultValueDefinition;
128:     }
129: 
130:     /**
131:      * Returns the property value for a particular class instance.
132:      *
133:      * @param object $object
134:      * @return mixed
135:      * @throws \TokenReflection\Exception\RuntimeException If it is not possible to return the property value.
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:      * Returns if the property was created at compile time.
162:      *
163:      * All properties in the source code are.
164:      *
165:      * @return boolean
166:      */
167:     public function isDefault()
168:     {
169:         return true;
170:     }
171: 
172:     /**
173:      * Returns property modifiers.
174:      *
175:      * @return integer
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:      * Returns if the property is private.
198:      *
199:      * @return boolean
200:      */
201:     public function isPrivate()
202:     {
203:         return (bool) ($this->modifiers & InternalReflectionProperty::IS_PRIVATE);
204:     }
205: 
206:     /**
207:      * Returns if the property is protected.
208:      *
209:      * @return boolean
210:      */
211:     public function isProtected()
212:     {
213:         return (bool) ($this->modifiers & InternalReflectionProperty::IS_PROTECTED);
214:     }
215: 
216:     /**
217:      * Returns if the property is public.
218:      *
219:      * @return boolean
220:      */
221:     public function isPublic()
222:     {
223:         return (bool) ($this->modifiers & InternalReflectionProperty::IS_PUBLIC);
224:     }
225: 
226:     /**
227:      * Returns if the poperty is static.
228:      *
229:      * @return boolean
230:      */
231:     public function isStatic()
232:     {
233:         return (bool) ($this->modifiers & InternalReflectionProperty::IS_STATIC);
234:     }
235: 
236:     /**
237:      * Returns the string representation of the reflection object.
238:      *
239:      * @return string
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:      * Exports a reflected object.
256:      *
257:      * @param \TokenReflection\Broker $broker Broker instance
258:      * @param string|object $class Class name or class instance
259:      * @param string $property Property name
260:      * @param boolean $return Return the export instead of outputting it
261:      * @return string|null
262:      * @throws \TokenReflection\Exception\RuntimeException If requested parameter doesn't exist.
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:      * Returns if the property is set accessible.
286:      *
287:      * @return boolean
288:      */
289:     public function isAccessible()
290:     {
291:         return $this->accessible;
292:     }
293: 
294:     /**
295:      * Sets a property to be accessible or not.
296:      *
297:      * @param boolean $accessible If the property should be accessible.
298:      */
299:     public function setAccessible($accessible)
300:     {
301:         $this->accessible = (bool) $accessible;
302:     }
303: 
304:     /**
305:      * Sets the property default value.
306:      *
307:      * @param mixed $value
308:      */
309:     public function setDefaultValue($value)
310:     {
311:         $this->defaultValue = $value;
312:         $this->defaultValueDefinition = var_export($value, true);
313:     }
314: 
315:     /**
316:      * Sets value of a property for a particular class instance.
317:      *
318:      * @param object $object Class instance
319:      * @param mixed $value Poperty value
320:      * @throws \TokenReflection\Exception\RuntimeException If it is not possible to set the property value.
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:      * Returns imported namespaces and aliases from the declaring namespace.
349:      *
350:      * @return array
351:      */
352:     public function getNamespaceAliases()
353:     {
354:         return $this->getDeclaringClass()->getNamespaceAliases();
355:     }
356: 
357:     /**
358:      * Creates a property alias for the given class.
359:      *
360:      * @param \TokenReflection\ReflectionClass $parent New parent class
361:      * @return \TokenReflection\ReflectionProperty
362:      */
363:     public function alias(ReflectionClass $parent)
364:     {
365:         $property = clone $this;
366:         $property->declaringClassName = $parent->getName();
367:         return $property;
368:     }
369: 
370:     /**
371:      * Returns the defining trait.
372:      *
373:      * @return \TokenReflection\IReflectionClass|null
374:      */
375:     public function getDeclaringTrait()
376:     {
377:         return null === $this->declaringTraitName ? null : $this->getBroker()->getClass($this->declaringTraitName);
378:     }
379: 
380:     /**
381:      * Returns the declaring trait name.
382:      *
383:      * @return string|null
384:      */
385:     public function getDeclaringTraitName()
386:     {
387:         return $this->declaringTraitName;
388:     }
389: 
390:     /**
391:      * Returns an element pretty (docblock compatible) name.
392:      *
393:      * @return string
394:      */
395:     public function getPrettyName()
396:     {
397:         return sprintf('%s::$%s', $this->declaringClassName ?: $this->declaringTraitName, $this->name);
398:     }
399: 
400:     /**
401:      * Processes the parent reflection object.
402:      *
403:      * @param \TokenReflection\IReflection $parent Parent reflection object
404:      * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream
405:      * @return \TokenReflection\ReflectionElement
406:      * @throws \TokenReflection\Exception\Parse If an invalid parent reflection object was provided.
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:      * Parses reflected element metadata from the token stream.
423:      *
424:      * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream
425:      * @param \TokenReflection\IReflection $parent Parent reflection object
426:      * @return \TokenReflection\ReflectionProperty
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:      * Parses class modifiers (abstract, final) and class type (class, interface).
442:      *
443:      * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream
444:      * @param \TokenReflection\ReflectionClass $class Defining class
445:      * @return \TokenReflection\ReflectionClass
446:      * @throws \TokenReflection\Exception\ParseException If the modifiers value cannot be determined.
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:      * Parses the property name.
501:      *
502:      * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream
503:      * @return \TokenReflection\ReflectionProperty
504:      * @throws \TokenReflection\Exception\ParseException If the property name could not be determined.
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:      * Parses the propety default value.
521:      *
522:      * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream
523:      * @return \TokenReflection\ReflectionProperty
524:      * @throws \TokenReflection\Exception\ParseException If the property default value could not be determined.
525:      */
526:     private function parseDefaultValue(Stream $tokenStream)
527:     {
528:         $type = $tokenStream->getType();
529: 
530:         if (';' === $type || ',' === $type) {
531:             // No default value
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: 
PHP Token Reflection API documentation generated by ApiGen 2.8.0