diff --git a/Neos.Flow/Classes/Aop/Builder/ProxyClassBuilder.php b/Neos.Flow/Classes/Aop/Builder/ProxyClassBuilder.php index 757b0d72b6..c1dccd1c33 100644 --- a/Neos.Flow/Classes/Aop/Builder/ProxyClassBuilder.php +++ b/Neos.Flow/Classes/Aop/Builder/ProxyClassBuilder.php @@ -398,6 +398,10 @@ public function buildProxyClass(string $targetClassName, array $aspectContainers $propertyName = $propertyIntroduction->getPropertyName(); $declaringAspectClassName = $propertyIntroduction->getDeclaringAspectClassName(); $possiblePropertyTypes = $this->reflectionService->getPropertyTagValues($declaringAspectClassName, $propertyName, 'var'); + if (count($possiblePropertyTypes) === 0) { + $typeHint = $this->reflectionService->getPropertyType($declaringAspectClassName, $propertyName); + $possiblePropertyTypes = $typeHint !== null ? [$typeHint] : []; + } if (count($possiblePropertyTypes) > 0 && !$this->reflectionService->isPropertyAnnotatedWith($declaringAspectClassName, $propertyName, Flow\Transient::class)) { $classSchema = $this->reflectionService->getClassSchema($targetClassName); $classSchema?->addProperty($propertyName, $possiblePropertyTypes[0]); diff --git a/Neos.Flow/Classes/ObjectManagement/Configuration/ConfigurationBuilder.php b/Neos.Flow/Classes/ObjectManagement/Configuration/ConfigurationBuilder.php index 79dc0550d0..318b9b59d9 100644 --- a/Neos.Flow/Classes/ObjectManagement/Configuration/ConfigurationBuilder.php +++ b/Neos.Flow/Classes/ObjectManagement/Configuration/ConfigurationBuilder.php @@ -339,20 +339,8 @@ protected static function parseAutowiring($value) protected function parsePropertyOfTypeObject($propertyName, $objectNameOrConfiguration, Configuration $parentObjectConfiguration) { if (is_array($objectNameOrConfiguration)) { - if (isset($objectNameOrConfiguration['name'])) { - $objectName = $objectNameOrConfiguration['name']; - unset($objectNameOrConfiguration['name']); - } else { - if (isset($objectNameOrConfiguration['factoryObjectName']) || isset($objectNameOrConfiguration['factoryMethodName'])) { - $objectName = null; - } else { - $annotations = $this->reflectionService->getPropertyTagValues($parentObjectConfiguration->getClassName(), $propertyName, 'var'); - if (count($annotations) !== 1) { - throw new InvalidObjectConfigurationException(sprintf('Object %s (%s), for property "%s", contains neither object name, nor factory object name, and nor is the property properly @var - annotated.', $parentObjectConfiguration->getClassName(), $parentObjectConfiguration->getConfigurationSourceHint(), $propertyName), 1297097815); - } - $objectName = $annotations[0]; - } - } + $objectName = $this->determineObjectNameOfProperty($objectNameOrConfiguration, $parentObjectConfiguration->getClassName(), $propertyName, $parentObjectConfiguration->getConfigurationSourceHint()); + unset($objectNameOrConfiguration['name']); $objectConfiguration = $this->parseConfigurationArray($objectName, $objectNameOrConfiguration, $parentObjectConfiguration->getConfigurationSourceHint() . ', property "' . $propertyName . '"'); $property = new ConfigurationProperty($propertyName, $objectConfiguration, ConfigurationProperty::PROPERTY_TYPES_OBJECT); } else { @@ -361,6 +349,42 @@ protected function parsePropertyOfTypeObject($propertyName, $objectNameOrConfigu return $property; } + /** + * @param $objectNameOrConfiguration + * @param $parentClassName + * @param $propertyName + * @param string $configurationSourceHint + * @return string + * @throws InvalidObjectConfigurationException + * @throws \Neos\Flow\Reflection\Exception\ClassLoadingForReflectionFailedException + * @throws \Neos\Flow\Reflection\Exception\InvalidClassException + * @throws \ReflectionException + */ + protected function determineObjectNameOfProperty($objectNameOrConfiguration, $parentClassName, $propertyName, string $configurationSourceHint): string + { + if (isset($objectNameOrConfiguration['name'])) { + return $objectNameOrConfiguration['name']; + } + + if (isset($objectNameOrConfiguration['factoryObjectName']) || isset($objectNameOrConfiguration['factoryMethodName'])) { + return ''; + } + + $varTags = $this->reflectionService->getPropertyTagValues($parentClassName, $propertyName, 'var'); + if (count($varTags) > 1) { + throw new InvalidObjectConfigurationException(sprintf('Object %s (%s), for property "%s", contains neither object name, nor factory object name, and nor is the property properly @var - annotated.', $parentClassName, $configurationSourceHint, $propertyName), 1297097815); + } + if (count($varTags) === 1) { + return $varTags[0]; + } + $typeHint = $this->reflectionService->getPropertyType($parentClassName, $propertyName); + if ($typeHint === null) { + throw new InvalidObjectConfigurationException(sprintf('Object %s (%s), for property "%s", contains neither object name, nor factory object name, and neither is the property properly type hinted or @var - tagged.', $parentClassName, $configurationSourceHint, $propertyName), 1779793488); + } + + return $typeHint; + } + /** * Parses the configuration for arguments of type OBJECT * @@ -590,6 +614,7 @@ protected function autowireProperties(array &$objectConfigurations) $enableLazyInjection = false; # See: https://github.com/neos/flow-development-collection/issues/2114 } } + // This is already a fallback, getPropertyType is used first. if ($objectName === null) { $objectName = trim(implode('', $this->reflectionService->getPropertyTagValues($className, $propertyName, 'var')), ' \\'); } diff --git a/Neos.Flow/Classes/Property/TypeConverter/ObjectConverter.php b/Neos.Flow/Classes/Property/TypeConverter/ObjectConverter.php index 0c350a3cfa..4e6f5726c1 100644 --- a/Neos.Flow/Classes/Property/TypeConverter/ObjectConverter.php +++ b/Neos.Flow/Classes/Property/TypeConverter/ObjectConverter.php @@ -156,8 +156,13 @@ public function getTypeOfChildProperty($targetType, $propertyName, PropertyMappi } return $parsedType['type'] . ($parsedType['elementType'] !== null ? '<' . $parsedType['elementType'] . '>' : ''); } + $typeHint = $this->reflectionService->getPropertyType($targetType, $propertyName); + if ($typeHint !== null) { + $parsedType = TypeHandling::parseType($typeHint); + return $parsedType['type']; + } - throw new InvalidTargetException(sprintf('Public property "%s" had no proper type annotation (i.e. "@var") in target object of type "%s".', $propertyName, $targetType), 1406821818); + throw new InvalidTargetException(sprintf('Public property "%s" had no type hints or proper type tag (i.e. "@var") in target object of type "%s".', $propertyName, $targetType), 1406821818); } } diff --git a/Neos.Flow/Classes/Reflection/ReflectionService.php b/Neos.Flow/Classes/Reflection/ReflectionService.php index 79bf710754..8dcfb95986 100644 --- a/Neos.Flow/Classes/Reflection/ReflectionService.php +++ b/Neos.Flow/Classes/Reflection/ReflectionService.php @@ -846,7 +846,22 @@ public function getPropertyTagsValues(string $className, string $propertyName): public function getPropertyTagValues(string $className, string $propertyName, string $tag): array { $className = $this->prepareClassReflectionForUsage($className); - return $this->classReflectionData[$className][self::DATA_CLASS_PROPERTIES][$propertyName][self::DATA_PROPERTY_TAGS_VALUES][$tag] ?? []; + $tagValues = $this->classReflectionData[$className][self::DATA_CLASS_PROPERTIES][$propertyName][self::DATA_PROPERTY_TAGS_VALUES][$tag] ?? []; + if ($tag !== 'var') { + return $tagValues; + } + + if ($tagValues !== []) { + return $tagValues; + } + + // Upgrade behavior for "var" tag requests also considering the type hints as fallback. + $propertyType = $this->getPropertyType($className, $propertyName); + if ($propertyType !== null) { + return [$propertyType]; + } + + return []; } /** diff --git a/Neos.Flow/Classes/Validation/ValidatorResolver.php b/Neos.Flow/Classes/Validation/ValidatorResolver.php index 54ff349e36..03efe0aaf9 100644 --- a/Neos.Flow/Classes/Validation/ValidatorResolver.php +++ b/Neos.Flow/Classes/Validation/ValidatorResolver.php @@ -296,20 +296,17 @@ protected function buildBaseValidatorConjunction($indexKey, $targetClassName, ar } $conjunctionValidator->addValidator($objectValidator); foreach ($this->reflectionService->getClassPropertyNames($targetClassName) as $classPropertyName) { + if ($this->reflectionService->isPropertyAnnotatedWith($targetClassName, $classPropertyName, Flow\IgnoreValidation::class)) { + continue; + } + $classPropertyTagsValues = $this->reflectionService->getPropertyTagsValues($targetClassName, $classPropertyName); if (!isset($classPropertyTagsValues['var'])) { - try { - $propertyReflection = new PropertyReflection($targetClassName, $classPropertyName); - } catch (\ReflectionException $e) { - throw new \RuntimeException(sprintf('Failed reflecting property %s from class %s while building base a validator conjunction: %s', $classPropertyName, $targetClassName, $e->getMessage()), 1651570561); - } - - if (!$propertyReflection->hasType()) { + $type = $this->reflectionService->getPropertyType($targetClassName, $classPropertyName); + if ($type === null) { throw new \InvalidArgumentException(sprintf('Failed building base validator conjunction for property %s in class %s because there is no @var annotation and no type declaration.', $classPropertyName, $targetClassName), 1363778104); } - /** @var \ReflectionNamedType $type */ - $type = $propertyReflection->getType(); - $classPropertyTagsValues['var'][] = $type->getName(); + $classPropertyTagsValues['var'][] = $this->reflectionService->getPropertyType($targetClassName, $classPropertyName); } try { $parsedType = TypeHandling::parseType(trim(implode('', $classPropertyTagsValues['var']), ' \\')); @@ -317,9 +314,6 @@ protected function buildBaseValidatorConjunction($indexKey, $targetClassName, ar throw new \InvalidArgumentException(sprintf(' @var annotation of ' . $exception->getMessage(), 'class "' . $targetClassName . '", property "' . $classPropertyName . '"'), 1315564744, $exception); } - if ($this->reflectionService->isPropertyAnnotatedWith($targetClassName, $classPropertyName, Flow\IgnoreValidation::class)) { - continue; - } if ($classSchema !== null && $classSchema->hasProperty($classPropertyName) && $classSchema->isPropertyTransient($classPropertyName)