woocommerce/apigen/libs/TokenReflection/TokenReflection/ReflectionClass.php

1987 lines
50 KiB
PHP

<?php
/**
* PHP Token Reflection
*
* Version 1.3.1
*
* LICENSE
*
* This source file is subject to the new BSD license that is bundled
* with this library in the file LICENSE.
*
* @author Ondřej Nešpor
* @author Jaroslav Hanslík
*/
namespace TokenReflection;
use TokenReflection\Exception, TokenReflection\Stream\StreamBase as Stream;
use ReflectionClass as InternalReflectionClass, ReflectionProperty as InternalReflectionProperty, ReflectionMethod as InternalReflectionMethod;
/**
* Tokenized class reflection.
*/
class ReflectionClass extends ReflectionElement implements IReflectionClass
{
/**
* Modifier for determining if the reflected object is an interface.
*
* @var integer
* @see http://svn.php.net/viewvc/php/php-src/branches/PHP_5_3/Zend/zend_compile.h?revision=306939&view=markup#l122
*/
const IS_INTERFACE = 0x80;
/**
* Modifier for determining if the reflected object is a trait.
*
* @var integer
* @see http://svn.php.net/viewvc/php/php-src/trunk/Zend/zend_compile.h?revision=306938&view=markup#l150
*/
const IS_TRAIT = 0x120;
/**
* Class implements interfaces.
*
* @see http://svn.php.net/viewvc/php/php-src/branches/PHP_5_3/Zend/zend_compile.h?revision=306939&view=markup#l152
*
* @var integer
*/
const IMPLEMENTS_INTERFACES = 0x80000;
/**
* Class implements traits.
*
* @see http://svn.php.net/viewvc/php/php-src/trunk/Zend/zend_compile.h?revision=306938&view=markup#l181
*
* @var integer
*/
const IMPLEMENTS_TRAITS = 0x400000;
/**
* Class namespace name.
*
* @var string
*/
private $namespaceName;
/**
* Class modifiers.
*
* @var integer
*/
private $modifiers = 0;
/**
* Class type (class/interface/trait).
*
* @var integer
*/
private $type = 0;
/**
* Determines if modifiers are complete.
*
* @var boolean
*/
private $modifiersComplete = false;
/**
* Parent class name.
*
* @var string
*/
private $parentClassName;
/**
* Implemented interface names.
*
* @var array
*/
private $interfaces = array();
/**
* Used trait names.
*
* @var array
*/
private $traits = array();
/**
* Aliases used at trait methods.
*
* Compatible with the internal reflection.
*
* @var array
*/
private $traitAliases = array();
/**
* Trait importing rules.
*
* Format:
* [<trait>::]<method> => array(
* array(<new-name>, [<access-level>])|null
* [, ...]
* )
*
* @var array
*/
private $traitImports = array();
/**
* Stores if the class definition is complete.
*
* @var array
*/
private $methods = array();
/**
* Constant reflections.
*
* @var array
*/
private $constants = array();
/**
* Properties reflections.
*
* @var array
*/
private $properties = array();
/**
* Stores if the class definition is complete.
*
* @var boolean
*/
private $definitionComplete = false;
/**
* Imported namespace/class aliases.
*
* @var array
*/
private $aliases = array();
/**
* Returns the unqualified name (UQN).
*
* @return string
*/
public function getShortName()
{
$name = $this->getName();
if ($this->namespaceName !== ReflectionNamespace::NO_NAMESPACE_NAME) {
$name = substr($name, strlen($this->namespaceName) + 1);
}
return $name;
}
/**
* Returns the namespace name.
*
* @return string
*/
public function getNamespaceName()
{
return $this->namespaceName === ReflectionNamespace::NO_NAMESPACE_NAME ? '' : $this->namespaceName;
}
/**
* Returns if the class is defined within a namespace.
*
* @return boolean
*/
public function inNamespace()
{
return null !== $this->namespaceName && ReflectionNamespace::NO_NAMESPACE_NAME !== $this->namespaceName;
}
/**
* Returns modifiers.
*
* @return array
*/
public function getModifiers()
{
if (false === $this->modifiersComplete) {
if (($this->modifiers & InternalReflectionClass::IS_EXPLICIT_ABSTRACT) && !($this->modifiers & InternalReflectionClass::IS_IMPLICIT_ABSTRACT)) {
foreach ($this->getMethods() as $reflectionMethod) {
if ($reflectionMethod->isAbstract()) {
$this->modifiers |= InternalReflectionClass::IS_IMPLICIT_ABSTRACT;
}
}
if (!empty($this->interfaces)) {
$this->modifiers |= InternalReflectionClass::IS_IMPLICIT_ABSTRACT;
}
}
if (!empty($this->interfaces)) {
$this->modifiers |= self::IMPLEMENTS_INTERFACES;
}
if ($this->isInterface() && !empty($this->methods)) {
$this->modifiers |= InternalReflectionClass::IS_IMPLICIT_ABSTRACT;
}
if (!empty($this->traits)) {
$this->modifiers |= self::IMPLEMENTS_TRAITS;
}
$this->modifiersComplete = null === $this->parentClassName || $this->getParentClass()->isComplete();
if ($this->modifiersComplete) {
foreach ($this->getInterfaces() as $interface) {
if (!$interface->isComplete()) {
$this->modifiersComplete = false;
break;
}
}
}
if ($this->modifiersComplete) {
foreach ($this->getTraits() as $trait) {
if (!$trait->isComplete()) {
$this->modifiersComplete = false;
break;
}
}
}
}
return $this->modifiers;
}
/**
* Returns if the class is abstract.
*
* @return boolean
*/
public function isAbstract()
{
if ($this->modifiers & InternalReflectionClass::IS_EXPLICIT_ABSTRACT) {
return true;
} elseif ($this->isInterface() && !empty($this->methods)) {
return true;
}
return false;
}
/**
* Returns if the class is final.
*
* @return boolean
*/
public function isFinal()
{
return (bool) ($this->modifiers & InternalReflectionClass::IS_FINAL);
}
/**
* Returns if the class is an interface.
*
* @return boolean
*/
public function isInterface()
{
return (bool) ($this->modifiers & self::IS_INTERFACE);
}
/**
* Returns if the class is an exception or its descendant.
*
* @return boolean
*/
public function isException()
{
return 'Exception' === $this->name || $this->isSubclassOf('Exception');
}
/**
* Returns if it is possible to create an instance of this class.
*
* @return boolean
*/
public function isInstantiable()
{
if ($this->isInterface() || $this->isAbstract()) {
return false;
}
if (null === ($constructor = $this->getConstructor())) {
return true;
}
return $constructor->isPublic();
}
/**
* Returns if objects of this class are cloneable.
*
* Introduced in PHP 5.4.
*
* @return boolean
* @see http://svn.php.net/viewvc/php/php-src/trunk/ext/reflection/php_reflection.c?revision=307971&view=markup#l4059
*/
public function isCloneable()
{
if ($this->isInterface() || $this->isAbstract()) {
return false;
}
if ($this->hasMethod('__clone')) {
return $this->getMethod('__clone')->isPublic();
}
return true;
}
/**
* Returns if the class is iterateable.
*
* Returns true if the class implements the Traversable interface.
*
* @return boolean
* @todo traits
*/
public function isIterateable()
{
return $this->implementsInterface('Traversable');
}
/**
* Returns if the current class is a subclass of the given class.
*
* @param string|object $class Class name or reflection object
* @return boolean
* @throws \TokenReflection\Exception\RuntimeException If the provided parameter is not a reflection class instance.
*/
public function isSubclassOf($class)
{
if (is_object($class)) {
if ($class instanceof InternalReflectionClass || $class instanceof IReflectionClass) {
$class = $class->getName();
} else {
$class = get_class($class);
}
}
if ($class === $this->parentClassName) {
return true;
}
$parent = $this->getParentClass();
return false === $parent ? false : $parent->isSubclassOf($class);
}
/**
* Returns the parent class reflection.
*
* @return \TokenReflection\ReflectionClass|boolean
*/
public function getParentClass()
{
$className = $this->getParentClassName();
if (null === $className) {
return false;
}
return $this->getBroker()->getClass($className);
}
/**
* Returns the parent class name.
*
* @return string|null
*/
public function getParentClassName()
{
return $this->parentClassName;
}
/**
* Returns the parent classes reflections.
*
* @return array
*/
public function getParentClasses()
{
$parent = $this->getParentClass();
if (false === $parent) {
return array();
}
return array_merge(array($parent->getName() => $parent), $parent->getParentClasses());
}
/**
* Returns the parent classes names.
*
* @return array
*/
public function getParentClassNameList()
{
$parent = $this->getParentClass();
if (false === $parent) {
return array();
}
return array_merge(array($parent->getName()), $parent->getParentClassNameList());
}
/**
* Returns if the class implements the given interface.
*
* @param string|object $interface Interface name or reflection object
* @return boolean
* @throws \TokenReflection\Exception\RuntimeException If the provided parameter is not an interface.
*/
public function implementsInterface($interface)
{
if (is_object($interface)) {
if (!$interface instanceof InternalReflectionClass && !$interface instanceof IReflectionClass) {
throw new Exception\RuntimeException(sprintf('Parameter must be a string or an instance of class reflection, "%s" provided.', get_class($interface)), Exception\RuntimeException::INVALID_ARGUMENT, $this);
}
if (!$interface->isInterface()) {
throw new Exception\RuntimeException(sprintf('"%s" is not an interface.', $interfaceName), Exception\RuntimeException::INVALID_ARGUMENT, $this);
}
$interfaceName = $interface->getName();
} else {
$interfaceName = $interface;
}
return in_array($interfaceName, $this->getInterfaceNames());
}
/**
* Returns interface reflections.
*
* @return array
*/
public function getInterfaces()
{
$interfaceNames = $this->getInterfaceNames();
if (empty($interfaceNames)) {
return array();
}
$broker = $this->getBroker();
return array_combine($interfaceNames, array_map(function($interfaceName) use ($broker) {
return $broker->getClass($interfaceName);
}, $interfaceNames));
}
/**
* Returns interface names.
*
* @return array
*/
public function getInterfaceNames()
{
$parentClass = $this->getParentClass();
$names = false !== $parentClass ? array_reverse(array_flip($parentClass->getInterfaceNames())) : array();
foreach ($this->interfaces as $interfaceName) {
$names[$interfaceName] = true;
foreach (array_reverse($this->getBroker()->getClass($interfaceName)->getInterfaceNames()) as $parentInterfaceName) {
$names[$parentInterfaceName] = true;
}
}
return array_keys($names);
}
/**
* Returns reflections of interfaces implemented by this class, not its parents.
*
* @return array
*/
public function getOwnInterfaces()
{
$interfaceNames = $this->getOwnInterfaceNames();
if (empty($interfaceNames)) {
return array();
}
$broker = $this->getBroker();
return array_combine($interfaceNames, array_map(function($interfaceName) use ($broker) {
return $broker->getClass($interfaceName);
}, $interfaceNames));
}
/**
* Returns names of interfaces implemented by this class, not its parents.
*
* @return array
*/
public function getOwnInterfaceNames()
{
return $this->interfaces;
}
/**
* Returns the class constructor reflection.
*
* @return \TokenReflection\ReflectionMethod|null
*/
public function getConstructor()
{
foreach ($this->getMethods() as $method) {
if ($method->isConstructor()) {
return $method;
}
}
return null;
}
/**
* Returns the class destructor reflection.
*
* @return \TokenReflection\ReflectionMethod|null
*/
public function getDestructor()
{
foreach ($this->getMethods() as $method) {
if ($method->isDestructor()) {
return $method;
}
}
return null;
}
/**
* Returns if the class implements the given method.
*
* @param string $name Method name
* @return boolean
*/
public function hasMethod($name)
{
foreach ($this->getMethods() as $method) {
if ($name === $method->getName()) {
return true;
}
}
return false;
}
/**
* Returns a method reflection.
*
* @param string $name Method name
* @return \TokenReflection\ReflectionMethod
* @throws \TokenReflection\Exception\RuntimeException If the requested method does not exist.
*/
public function getMethod($name)
{
if (isset($this->methods[$name])) {
return $this->methods[$name];
}
foreach ($this->getMethods() as $method) {
if ($name === $method->getName()) {
return $method;
}
}
throw new Exception\RuntimeException(sprintf('There is no method "%s".', $name), Exception\RuntimeException::DOES_NOT_EXIST, $this);
}
/**
* Returns method reflections.
*
* @param integer $filter Methods filter
* @return array
*/
public function getMethods($filter = null)
{
$methods = $this->methods;
foreach ($this->getTraitMethods() as $traitMethod) {
if (!isset($methods[$traitMethod->getName()])) {
$methods[$traitMethod->getName()] = $traitMethod;
}
}
if (null !== $this->parentClassName) {
foreach ($this->getParentClass()->getMethods(null) as $parentMethod) {
if (!isset($methods[$parentMethod->getName()])) {
$methods[$parentMethod->getName()] = $parentMethod;
}
}
}
foreach ($this->getOwnInterfaces() as $interface) {
foreach ($interface->getMethods(null) as $parentMethod) {
if (!isset($methods[$parentMethod->getName()])) {
$methods[$parentMethod->getName()] = $parentMethod;
}
}
}
if (null !== $filter) {
$methods = array_filter($methods, function(IReflectionMethod $method) use ($filter) {
return $method->is($filter);
});
}
return array_values($methods);
}
/**
* Returns if the class implements (and not its parents) the given method.
*
* @param string $name Method name
* @return boolean
*/
public function hasOwnMethod($name)
{
return isset($this->methods[$name]);
}
/**
* Returns reflections of methods declared by this class, not its parents.
*
* @param integer $filter Methods filter
* @return array
*/
public function getOwnMethods($filter = null)
{
$methods = $this->methods;
if (null !== $filter) {
$methods = array_filter($methods, function(ReflectionMethod $method) use ($filter) {
return $method->is($filter);
});
}
return array_values($methods);
}
/**
* Returns if the class imports the given method from traits.
*
* @param string $name Method name
* @return boolean
*/
public function hasTraitMethod($name)
{
if (isset($this->methods[$name])) {
return false;
}
foreach ($this->getOwnTraits() as $trait) {
if ($trait->hasMethod($name)) {
return true;
}
}
return false;
}
/**
* Returns reflections of method imported from traits.
*
* @param integer $filter Methods filter
* @return array
* @throws \TokenReflection\Exception\RuntimeException If trait method was already imported.
*/
public function getTraitMethods($filter = null)
{
$methods = array();
foreach ($this->getOwnTraits() as $trait) {
$traitName = $trait->getName();
foreach ($trait->getMethods(null) as $traitMethod) {
$methodName = $traitMethod->getName();
$imports = array();
if (isset($this->traitImports[$traitName . '::' . $methodName])) {
$imports = $this->traitImports[$traitName . '::' . $methodName];
}
if (isset($this->traitImports[$methodName])) {
$imports = empty($imports) ? $this->traitImports[$methodName] : array_merge($imports, $this->traitImports[$methodName]);
}
foreach ($imports as $import) {
if (null !== $import) {
list($newName, $accessLevel) = $import;
if ('' === $newName) {
$newName = $methodName;
$imports[] = null;
}
if (!isset($this->methods[$newName])) {
if (isset($methods[$newName])) {
throw new Exception\RuntimeException(sprintf('Trait method "%s" was already imported.', $newName), Exception\RuntimeException::ALREADY_EXISTS, $this);
}
$methods[$newName] = $traitMethod->alias($this, $newName, $accessLevel);
}
}
}
if (!in_array(null, $imports)) {
if (!isset($this->methods[$methodName])) {
if (isset($methods[$methodName])) {
throw new Exception\RuntimeException(sprintf('Trait method "%s" was already imported.', $methodName), Exception\RuntimeException::ALREADY_EXISTS, $this);
}
$methods[$methodName] = $traitMethod->alias($this);
}
}
}
}
if (null !== $filter) {
$methods = array_filter($methods, function(IReflectionMethod $method) use ($filter) {
return (bool) ($method->getModifiers() & $filter);
});
}
return array_values($methods);
}
/**
* Returns if the class defines the given constant.
*
* @param string $name Constant name.
* @return boolean
*/
public function hasConstant($name)
{
if (isset($this->constants[$name])) {
return true;
}
foreach ($this->getConstantReflections() as $constant) {
if ($name === $constant->getName()) {
return true;
}
}
return false;
}
/**
* Returns a constant value.
*
* @param string $name Constant name
* @return mixed|false
*/
public function getConstant($name)
{
try {
return $this->getConstantReflection($name)->getValue();
} catch (Exception\BaseException $e) {
return false;
}
}
/**
* Returns a constant reflection.
*
* @param string $name Constant name
* @return \TokenReflection\ReflectionConstant
* @throws \TokenReflection\Exception\RuntimeException If the requested constant does not exist.
*/
public function getConstantReflection($name)
{
if (isset($this->constants[$name])) {
return $this->constants[$name];
}
foreach ($this->getConstantReflections() as $constant) {
if ($name === $constant->getName()) {
return $constant;
}
}
throw new Exception\RuntimeException(sprintf('There is no constant "%s".', $name), Exception\RuntimeException::DOES_NOT_EXIST, $this);
}
/**
* Returns constant values.
*
* @return array
*/
public function getConstants()
{
$constants = array();
foreach ($this->getConstantReflections() as $constant) {
$constants[$constant->getName()] = $constant->getValue();
}
return $constants;
}
/**
* Returns constant reflections.
*
* @return array
*/
public function getConstantReflections()
{
if (null === $this->parentClassName && empty($this->interfaces)) {
return array_values($this->constants);
} else {
$reflections = array_values($this->constants);
if (null !== $this->parentClassName) {
$reflections = array_merge($reflections, $this->getParentClass()->getConstantReflections());
}
foreach ($this->getOwnInterfaces() as $interface) {
$reflections = array_merge($reflections, $interface->getConstantReflections());
}
return $reflections;
}
}
/**
* Returns if the class (and not its parents) defines the given constant.
*
* @param string $name Constant name.
* @return boolean
*/
public function hasOwnConstant($name)
{
return isset($this->constants[$name]);
}
/**
* Returns constants declared by this class, not by its parents.
*
* @return array
*/
public function getOwnConstants()
{
return array_map(function(ReflectionConstant $constant) {
return $constant->getValue();
}, $this->constants);
}
/**
* Returns reflections of constants declared by this class, not by its parents.
*
* @return array
*/
public function getOwnConstantReflections()
{
return array_values($this->constants);
}
/**
* Returns if the class defines the given property.
*
* @param string $name Property name
* @return boolean
*/
public function hasProperty($name)
{
foreach ($this->getProperties() as $property) {
if ($name === $property->getName()) {
return true;
}
}
return false;
}
/**
* Return a property reflection.
*
* @param string $name Property name
* @return \TokenReflection\ReflectionProperty
* @throws \TokenReflection\Exception\RuntimeException If the requested property does not exist.
*/
public function getProperty($name)
{
if (isset($this->properties[$name])) {
return $this->properties[$name];
}
foreach ($this->getProperties() as $property) {
if ($name === $property->getName()) {
return $property;
}
}
throw new Exception\RuntimeException(sprintf('There is no property "%s".', $name, $this->name), Exception\RuntimeException::DOES_NOT_EXIST, $this);
}
/**
* Returns property reflections.
*
* @param integer $filter Properties filter
* @return array
*/
public function getProperties($filter = null)
{
$properties = $this->properties;
foreach ($this->getTraitProperties(null) as $traitProperty) {
if (!isset($properties[$traitProperty->getName()])) {
$properties[$traitProperty->getName()] = $traitProperty->alias($this);
}
}
if (null !== $this->parentClassName) {
foreach ($this->getParentClass()->getProperties(null) as $parentProperty) {
if (!isset($properties[$parentProperty->getName()])) {
$properties[$parentProperty->getName()] = $parentProperty;
}
}
}
if (null !== $filter) {
$properties = array_filter($properties, function(IReflectionProperty $property) use ($filter) {
return (bool) ($property->getModifiers() & $filter);
});
}
return array_values($properties);
}
/**
* Returns if the class (and not its parents) defines the given property.
*
* @param string $name Property name
* @return boolean
*/
public function hasOwnProperty($name)
{
return isset($this->properties[$name]);
}
/**
* Returns reflections of properties declared by this class, not its parents.
*
* @param integer $filter Properties filter
* @return array
*/
public function getOwnProperties($filter = null)
{
$properties = $this->properties;
if (null !== $filter) {
$properties = array_filter($properties, function(ReflectionProperty $property) use ($filter) {
return (bool) ($property->getModifiers() & $filter);
});
}
return array_values($properties);
}
/**
* Returns if the class imports the given property from traits.
*
* @param string $name Property name
* @return boolean
*/
public function hasTraitProperty($name)
{
if (isset($this->properties[$name])) {
return false;
}
foreach ($this->getOwnTraits() as $trait) {
if ($trait->hasProperty($name)) {
return true;
}
}
return false;
}
/**
* Returns reflections of properties imported from traits.
*
* @param integer $filter Properties filter
* @return array
*/
public function getTraitProperties($filter = null)
{
$properties = array();
foreach ($this->getOwnTraits() as $trait) {
foreach ($trait->getProperties(null) as $traitProperty) {
if (!isset($this->properties[$traitProperty->getName()]) && !isset($properties[$traitProperty->getName()])) {
$properties[$traitProperty->getName()] = $traitProperty->alias($this);
}
}
}
if (null !== $filter) {
$properties = array_filter($properties, function(IReflectionProperty $property) use ($filter) {
return (bool) ($property->getModifiers() & $filter);
});
}
return array_values($properties);
}
/**
* Returns default properties.
*
* @return array
*/
public function getDefaultProperties()
{
static $accessLevels = array(InternalReflectionProperty::IS_PUBLIC, InternalReflectionProperty::IS_PROTECTED, InternalReflectionProperty::IS_PRIVATE);
$defaults = array();
$properties = $this->getProperties();
foreach (array(true, false) as $static) {
foreach ($properties as $property) {
foreach ($accessLevels as $level) {
if ($property->isStatic() === $static && ($property->getModifiers() & $level)) {
$defaults[$property->getName()] = $property->getDefaultValue();
}
}
}
}
return $defaults;
}
/**
* Returns static properties reflections.
*
* @return array
*/
public function getStaticProperties()
{
$defaults = array();
foreach ($this->getProperties(InternalReflectionProperty::IS_STATIC) as $property) {
if ($property instanceof ReflectionProperty) {
$defaults[$property->getName()] = $property->getDefaultValue();
}
}
return $defaults;
}
/**
* Returns a value of a static property.
*
* @param string $name Property name
* @param mixed $default Default value
* @return mixed
* @throws \TokenReflection\Exception\RuntimeException If the requested static property does not exist.
* @throws \TokenReflection\Exception\RuntimeException If the requested static property is not accessible.
*/
public function getStaticPropertyValue($name, $default = null)
{
if ($this->hasProperty($name) && ($property = $this->getProperty($name)) && $property->isStatic()) {
if (!$property->isPublic() && !$property->isAccessible()) {
throw new Exception\RuntimeException(sprintf('Static property "%s" is not accessible.', $name), Exception\RuntimeException::NOT_ACCESSBILE, $this);
}
return $property->getDefaultValue();
}
throw new Exception\RuntimeException(sprintf('There is no static property "%s".', $name), Exception\RuntimeException::DOES_NOT_EXIST, $this);
}
/**
* Returns traits used by this class.
*
* @return array
*/
public function getTraits()
{
$traitNames = $this->getTraitNames();
if (empty($traitNames)) {
return array();
}
$broker = $this->getBroker();
return array_combine($traitNames, array_map(function($traitName) use ($broker) {
return $broker->getClass($traitName);
}, $traitNames));
}
/**
* Returns traits used by this class and not its parents.
*
* @return array
*/
public function getOwnTraits()
{
$ownTraitNames = $this->getOwnTraitNames();
if (empty($ownTraitNames)) {
return array();
}
$broker = $this->getBroker();
return array_combine($ownTraitNames, array_map(function($traitName) use ($broker) {
return $broker->getClass($traitName);
}, $ownTraitNames));
}
/**
* Returns names of used traits.
*
* @return array
*/
public function getTraitNames()
{
$parentClass = $this->getParentClass();
$names = $parentClass ? $parentClass->getTraitNames() : array();
foreach ($this->traits as $traitName) {
$names[] = $traitName;
}
return array_unique($names);
}
/**
* Returns names of traits used by this class an not its parents.
*
* @return array
*/
public function getOwnTraitNames()
{
return $this->traits;
}
/**
* Returns method aliases from traits.
*
* @return array
*/
public function getTraitAliases()
{
return $this->traitAliases;
}
/**
* Returns if the class is a trait.
*
* @return boolean
*/
public function isTrait()
{
return self::IS_TRAIT === $this->type;
}
/**
* Returns if the class definition is valid.
*
* @return boolean
*/
public function isValid()
{
if (null !== $this->parentClassName && !$this->getParentClass()->isValid()) {
return false;
}
foreach ($this->getInterfaces() as $interface) {
if (!$interface->isValid()) {
return false;
}
}
foreach ($this->getTraits() as $trait) {
if (!$trait->isValid()) {
return false;
}
}
return true;
}
/**
* Returns if the class uses a particular trait.
*
* @param \ReflectionClass|\TokenReflection\IReflectionClass|string $trait Trait reflection or name
* @return boolean
* @throws \TokenReflection\Exception\RuntimeException If an invalid parameter was provided.
*/
public function usesTrait($trait)
{
if (is_object($trait)) {
if (!$trait instanceof InternalReflectionClass && !$trait instanceof IReflectionClass) {
throw new Exception\RuntimeException(sprintf('Parameter must be a string or an instance of trait reflection, "%s" provided.', get_class($trait)), Exception\RuntimeException::INVALID_ARGUMENT, $this);
}
$traitName = $trait->getName();
if (!$trait->isTrait()) {
throw new Exception\RuntimeException(sprintf('"%s" is not a trait.', $traitName), Exception\RuntimeException::INVALID_ARGUMENT, $this);
}
} else {
$reflection = $this->getBroker()->getClass($trait);
if (!$reflection->isTrait()) {
throw new Exception\RuntimeException(sprintf('"%s" is not a trait.', $trait), Exception\RuntimeException::INVALID_ARGUMENT, $this);
}
$traitName = $trait;
}
return in_array($traitName, $this->getTraitNames());
}
/**
* Returns reflections of direct subclasses.
*
* @return array
*/
public function getDirectSubclasses()
{
$that = $this->name;
return array_filter($this->getBroker()->getClasses(), function(ReflectionClass $class) use ($that) {
if (!$class->isSubclassOf($that)) {
return false;
}
return null === $class->getParentClassName() || !$class->getParentClass()->isSubClassOf($that);
});
}
/**
* Returns names of direct subclasses.
*
* @return array
*/
public function getDirectSubclassNames()
{
return array_keys($this->getDirectSubclasses());
}
/**
* Returns reflections of indirect subclasses.
*
* @return array
*/
public function getIndirectSubclasses()
{
$that = $this->name;
return array_filter($this->getBroker()->getClasses(), function(ReflectionClass $class) use ($that) {
if (!$class->isSubclassOf($that)) {
return false;
}
return null !== $class->getParentClassName() && $class->getParentClass()->isSubClassOf($that);
});
}
/**
* Returns names of indirect subclasses.
*
* @return array
*/
public function getIndirectSubclassNames()
{
return array_keys($this->getIndirectSubclasses());
}
/**
* Returns reflections of classes directly implementing this interface.
*
* @return array
*/
public function getDirectImplementers()
{
if (!$this->isInterface()) {
return array();
}
$that = $this->name;
return array_filter($this->getBroker()->getClasses(), function(ReflectionClass $class) use ($that) {
if ($class->isInterface() || !$class->implementsInterface($that)) {
return false;
}
return null === $class->getParentClassName() || !$class->getParentClass()->implementsInterface($that);
});
}
/**
* Returns names of classes directly implementing this interface.
*
* @return array
*/
public function getDirectImplementerNames()
{
return array_keys($this->getDirectImplementers());
}
/**
* Returns reflections of classes indirectly implementing this interface.
*
* @return array
*/
public function getIndirectImplementers()
{
if (!$this->isInterface()) {
return array();
}
$that = $this->name;
return array_filter($this->getBroker()->getClasses(), function(ReflectionClass $class) use ($that) {
if ($class->isInterface() || !$class->implementsInterface($that)) {
return false;
}
return null !== $class->getParentClassName() && $class->getParentClass()->implementsInterface($that);
});
}
/**
* Returns names of classes indirectly implementing this interface.
*
* @return array
*/
public function getIndirectImplementerNames()
{
return array_keys($this->getIndirectImplementers());
}
/**
* Returns if the given object is an instance of this class.
*
* @param object $object Instance
* @return boolean
* @throws \TokenReflection\Exception\RuntimeException If the provided argument is not an object.
*/
public function isInstance($object)
{
if (!is_object($object)) {
throw new Exception\RuntimeException(sprintf('Parameter must be an object, "%s" provided.', gettype($object)), Exception\RuntimeException::INVALID_ARGUMENT, $this);
}
return $this->name === get_class($object) || is_subclass_of($object, $this->getName());
}
/**
* Creates a new class instance without using a constructor.
*
* @return object
* @throws \TokenReflection\Exception\RuntimeException If the class inherits from an internal class.
*/
public function newInstanceWithoutConstructor()
{
if (!class_exists($this->name, true)) {
throw new Exception\RuntimeException('Could not create an instance; class does not exist.', Exception\RuntimeException::DOES_NOT_EXIST, $this);
}
$reflection = new \TokenReflection\Php\ReflectionClass($this->getName(), $this->getBroker());
return $reflection->newInstanceWithoutConstructor();
}
/**
* Creates a new instance using variable number of parameters.
*
* Use any number of constructor parameters as function parameters.
*
* @param mixed $args
* @return object
*/
public function newInstance($args)
{
return $this->newInstanceArgs(func_get_args());
}
/**
* Creates a new instance using an array of parameters.
*
* @param array $args Array of constructor parameters
* @return object
* @throws \TokenReflection\Exception\RuntimeException If the required class does not exist.
*/
public function newInstanceArgs(array $args = array())
{
if (!class_exists($this->name, true)) {
throw new Exception\RuntimeException('Could not create an instance; class does not exist.', Exception\RuntimeException::DOES_NOT_EXIST, $this);
}
$reflection = new InternalReflectionClass($this->name);
return $reflection->newInstanceArgs($args);
}
/**
* Sets a static property value.
*
* @param string $name Property name
* @param mixed $value Property value
* @throws \TokenReflection\Exception\RuntimeException If the requested static property does not exist.
* @throws \TokenReflection\Exception\RuntimeException If the requested static property is not accessible.
*/
public function setStaticPropertyValue($name, $value)
{
if ($this->hasProperty($name) && ($property = $this->getProperty($name)) && $property->isStatic()) {
if (!$property->isPublic() && !$property->isAccessible()) {
throw new Exception\RuntimeException(sprintf('Static property "%s" is not accessible.', $name), Exception\RuntimeException::NOT_ACCESSBILE, $this);
}
$property->setDefaultValue($value);
return;
}
throw new Exception\RuntimeException(sprintf('There is no static property "%s".', $name), Exception\RuntimeException::DOES_NOT_EXIST, $this);
}
/**
* Returns the string representation of the reflection object.
*
* @return string
*/
public function __toString()
{
$implements = '';
$interfaceNames = $this->getInterfaceNames();
if (count($interfaceNames) > 0) {
$implements = sprintf(
' %s %s',
$this->isInterface() ? 'extends' : 'implements',
implode(', ', $interfaceNames)
);
}
$buffer = '';
$count = 0;
foreach ($this->getConstantReflections() as $constant) {
$buffer .= ' ' . $constant->__toString();
$count++;
}
$constants = sprintf("\n\n - Constants [%d] {\n%s }", $count, $buffer);
$sBuffer = '';
$sCount = 0;
$buffer = '';
$count = 0;
foreach ($this->getProperties() as $property) {
$string = ' ' . preg_replace('~\n(?!$)~', "\n ", $property->__toString());
if ($property->isStatic()) {
$sBuffer .= $string;
$sCount++;
} else {
$buffer .= $string;
$count++;
}
}
$staticProperties = sprintf("\n\n - Static properties [%d] {\n%s }", $sCount, $sBuffer);
$properties = sprintf("\n\n - Properties [%d] {\n%s }", $count, $buffer);
$sBuffer = '';
$sCount = 0;
$buffer = '';
$count = 0;
foreach ($this->getMethods() as $method) {
// Skip private methods of parent classes
if ($method->getDeclaringClassName() !== $this->getName() && $method->isPrivate()) {
continue;
}
// Indent
$string = "\n ";
$string .= preg_replace('~\n(?!$|\n|\s*\*)~', "\n ", $method->__toString());
// Add inherits
if ($method->getDeclaringClassName() !== $this->getName()) {
$string = preg_replace(
array('~Method [ <[\w:]+~', '~, overwrites[^,]+~'),
array('\0, inherits ' . $method->getDeclaringClassName(), ''),
$string
);
}
if ($method->isStatic()) {
$sBuffer .= $string;
$sCount++;
} else {
$buffer .= $string;
$count++;
}
}
$staticMethods = sprintf("\n\n - Static methods [%d] {\n%s }", $sCount, ltrim($sBuffer, "\n"));
$methods = sprintf("\n\n - Methods [%d] {\n%s }", $count, ltrim($buffer, "\n"));
return sprintf(
"%s%s [ <user>%s %s%s%s %s%s%s ] {\n @@ %s %d-%d%s%s%s%s%s\n}\n",
$this->getDocComment() ? $this->getDocComment() . "\n" : '',
$this->isInterface() ? 'Interface' : 'Class',
$this->isIterateable() ? ' <iterateable>' : '',
$this->isAbstract() && !$this->isInterface() ? 'abstract ' : '',
$this->isFinal() ? 'final ' : '',
$this->isInterface() ? 'interface' : 'class',
$this->getName(),
null !== $this->getParentClassName() ? ' extends ' . $this->getParentClassName() : '',
$implements,
$this->getFileName(),
$this->getStartLine(),
$this->getEndLine(),
$constants,
$staticProperties,
$staticMethods,
$properties,
$methods
);
}
/**
* Exports a reflected object.
*
* @param \TokenReflection\Broker $broker Broker instance
* @param string|object $className Class name or class instance
* @param boolean $return Return the export instead of outputting it
* @return string|null
* @throws \TokenReflection\Exception\RuntimeException If requested parameter doesn't exist.
*/
public static function export(Broker $broker, $className, $return = false)
{
if (is_object($className)) {
$className = get_class($className);
}
$class = $broker->getClass($className);
if ($class instanceof Invalid\ReflectionClass) {
throw new Exception\RuntimeException('Class is invalid.', Exception\RuntimeException::UNSUPPORTED);
} elseif ($class instanceof Dummy\ReflectionClass) {
throw new Exception\RuntimeException('Class does not exist.', Exception\RuntimeException::DOES_NOT_EXIST);
}
if ($return) {
return $class->__toString();
}
echo $class->__toString();
}
/**
* Returns if the class definition is complete.
*
* @return boolean
*/
public function isComplete()
{
if (!$this->definitionComplete) {
if (null !== $this->parentClassName && !$this->getParentClass()->isComplete()) {
return false;
}
foreach ($this->getOwnInterfaces() as $interface) {
if (!$interface->isComplete()) {
return false;
}
}
$this->definitionComplete = true;
}
return $this->definitionComplete;
}
/**
* Returns imported namespaces and aliases from the declaring namespace.
*
* @return array
*/
public function getNamespaceAliases()
{
return $this->aliases;
}
/**
* Processes the parent reflection object.
*
* @param \TokenReflection\IReflection $parent Parent reflection object
* @param \TokenReflection\Stream\StreamBase $tokenStream Token substream
* @return \TokenReflection\ReflectionClass
* @throws \TokenReflection\ParseException On invalid parent reflection provided
*/
protected function processParent(IReflection $parent, Stream $tokenStream)
{
if (!$parent instanceof ReflectionFileNamespace) {
throw new Exception\ParseException($this, $tokenStream, sprintf('Invalid parent reflection provided: "%s".', get_class($parent)), Exception\ParseException::INVALID_PARENT);
}
$this->namespaceName = $parent->getName();
$this->aliases = $parent->getNamespaceAliases();
return parent::processParent($parent, $tokenStream);
}
/**
* Parses reflected element metadata from the token stream.
*
* @param \TokenReflection\Stream\StreamBase $tokenStream Token substream
* @param \TokenReflection\IReflection $parent Parent reflection object
* @return \TokenReflection\ReflectionClass
*/
protected function parse(Stream $tokenStream, IReflection $parent)
{
return $this
->parseModifiers($tokenStream)
->parseName($tokenStream)
->parseParent($tokenStream, $parent)
->parseInterfaces($tokenStream, $parent);
}
/**
* Parses class modifiers (abstract, final) and class type (class, interface).
*
* @param \TokenReflection\Stream\StreamBase $tokenStream Token substream
* @return \TokenReflection\ReflectionClass
*/
private function parseModifiers(Stream $tokenStream)
{
while (true) {
switch ($tokenStream->getType()) {
case null:
break 2;
case T_ABSTRACT:
$this->modifiers = InternalReflectionClass::IS_EXPLICIT_ABSTRACT;
break;
case T_FINAL:
$this->modifiers = InternalReflectionClass::IS_FINAL;
break;
case T_INTERFACE:
$this->modifiers = self::IS_INTERFACE;
$this->type = self::IS_INTERFACE;
$tokenStream->skipWhitespaces(true);
break 2;
case T_TRAIT:
$this->modifiers = self::IS_TRAIT;
$this->type = self::IS_TRAIT;
$tokenStream->skipWhitespaces(true);
break 2;
case T_CLASS:
$tokenStream->skipWhitespaces(true);
break 2;
default:
break;
}
$tokenStream->skipWhitespaces(true);
}
return $this;
}
/**
* Parses the class/interface name.
*
* @param \TokenReflection\Stream\StreamBase $tokenStream Token substream
* @return \TokenReflection\ReflectionClass
* @throws \TokenReflection\Exception\ParseException If the class name could not be determined.
*/
protected function parseName(Stream $tokenStream)
{
if (!$tokenStream->is(T_STRING)) {
throw new Exception\ParseException($this, $tokenStream, 'Unexpected token found.', Exception\ParseException::UNEXPECTED_TOKEN);
}
if ($this->namespaceName === ReflectionNamespace::NO_NAMESPACE_NAME) {
$this->name = $tokenStream->getTokenValue();
} else {
$this->name = $this->namespaceName . '\\' . $tokenStream->getTokenValue();
}
$tokenStream->skipWhitespaces(true);
return $this;
}
/**
* Parses the parent class.
*
* @param \TokenReflection\Stream\StreamBase $tokenStream Token substream
* @param \TokenReflection\IReflection $parent Parent reflection object
* @return \TokenReflection\ReflectionClass
*/
private function parseParent(Stream $tokenStream, ReflectionElement $parent = null)
{
if (!$tokenStream->is(T_EXTENDS)) {
return $this;
}
while (true) {
$tokenStream->skipWhitespaces(true);
$parentClassName = '';
while (true) {
switch ($tokenStream->getType()) {
case T_STRING:
case T_NS_SEPARATOR:
$parentClassName .= $tokenStream->getTokenValue();
break;
default:
break 2;
}
$tokenStream->skipWhitespaces(true);
}
$parentClassName = Resolver::resolveClassFQN($parentClassName, $this->aliases, $this->namespaceName);
if ($this->isInterface()) {
$this->interfaces[] = $parentClassName;
if (',' === $tokenStream->getTokenValue()) {
continue;
}
} else {
$this->parentClassName = $parentClassName;
}
break;
}
return $this;
}
/**
* Parses implemented interfaces.
*
* @param \TokenReflection\Stream\StreamBase $tokenStream Token substream
* @param \TokenReflection\IReflection $parent Parent reflection object
* @return \TokenReflection\ReflectionClass
* @throws \TokenReflection\Exception\ParseException On error while parsing interfaces.
*/
private function parseInterfaces(Stream $tokenStream, ReflectionElement $parent = null)
{
if (!$tokenStream->is(T_IMPLEMENTS)) {
return $this;
}
if ($this->isInterface()) {
throw new Exception\ParseException($this, $tokenStream, 'Interfaces cannot implement interfaces.', Exception\ParseException::LOGICAL_ERROR);
}
while (true) {
$interfaceName = '';
$tokenStream->skipWhitespaces(true);
while (true) {
switch ($tokenStream->getType()) {
case T_STRING:
case T_NS_SEPARATOR:
$interfaceName .= $tokenStream->getTokenValue();
break;
default:
break 2;
}
$tokenStream->skipWhitespaces(true);
}
$this->interfaces[] = Resolver::resolveClassFQN($interfaceName, $this->aliases, $this->namespaceName);
$type = $tokenStream->getType();
if ('{' === $type) {
break;
} elseif (',' !== $type) {
throw new Exception\ParseException($this, $tokenStream, 'Unexpected token found, expected "{" or ";".', Exception\ParseException::UNEXPECTED_TOKEN);
}
}
return $this;
}
/**
* Parses child reflection objects from the token stream.
*
* @param \TokenReflection\Stream\StreamBase $tokenStream Token substream
* @param \TokenReflection\IReflection $parent Parent reflection object
* @return \TokenReflection\ReflectionClass
* @throws \TokenReflection\Exception\ParseException If a parse error was detected.
*/
protected function parseChildren(Stream $tokenStream, IReflection $parent)
{
while (true) {
switch ($type = $tokenStream->getType()) {
case null:
break 2;
case T_COMMENT:
case T_DOC_COMMENT:
$docblock = $tokenStream->getTokenValue();
if (preg_match('~^' . preg_quote(self::DOCBLOCK_TEMPLATE_START, '~') . '~', $docblock)) {
array_unshift($this->docblockTemplates, new ReflectionAnnotation($this, $docblock));
} elseif (self::DOCBLOCK_TEMPLATE_END === $docblock) {
array_shift($this->docblockTemplates);
}
$tokenStream->next();
break;
case '}':
break 2;
case T_PUBLIC:
case T_PRIVATE:
case T_PROTECTED:
case T_STATIC:
case T_VAR:
case T_VARIABLE:
static $searching = array(T_VARIABLE => true, T_FUNCTION => true);
if (T_VAR !== $tokenStream->getType()) {
$position = $tokenStream->key();
while (null !== ($type = $tokenStream->getType($position)) && !isset($searching[$type])) {
$position++;
}
}
if (T_VARIABLE === $type || T_VAR === $type) {
$property = new ReflectionProperty($tokenStream, $this->getBroker(), $this);
$this->properties[$property->getName()] = $property;
$tokenStream->next();
break;
}
// Break missing on purpose
case T_FINAL:
case T_ABSTRACT:
case T_FUNCTION:
$method = new ReflectionMethod($tokenStream, $this->getBroker(), $this);
$this->methods[$method->getName()] = $method;
$tokenStream->next();
break;
case T_CONST:
$tokenStream->skipWhitespaces(true);
while ($tokenStream->is(T_STRING)) {
$constant = new ReflectionConstant($tokenStream, $this->getBroker(), $this);
$this->constants[$constant->getName()] = $constant;
if ($tokenStream->is(',')) {
$tokenStream->skipWhitespaces(true);
} else {
$tokenStream->next();
}
}
break;
case T_USE:
$tokenStream->skipWhitespaces(true);
while (true) {
$traitName = '';
$type = $tokenStream->getType();
while (T_STRING === $type || T_NS_SEPARATOR === $type) {
$traitName .= $tokenStream->getTokenValue();
$type = $tokenStream->skipWhitespaces(true)->getType();
}
if ('' === trim($traitName, '\\')) {
throw new Exception\ParseException($this, $tokenStream, 'An empty trait name found.', Exception\ParseException::LOGICAL_ERROR);
}
$this->traits[] = Resolver::resolveClassFQN($traitName, $this->aliases, $this->namespaceName);
if (';' === $type) {
// End of "use"
$tokenStream->skipWhitespaces();
break;
} elseif (',' === $type) {
// Next trait name follows
$tokenStream->skipWhitespaces();
continue;
} elseif ('{' !== $type) {
// Unexpected token
throw new Exception\ParseException($this, $tokenStream, 'Unexpected token found: "%s".', Exception\ParseException::UNEXPECTED_TOKEN);
}
// Aliases definition
$type = $tokenStream->skipWhitespaces(true)->getType();
while (true) {
if ('}' === $type) {
$tokenStream->skipWhitespaces();
break 2;
}
$leftSide = '';
$rightSide = array('', null);
$alias = true;
while (T_STRING === $type || T_NS_SEPARATOR === $type || T_DOUBLE_COLON === $type) {
$leftSide .= $tokenStream->getTokenValue();
$type = $tokenStream->skipWhitespaces(true)->getType();
}
if (T_INSTEADOF === $type) {
$alias = false;
} elseif (T_AS !== $type) {
throw new Exception\ParseException($this, $tokenStream, 'Unexpected token found.', Exception\ParseException::UNEXPECTED_TOKEN);
}
$type = $tokenStream->skipWhitespaces(true)->getType();
if (T_PUBLIC === $type || T_PROTECTED === $type || T_PRIVATE === $type) {
if (!$alias) {
throw new Exception\ParseException($this, $tokenStream, 'Unexpected token found.', Exception\ParseException::UNEXPECTED_TOKEN);
}
switch ($type) {
case T_PUBLIC:
$type = InternalReflectionMethod::IS_PUBLIC;
break;
case T_PROTECTED:
$type = InternalReflectionMethod::IS_PROTECTED;
break;
case T_PRIVATE:
$type = InternalReflectionMethod::IS_PRIVATE;
break;
default:
break;
}
$rightSide[1] = $type;
$type = $tokenStream->skipWhitespaces(true)->getType();
}
while (T_STRING === $type || (T_NS_SEPARATOR === $type && !$alias)) {
$rightSide[0] .= $tokenStream->getTokenValue();
$type = $tokenStream->skipWhitespaces(true)->getType();
}
if (empty($leftSide)) {
throw new Exception\ParseException($this, $tokenStream, 'An empty method name was found.', Exception\ParseException::LOGICAL_ERROR);
}
if ($alias) {
// Alias
if ($pos = strpos($leftSide, '::')) {
$methodName = substr($leftSide, $pos + 2);
$className = Resolver::resolveClassFQN(substr($leftSide, 0, $pos), $this->aliases, $this->namespaceName);
$leftSide = $className . '::' . $methodName;
$this->traitAliases[$rightSide[0]] = $leftSide;
} else {
$this->traitAliases[$rightSide[0]] = '(null)::' . $leftSide;
}
$this->traitImports[$leftSide][] = $rightSide;
} else {
// Insteadof
if ($pos = strpos($leftSide, '::')) {
$methodName = substr($leftSide, $pos + 2);
} else {
throw new Exception\ParseException($this, $tokenStream, 'A T_DOUBLE_COLON has to be present when using T_INSTEADOF.', Exception\ParseException::UNEXPECTED_TOKEN);
}
$this->traitImports[Resolver::resolveClassFQN($rightSide[0], $this->aliases, $this->namespaceName) . '::' . $methodName][] = null;
}
if (',' === $type) {
$tokenStream->skipWhitespaces(true);
continue;
} elseif (';' !== $type) {
throw new Exception\ParseException($this, $tokenStream, 'Unexpected token found.', Exception\ParseException::UNEXPECTED_TOKEN);
}
$type = $tokenStream->skipWhitespaces()->getType();
}
}
break;
default:
$tokenStream->next();
break;
}
}
return $this;
}
}