/** * Get a ReflectionProperty deeply. * * @param \ReflectionClass $class * @param string $dottedReference The given dotted reference * @return mixed Usually a \ReflectionProperty. If the dotted reference falls into * an associative array property, we will return "<array>" */ private function getDeepProperty(\ReflectionClass $class, $dottedReference) { $originalClass = $class; $fields = $this->pathParser->parse($dottedReference); $fieldCount = count($fields); $property = null; $className = $originalClass->getName(); $properties = array(); foreach ($fields as $i => $field) { if ($className == '<array>') { $properties[] = '<array>'; continue; } $isPrimitive = $this->isPrimitiveType($className); // Cannot traverse into primitive types (it is impossible for this // to happen on first iteration) if ($isPrimitive) { throw new \InvalidArgumentException("Cannot traverse into primitive type {$className} " . "to satisfy reference '{$field->field}' " . "of full reference '{$dottedReference}'"); } $currentClass = new \ReflectionClass($className); $property = $currentClass->getProperty($field->field); $properties[] = $property; // We're done? We'll return $property at the end. if ($i + 1 >= $fieldCount) { break; } $className = $this->reflector->getTypeFromProperty($property); if (!$className) { throw new \InvalidArgumentException("Failed to reflect on field reference '{$dottedReference}' of class {$originalClass->getName()}"); } } return $properties; }
/** * Get a ReflectionProperty deeply. * * @param \ReflectionClass $class * @param string $dottedReference The given dotted reference * @return mixed Usually a \ReflectionProperty. If the dotted reference falls into * an associative array property, we will return "<array>" */ private function getDeepProperty(\ReflectionClass $class, $dottedReference) { $pathParser = new PathParser(); $reflector = new Reflector(); $originalClass = $class; $fields = $pathParser->parse($dottedReference); $fieldCount = count($fields); $reflectionField = null; $className = $originalClass->getName(); $reflectionFields = array(); foreach ($fields as $i => $field) { if ($className == '<array>') { $reflectionFields[] = '<array>'; continue; } // Cannot traverse into primitive types (it is impossible for this // to happen on first iteration) if ($reflector->isPrimitiveType($className)) { throw new \InvalidArgumentException("Cannot traverse into primitive type {$className} " . "to satisfy reference '{$field->field}' " . "of full reference '{$dottedReference}'"); } $currentClass = new \ReflectionClass($className); $fieldName = $field->field; $methodName = "get{$fieldName}"; if ($currentClass->hasMethod($methodName)) { //Method $reflectionField = $currentClass->getMethod($methodName); } else { if ($currentClass->hasProperty($fieldName)) { //Property $reflectionField = $currentClass->getProperty($fieldName); } else { throw new \Exception("Could get property \"{$fieldName}\" in the class \"{$className}\" it either does not exist or does not have a getter."); } } $reflectionFields[] = $reflectionField; // We're done? We'll return $property at the end. if ($i + 1 >= $fieldCount) { break; } $className = $reflector->getType($reflectionField); if (!$className) { throw new \InvalidArgumentException("Failed to reflect on field reference '{$dottedReference}' of class {$originalClass->getName()}"); } } return $reflectionFields; }
/** * Gets the value of the property named $field on $instance. * * @param object $instance * @param mixed $field A string path (see PathParser), an array of path field reference objects, * or a single field reference object. * @return mixed * @throws \Exception Thrown if no such field/property exists */ private function get($instance, $field) { if (is_string($field)) { $field = $this->pathParser->parse($field); } if (is_array($field)) { $path = $field; } else { $path = array($field); } // Walk the path to the penultimate object. $contextObject =& $instance; foreach ($path as $i => $item) { if ($i + 1 >= count($path)) { continue; } $contextObject = $this->get($contextObject, $item); } $fieldName = $path[count($path) - 1]->field; // Arrays... if (is_array($contextObject)) { if (!isset($contextObject[$fieldName])) { return null; } return $contextObject[$fieldName]; } // Doesn't exist _yet_! if (is_null($contextObject)) { return null; } // Various ways of retrieving an object field... if (is_object($contextObject)) { $methodName = "get{$fieldName}"; if (method_exists($contextObject, $methodName)) { return $contextObject->{$methodName}(); } else { if (property_exists($contextObject, $fieldName)) { return $contextObject->{$fieldName}; } else { throw new \InvalidArgumentException("No such property {$fieldName} on object of class " . get_class($contextObject)); } } } throw new \InvalidArgumentException("Cannot set field {$fieldName} on a non-object:\nPath trace: " . print_r($path, true) . "\nObject trace: " . print_r($contextObject, true)); }