. */ namespace Doctrine\ORM\Mapping\Reflection; use Doctrine\Common\Persistence\Mapping\ReflectionService; use ReflectionClass; use ReflectionProperty; /** * Utility class to retrieve all reflection instance properties of a given class, including * private inherited properties and transient properties. * * @private This API is for internal use only * * @author Marco Pivetta */ final class ReflectionPropertiesGetter { /** * @var ReflectionProperty[][] indexed by class name and property internal name */ private $properties = []; /** * @var ReflectionService */ private $reflectionService; /** * @param ReflectionService $reflectionService */ public function __construct(ReflectionService $reflectionService) { $this->reflectionService = $reflectionService; } /** * @param $className * * @return ReflectionProperty[] indexed by property internal name */ public function getProperties($className) { if (isset($this->properties[$className])) { return $this->properties[$className]; } return $this->properties[$className] = call_user_func_array( 'array_merge', // first merge because `array_merge` expects >= 1 params array_merge( [[]], array_map( [$this, 'getClassProperties'], $this->getHierarchyClasses($className) ) ) ); } /** * @param string $className * * @return ReflectionClass[] */ private function getHierarchyClasses($className) { $classes = []; $parentClassName = $className; while ($parentClassName && $currentClass = $this->reflectionService->getClass($parentClassName)) { $classes[] = $currentClass; $parentClassName = null; if ($parentClass = $currentClass->getParentClass()) { $parentClassName = $parentClass->getName(); } } return $classes; } /** * @param ReflectionClass $reflectionClass * * @return ReflectionProperty[] */ private function getClassProperties(ReflectionClass $reflectionClass) { $properties = $reflectionClass->getProperties(); return array_filter( array_filter(array_map( [$this, 'getAccessibleProperty'], array_combine( array_map([$this, 'getLogicalName'], $properties), $properties ) )), [$this, 'isInstanceProperty'] ); } /** * @param ReflectionProperty $reflectionProperty * * @return bool */ private function isInstanceProperty(ReflectionProperty $reflectionProperty) { return ! $reflectionProperty->isStatic(); } /** * @param ReflectionProperty $property * * @return null|ReflectionProperty */ private function getAccessibleProperty(ReflectionProperty $property) { return $this->reflectionService->getAccessibleProperty( $property->getDeclaringClass()->getName(), $property->getName() ); } /** * @param ReflectionProperty $property * * @return string */ private function getLogicalName(ReflectionProperty $property) { $propertyName = $property->getName(); if ($property->isPublic()) { return $propertyName; } if ($property->isProtected()) { return "\0*\0" . $propertyName; } return "\0" . $property->getDeclaringClass()->getName() . "\0" . $propertyName; } }