getDefaultProperties(); if (isset($target->{$property}) && $target->{$property} !== ($defaultProperties[$property] ?? null)) { return $target->{$property}; } if ($instance = $this->getAttributeInstance($target, $attributeClass, $attributeDeclaringClass)) { if ($this->propertyOverridesAttribute($target, $reflection, $property, $attributeDeclaringClass)) { return $target->{$property}; } return $this->extractAttributeValue($instance); } return $target->{$property} ?? $default; } /** * Extract the value from an attribute instance. * * @param object $instance * @return mixed */ protected function extractAttributeValue($instance) { $properties = get_object_vars($instance); return $properties === [] ? true : reset($properties); } /** * Get an instance of the given attribute class from the target class or its parents. * * @param object $target * @param class-string $attributeClass * @param \ReflectionClass|null $declaringClass * @return object|null */ protected function getAttributeInstance($target, string $attributeClass, ?ReflectionClass &$declaringClass = null) { $reflection = new ReflectionClass($target); try { do { $attributes = $reflection->getAttributes($attributeClass); if (count($attributes) > 0) { $declaringClass = $reflection; return $attributes[0]->newInstance(); } } while ($reflection = $reflection->getParentClass()); } catch (Exception) { // } return null; } /** * Determine if a property declared on a child class overrides an inherited attribute. * * @param object $target * @param \ReflectionClass $reflection * @param string|null $property * @param \ReflectionClass $attributeDeclaringClass * @return bool */ protected function propertyOverridesAttribute($target, ReflectionClass $reflection, ?string $property, ReflectionClass $attributeDeclaringClass) { if (is_null($property) || ! $reflection->hasProperty($property)) { return false; } $property = $reflection->getProperty($property); return $property->isPublic() && $property->isInitialized($target) && $property->getDeclaringClass()->isSubclassOf($attributeDeclaringClass->getName()); } }