diff --git a/Neos.Flow/Classes/Reflection/ReflectionService.php b/Neos.Flow/Classes/Reflection/ReflectionService.php index 1067650605..e444d72dcf 100644 --- a/Neos.Flow/Classes/Reflection/ReflectionService.php +++ b/Neos.Flow/Classes/Reflection/ReflectionService.php @@ -37,20 +37,15 @@ * service also builds up class schema information which is used by the Flow's * persistence layer. * - * Reflection of classes of all active packages is triggered through the bootstrap's - * initializeReflectionService() method. In a development context, single classes - * may be re-reflected once files are modified whereas in a production context - * reflection is done once and successive requests read from the frozen caches for - * performance reasons. + * The list of available classes from flow packages is determined during initialisation of the + * CompileTimeObjectManager which also triggers the initial build of reflection data. * - * The list of available classes is determined by the CompiletimeObjectManager which - * also triggers the initial build of reflection data in this service. + * During shutdown any reflection changes that occurred are saved to the cache. * - * The invalidation of reflection cache entries is done by the CacheManager which - * in turn is triggered by signals sent by the file monitor. + * The invalidation of reflection cache entries is done by the CacheManager during development + * via flushClassCachesByChangedFiles by removing the reflected data from the cache. * - * The internal representation of cache data is optimized for memory consumption and - * speed by using constants which have an integer value. + * The internal representation of cache data is optimized for memory consumption. * * @api * @Flow\Scope("singleton") @@ -284,7 +279,7 @@ public function getDefaultImplementationClassNameForInterface(string $interfaceN } $interfaceName = $this->prepareClassReflectionForUsage($interfaceName); - $classNamesFound = isset($this->classReflectionData[$interfaceName][self::DATA_INTERFACE_IMPLEMENTATIONS]) ? array_keys($this->classReflectionData[$interfaceName][self::DATA_INTERFACE_IMPLEMENTATIONS]) : []; + $classNamesFound = array_keys($this->classReflectionData[$interfaceName][self::DATA_INTERFACE_IMPLEMENTATIONS] ?? []); if (count($classNamesFound) === 1) { return $classNamesFound[0]; } @@ -320,7 +315,7 @@ public function getAllImplementationClassNamesForInterface(string $interfaceName } $interfaceName = $this->prepareClassReflectionForUsage($interfaceName); - return (isset($this->classReflectionData[$interfaceName][self::DATA_INTERFACE_IMPLEMENTATIONS])) ? array_keys($this->classReflectionData[$interfaceName][self::DATA_INTERFACE_IMPLEMENTATIONS]) : []; + return array_keys($this->classReflectionData[$interfaceName][self::DATA_INTERFACE_IMPLEMENTATIONS] ?? []); } /** @@ -340,7 +335,7 @@ public function getAllSubClassNamesForClass(string $className): array throw new \InvalidArgumentException('"' . $className . '" does not exist or is not the name of a class.', 1257168042); } $className = $this->prepareClassReflectionForUsage($className); - return (isset($this->classReflectionData[$className][self::DATA_CLASS_SUBCLASSES])) ? array_keys($this->classReflectionData[$className][self::DATA_CLASS_SUBCLASSES]) : []; + return array_keys($this->classReflectionData[$className][self::DATA_CLASS_SUBCLASSES] ?? []); } /** @@ -354,7 +349,7 @@ public function getClassNamesByAnnotation(string $annotationClassName): array } $annotationClassName = $this->cleanClassName($annotationClassName); - return (isset($this->annotatedClasses[$annotationClassName]) ? array_keys($this->annotatedClasses[$annotationClassName]) : []); + return array_keys($this->annotatedClasses[$annotationClassName] ?? []); } /** @@ -371,7 +366,7 @@ public function isClassAnnotatedWith(string $className, string $annotationClassN $annotationClassName = $this->cleanClassName($annotationClassName); - return (isset($this->annotatedClasses[$annotationClassName][$className])); + return isset($this->annotatedClasses[$annotationClassName][$className]); } /** @@ -440,11 +435,9 @@ public function getClassAnnotation(string $className, string $annotationClassNam public function isClassImplementationOf(string $className, string $interfaceName): bool { $className = $this->prepareClassReflectionForUsage($className); + $this->prepareClassReflectionForUsage($interfaceName); - $interfaceName = $this->cleanClassName($interfaceName); - $this->loadOrReflectClassIfNecessary($interfaceName); - - return (isset($this->classReflectionData[$interfaceName][self::DATA_INTERFACE_IMPLEMENTATIONS][$className])); + return isset($this->classReflectionData[$interfaceName][self::DATA_INTERFACE_IMPLEMENTATIONS][$className]); } /** @@ -517,7 +510,7 @@ public function getClassesContainingMethodsAnnotatedWith(string $annotationClass $this->initialize(); } - return isset($this->classesByMethodAnnotations[$annotationClassName]) ? array_keys($this->classesByMethodAnnotations[$annotationClassName]) : []; + return array_keys($this->classesByMethodAnnotations[$annotationClassName] ?? []); } /** @@ -575,7 +568,7 @@ public function isMethodStatic(string $className, string $methodName): bool public function isMethodPublic(string $className, string $methodName): bool { $className = $this->prepareClassReflectionForUsage($className); - return (isset($this->classReflectionData[$className][self::DATA_CLASS_METHODS][$methodName][self::DATA_METHOD_VISIBILITY]) && $this->classReflectionData[$className][self::DATA_CLASS_METHODS][$methodName][self::DATA_METHOD_VISIBILITY] === self::VISIBILITY_PUBLIC); + return ($this->classReflectionData[$className][self::DATA_CLASS_METHODS][$methodName][self::DATA_METHOD_VISIBILITY] ?? null) === self::VISIBILITY_PUBLIC; } /** @@ -588,7 +581,7 @@ public function isMethodPublic(string $className, string $methodName): bool public function isMethodProtected(string $className, string $methodName): bool { $className = $this->prepareClassReflectionForUsage($className); - return (isset($this->classReflectionData[$className][self::DATA_CLASS_METHODS][$methodName][self::DATA_METHOD_VISIBILITY]) && $this->classReflectionData[$className][self::DATA_CLASS_METHODS][$methodName][self::DATA_METHOD_VISIBILITY] === self::VISIBILITY_PROTECTED); + return ($this->classReflectionData[$className][self::DATA_CLASS_METHODS][$methodName][self::DATA_METHOD_VISIBILITY] ?? null) === self::VISIBILITY_PROTECTED; } /** @@ -601,7 +594,7 @@ public function isMethodProtected(string $className, string $methodName): bool public function isMethodPrivate(string $className, string $methodName): bool { $className = $this->prepareClassReflectionForUsage($className); - return (isset($this->classReflectionData[$className][self::DATA_CLASS_METHODS][$methodName][self::DATA_METHOD_VISIBILITY]) && $this->classReflectionData[$className][self::DATA_CLASS_METHODS][$methodName][self::DATA_METHOD_VISIBILITY] === self::VISIBILITY_PRIVATE); + return ($this->classReflectionData[$className][self::DATA_CLASS_METHODS][$methodName][self::DATA_METHOD_VISIBILITY] ?? null) === self::VISIBILITY_PRIVATE; } /** @@ -723,7 +716,7 @@ public function getMethodAnnotation(string $className, string $methodName, strin public function getClassPropertyNames(string $className): array { $className = $this->prepareClassReflectionForUsage($className); - return isset($this->classReflectionData[$className][self::DATA_CLASS_PROPERTIES]) ? array_keys($this->classReflectionData[$className][self::DATA_CLASS_PROPERTIES]) : []; + return array_keys($this->classReflectionData[$className][self::DATA_CLASS_PROPERTIES] ?? []); } /** @@ -808,12 +801,10 @@ public function getMethodDeclaredReturnType(string $className, string $methodNam public function getPropertyNamesByTag(string $className, string $tag): array { $className = $this->prepareClassReflectionForUsage($className); - if (!isset($this->classReflectionData[$className][self::DATA_CLASS_PROPERTIES])) { - return []; - } $propertyNames = []; - foreach ($this->classReflectionData[$className][self::DATA_CLASS_PROPERTIES] as $propertyName => $propertyData) { + $classProperties = $this->classReflectionData[$className][self::DATA_CLASS_PROPERTIES] ?? []; + foreach ($classProperties as $propertyName => $propertyData) { if (isset($propertyData[self::DATA_PROPERTY_TAGS_VALUES][$tag])) { $propertyNames[$propertyName] = true; } @@ -836,10 +827,6 @@ public function getPropertyNamesByTag(string $className, string $tag): array public function getPropertyTagsValues(string $className, string $propertyName): array { $className = $this->prepareClassReflectionForUsage($className); - if (!isset($this->classReflectionData[$className][self::DATA_CLASS_PROPERTIES][$propertyName])) { - return []; - } - return $this->classReflectionData[$className][self::DATA_CLASS_PROPERTIES][$propertyName][self::DATA_PROPERTY_TAGS_VALUES] ?? []; } @@ -859,10 +846,6 @@ public function getPropertyTagsValues(string $className, string $propertyName): public function getPropertyTagValues(string $className, string $propertyName, string $tag): array { $className = $this->prepareClassReflectionForUsage($className); - if (!isset($this->classReflectionData[$className][self::DATA_CLASS_PROPERTIES][$propertyName])) { - return []; - } - return $this->classReflectionData[$className][self::DATA_CLASS_PROPERTIES][$propertyName][self::DATA_PROPERTY_TAGS_VALUES][$tag] ?? []; } @@ -894,8 +877,7 @@ public function getPropertyType(string $className, string $propertyName): ?strin public function isPropertyPrivate(string $className, string $propertyName): bool { $className = $this->prepareClassReflectionForUsage($className); - return (isset($this->classReflectionData[$className][self::DATA_CLASS_PROPERTIES][$propertyName][self::DATA_PROPERTY_VISIBILITY]) - && $this->classReflectionData[$className][self::DATA_CLASS_PROPERTIES][$propertyName][self::DATA_PROPERTY_VISIBILITY] === self::VISIBILITY_PRIVATE); + return ($this->classReflectionData[$className][self::DATA_CLASS_PROPERTIES][$propertyName][self::DATA_PROPERTY_VISIBILITY] ?? null) === self::VISIBILITY_PRIVATE; } /** @@ -949,12 +931,10 @@ public function isPropertyAnnotatedWith(string $className, string $propertyName, public function getPropertyNamesByAnnotation(string $className, string $annotationClassName): array { $className = $this->prepareClassReflectionForUsage($className); - if (!isset($this->classReflectionData[$className][self::DATA_CLASS_PROPERTIES])) { - return []; - } $propertyNames = []; - foreach ($this->classReflectionData[$className][self::DATA_CLASS_PROPERTIES] as $propertyName => $propertyData) { + $classProperties = $this->classReflectionData[$className][self::DATA_CLASS_PROPERTIES] ?? []; + foreach ($classProperties as $propertyName => $propertyData) { if (isset($propertyData[self::DATA_PROPERTY_ANNOTATIONS][$annotationClassName])) { $propertyNames[$propertyName] = true; } @@ -1073,11 +1053,8 @@ protected function prepareClassReflectionForUsage(string $className): string */ protected function reflectEmergedClasses(): void { - $availableClassnames = []; - foreach ($this->availableClassNames as $classNamesInPackage) { - $availableClassnames[] = $classNamesInPackage; - } - $classNamesToReflect = array_merge([], ...$availableClassnames); + // flatten nested array structure to a list of classes + $classNamesToReflect = array_merge(...array_values($this->availableClassNames)); $reflectedClassNames = array_keys($this->classReflectionData); sort($classNamesToReflect); sort($reflectedClassNames); @@ -1087,9 +1064,9 @@ protected function reflectEmergedClasses(): void } $this->log('Reflected class names did not match class names to reflect', LogLevel::DEBUG); - $count = 0; - $classNameFilterFunction = function ($className) use (&$count): bool { + $classNamesToBuildSchemaFor = []; + foreach ($newClassNames as $className) { $this->loadOrReflectClassIfNecessary($className); if ( !$this->isClassAnnotatedWith($className, Flow\Entity::class) && @@ -1097,7 +1074,7 @@ protected function reflectEmergedClasses(): void !$this->isClassAnnotatedWith($className, ORM\Embeddable::class) && !$this->isClassAnnotatedWith($className, Flow\ValueObject::class) ) { - return false; + continue; } $scopeAnnotation = $this->getClassAnnotation($className, Flow\Scope::class); @@ -1105,15 +1082,13 @@ protected function reflectEmergedClasses(): void throw new Exception(sprintf('Classes tagged as entity or value object must be of scope prototype, however, %s is declared as %s.', $className, $scopeAnnotation->value), 1264103349); } - $count++; - return true; + $classNamesToBuildSchemaFor[] = $className; }; - $classNamesToBuildSchemaFor = array_filter($newClassNames, $classNameFilterFunction); $this->buildClassSchemata($classNamesToBuildSchemaFor); - if ($count > 0) { - $this->log(sprintf('Reflected %s emerged classes.', $count), LogLevel::INFO, LogEnvironment::fromMethodName(__METHOD__)); + if ($classNamesToBuildSchemaFor !== []) { + $this->log(sprintf('Reflected %s emerged classes.', count($classNamesToBuildSchemaFor)), LogLevel::INFO, LogEnvironment::fromMethodName(__METHOD__)); } } @@ -1822,8 +1797,7 @@ protected function convertParameterReflectionToArray(ParameterReflection $parame */ protected function forgetChangedClasses(): void { - $classNames = array_keys($this->classReflectionData); - foreach ($classNames as $className) { + foreach ($this->classReflectionData as $className => $_) { if (is_string($className) && !$this->reflectionDataRuntimeCache->has($this->produceCacheIdentifierFromClassName($className))) { $this->forgetClass($className); } @@ -1868,12 +1842,12 @@ private function forgetClass(string $className): void } if (isset($this->classReflectionData[$className][self::DATA_CLASS_SUBCLASSES])) { - foreach (array_keys($this->classReflectionData[$className][self::DATA_CLASS_SUBCLASSES]) as $subClassName) { + foreach ($this->classReflectionData[$className][self::DATA_CLASS_SUBCLASSES] as $subClassName => $_) { $this->forgetClass((string)$subClassName); } } - foreach (array_keys($this->annotatedClasses) as $annotationClassName) { + foreach ($this->annotatedClasses as $annotationClassName => $_) { if (isset($this->annotatedClasses[$annotationClassName][$className])) { unset($this->annotatedClasses[$annotationClassName][$className]); } @@ -1884,7 +1858,7 @@ private function forgetClass(string $className): void unset($this->classSchemata[$className]); } - foreach (array_keys($this->classesByMethodAnnotations) as $annotationClassName) { + foreach ($this->classesByMethodAnnotations as $annotationClassName => $_) { unset($this->classesByMethodAnnotations[$annotationClassName][$className]); } @@ -1894,13 +1868,6 @@ private function forgetClass(string $className): void /** * Loads reflection data from the cache or reflects the class if needed. * - * If the class is completely unknown, this method won't try to load or reflect - * it. If it is known and reflection data has been loaded already, it won't be - * loaded again. - * - * In Production context, with frozen caches, this method will load reflection - * data for the specified class from the runtime cache. - * * @param class-string $className * @throws ClassLoadingForReflectionFailedException * @throws InvalidClassException @@ -1931,7 +1898,7 @@ private function loadOrReflectClassIfNecessary(string $className): void */ public function saveToCache(): void { - if (empty($this->updatedReflectionData)) { + if ($this->updatedReflectionData === []) { return; } @@ -1939,16 +1906,6 @@ public function saveToCache(): void $this->initialize(); } - $this->updateCacheEntries(); - } - - /** - * Save reflection data to cache in Production context. - * - * @throws Exception - */ - private function updateCacheEntries(): void - { $classNames = []; foreach ($this->classReflectionData as $className => $reflectionData) { if ($this->isClassReflected($className)) { @@ -1956,7 +1913,7 @@ private function updateCacheEntries(): void } } - foreach (array_keys($this->updatedReflectionData) as $className) { + foreach ($this->updatedReflectionData as $className => $_) { $reflectionData = $this->classReflectionData[$className]; $cacheIdentifier = $this->produceCacheIdentifierFromClassName($className); $this->reflectionDataRuntimeCache->set($cacheIdentifier, $reflectionData);