From 1bdf1656d371e1d8d0c7d293113afef637441d5f Mon Sep 17 00:00:00 2001 From: Jenkins Date: Tue, 28 Jan 2025 11:19:52 +0000 Subject: [PATCH 1/9] TASK: Update references [skip ci] --- .../TheDefinitiveGuide/PartV/AnnotationReference.rst | 2 +- .../Documentation/TheDefinitiveGuide/PartV/CommandReference.rst | 2 +- .../PartV/FluidAdaptorViewHelperReference.rst | 2 +- .../Documentation/TheDefinitiveGuide/PartV/SignalsReference.rst | 2 +- .../TheDefinitiveGuide/PartV/TYPO3FluidViewHelperReference.rst | 2 +- .../TheDefinitiveGuide/PartV/TypeConverterReference.rst | 2 +- .../TheDefinitiveGuide/PartV/ValidatorReference.rst | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Neos.Flow/Documentation/TheDefinitiveGuide/PartV/AnnotationReference.rst b/Neos.Flow/Documentation/TheDefinitiveGuide/PartV/AnnotationReference.rst index eb0e9407c3..4ae5f0c7a9 100644 --- a/Neos.Flow/Documentation/TheDefinitiveGuide/PartV/AnnotationReference.rst +++ b/Neos.Flow/Documentation/TheDefinitiveGuide/PartV/AnnotationReference.rst @@ -3,7 +3,7 @@ Flow Annotation Reference ========================= -This reference was automatically generated from code on 2025-01-27 +This reference was automatically generated from code on 2025-01-28 .. _`Flow Annotation Reference: After`: diff --git a/Neos.Flow/Documentation/TheDefinitiveGuide/PartV/CommandReference.rst b/Neos.Flow/Documentation/TheDefinitiveGuide/PartV/CommandReference.rst index 841cc56ff9..4c6d620d07 100644 --- a/Neos.Flow/Documentation/TheDefinitiveGuide/PartV/CommandReference.rst +++ b/Neos.Flow/Documentation/TheDefinitiveGuide/PartV/CommandReference.rst @@ -19,7 +19,7 @@ commands that may be available, use:: ./flow help -The following reference was automatically generated from code on 2025-01-27 +The following reference was automatically generated from code on 2025-01-28 .. _`Flow Command Reference: NEOS.FLOW`: diff --git a/Neos.Flow/Documentation/TheDefinitiveGuide/PartV/FluidAdaptorViewHelperReference.rst b/Neos.Flow/Documentation/TheDefinitiveGuide/PartV/FluidAdaptorViewHelperReference.rst index 66ea3f656a..ead0385d8e 100644 --- a/Neos.Flow/Documentation/TheDefinitiveGuide/PartV/FluidAdaptorViewHelperReference.rst +++ b/Neos.Flow/Documentation/TheDefinitiveGuide/PartV/FluidAdaptorViewHelperReference.rst @@ -3,7 +3,7 @@ FluidAdaptor ViewHelper Reference ================================= -This reference was automatically generated from code on 2025-01-27 +This reference was automatically generated from code on 2025-01-28 .. _`FluidAdaptor ViewHelper Reference: f:debug`: diff --git a/Neos.Flow/Documentation/TheDefinitiveGuide/PartV/SignalsReference.rst b/Neos.Flow/Documentation/TheDefinitiveGuide/PartV/SignalsReference.rst index 893b9a9c7c..fd6bbc9c8f 100644 --- a/Neos.Flow/Documentation/TheDefinitiveGuide/PartV/SignalsReference.rst +++ b/Neos.Flow/Documentation/TheDefinitiveGuide/PartV/SignalsReference.rst @@ -3,7 +3,7 @@ Flow Signals Reference ====================== -This reference was automatically generated from code on 2025-01-27 +This reference was automatically generated from code on 2025-01-28 .. _`Flow Signals Reference: AbstractAdvice (``Neos\Flow\Aop\Advice\AbstractAdvice``)`: diff --git a/Neos.Flow/Documentation/TheDefinitiveGuide/PartV/TYPO3FluidViewHelperReference.rst b/Neos.Flow/Documentation/TheDefinitiveGuide/PartV/TYPO3FluidViewHelperReference.rst index a50a159095..dc21dcfe28 100644 --- a/Neos.Flow/Documentation/TheDefinitiveGuide/PartV/TYPO3FluidViewHelperReference.rst +++ b/Neos.Flow/Documentation/TheDefinitiveGuide/PartV/TYPO3FluidViewHelperReference.rst @@ -3,7 +3,7 @@ TYPO3 Fluid ViewHelper Reference ================================ -This reference was automatically generated from code on 2025-01-27 +This reference was automatically generated from code on 2025-01-28 .. _`TYPO3 Fluid ViewHelper Reference: f:alias`: diff --git a/Neos.Flow/Documentation/TheDefinitiveGuide/PartV/TypeConverterReference.rst b/Neos.Flow/Documentation/TheDefinitiveGuide/PartV/TypeConverterReference.rst index 3957a2b8f2..b8cff2d431 100644 --- a/Neos.Flow/Documentation/TheDefinitiveGuide/PartV/TypeConverterReference.rst +++ b/Neos.Flow/Documentation/TheDefinitiveGuide/PartV/TypeConverterReference.rst @@ -3,7 +3,7 @@ Flow TypeConverter Reference ============================ -This reference was automatically generated from code on 2025-01-27 +This reference was automatically generated from code on 2025-01-28 .. _`Flow TypeConverter Reference: ArrayConverter`: diff --git a/Neos.Flow/Documentation/TheDefinitiveGuide/PartV/ValidatorReference.rst b/Neos.Flow/Documentation/TheDefinitiveGuide/PartV/ValidatorReference.rst index dc0d400af1..5231d7b31c 100644 --- a/Neos.Flow/Documentation/TheDefinitiveGuide/PartV/ValidatorReference.rst +++ b/Neos.Flow/Documentation/TheDefinitiveGuide/PartV/ValidatorReference.rst @@ -3,7 +3,7 @@ Flow Validator Reference ======================== -This reference was automatically generated from code on 2025-01-27 +This reference was automatically generated from code on 2025-01-28 .. _`Flow Validator Reference: AggregateBoundaryValidator`: From 46045a84781f5521d93acf528d43655dadf4c896 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Mu=CC=88ller?= Date: Thu, 6 Feb 2025 17:00:15 +0100 Subject: [PATCH 2/9] !!!TASK: Reduce complexity of ReflectionService This change tackles some problems within the reflection service that stem from historically increasing complexity due to various caching mechanisms depending on application context and compile time status. The aim was to cut down on this complexity, while ensuring that all existing use-cases continue working as intended. This ultimately also fixes issue #3402 by providing the same reflection data across all possible contexts. A few features and caches got deprecated with this change and could be breaking in the rare case you used the freeze package api in your code: The entire concept of freezing a package is deprecated What remains are the commands in the package controller, which are now all no-ops and deprecated to be removed with 9.0. This is to ensure deployment pipelines possibly calling freeze commands do not break with the 8.4 update. Additionally the single method `PackageManager::isPackageFrozen` remains, while the rest was removed. None of the methods was ever api and it seems unlikely that someone used them in user-land code. `isPackageFrozen` however is at the very least used in Framework and Neos code and therefore remains until 9.0, but will now return false for every package. Caches deprecated and unused With the simplification two caches are no longer needed, both are still declared so that possibly existing cache configuration in user projects doesn't error, but both `Flow_Reflection_Status` and `Flow_Reflection_CompiletimeData` will no longer be used and any content can be removed. The only reflection cache is now `Flow_Reflection_RuntimeData`, which makes the name somewhat deceptive as it is also used in compile time. To avoid backwards compatibility issues however it makes sense to keep the name for the foreseeable future. Quick performance comparisons suggest that especially the initial compile from empty cache benefits from this change. Reflection updates in Development context afterwards seem to be on par with the existing code base. --- .../Classes/Aop/Builder/ProxyClassBuilder.php | 4 +- Neos.Flow/Classes/Cache/CacheManager.php | 5 +- .../Command/CacheCommandController.php | 20 - .../Command/PackageCommandController.php | 167 +------ Neos.Flow/Classes/Core/Booting/Scripts.php | 6 +- Neos.Flow/Classes/Package.php | 6 +- Neos.Flow/Classes/Package/PackageManager.php | 86 +--- .../Classes/Reflection/ReflectionService.php | 419 +++--------------- .../Reflection/ReflectionServiceFactory.php | 6 - Neos.Flow/Configuration/Caches.yaml | 13 +- .../Reflection/ReflectionServiceTest.php | 1 + .../Tests/Unit/Cache/CacheManagerTest.php | 4 +- .../Tests/Unit/Package/PackageManagerTest.php | 32 -- Neos.FluidAdaptor/Classes/Package.php | 4 - 14 files changed, 103 insertions(+), 670 deletions(-) diff --git a/Neos.Flow/Classes/Aop/Builder/ProxyClassBuilder.php b/Neos.Flow/Classes/Aop/Builder/ProxyClassBuilder.php index b1052792f7..a175e0cf8d 100644 --- a/Neos.Flow/Classes/Aop/Builder/ProxyClassBuilder.php +++ b/Neos.Flow/Classes/Aop/Builder/ProxyClassBuilder.php @@ -441,10 +441,8 @@ public function buildProxyClass(string $targetClassName, array $aspectContainers * @param ClassNameIndex $targetClassNameCandidates target class names for advices * @param ClassNameIndex $treatedSubClasses Already treated (sub) classes to avoid duplication * @return ClassNameIndex The new collection of already treated classes - * @throws ClassLoadingForReflectionFailedException - * @throws \ReflectionException - * @throws InvalidClassException * @throws CannotBuildObjectException + * @throws \ReflectionException */ protected function proxySubClassesOfClassToEnsureAdvices(string $className, ClassNameIndex $targetClassNameCandidates, ClassNameIndex $treatedSubClasses): ClassNameIndex { diff --git a/Neos.Flow/Classes/Cache/CacheManager.php b/Neos.Flow/Classes/Cache/CacheManager.php index ede879875d..a1108b1353 100644 --- a/Neos.Flow/Classes/Cache/CacheManager.php +++ b/Neos.Flow/Classes/Cache/CacheManager.php @@ -374,9 +374,10 @@ protected function flushClassCachesByChangedFiles(array $changedFiles): void $flushDoctrineProxyCache = false; $flushPolicyCache = false; if (count($modifiedClassNamesWithUnderscores) > 0) { - $reflectionStatusCache = $this->getCache('Flow_Reflection_Status'); + $reflectionDataRuntimeCache = $this->getCache('Flow_Reflection_RuntimeData'); foreach (array_keys($modifiedClassNamesWithUnderscores) as $classNameWithUnderscores) { - $reflectionStatusCache->remove($classNameWithUnderscores); + $this->logger->debug('File change detected, removing reflection for ' . $classNameWithUnderscores); + $reflectionDataRuntimeCache->remove($classNameWithUnderscores); if ($flushDoctrineProxyCache === false && preg_match('/_Domain_Model_(.+)/', $classNameWithUnderscores) === 1) { $flushDoctrineProxyCache = true; } diff --git a/Neos.Flow/Classes/Command/CacheCommandController.php b/Neos.Flow/Classes/Command/CacheCommandController.php index 9782466b81..b04fb1b520 100644 --- a/Neos.Flow/Classes/Command/CacheCommandController.php +++ b/Neos.Flow/Classes/Command/CacheCommandController.php @@ -135,9 +135,6 @@ public function injectEnvironment(Environment $environment) * from running, the removal of any temporary data can be forced by specifying * the option --force. * - * This command does not remove the precompiled data provided by frozen - * packages unless the --force option is used. - * * @param boolean $force Force flushing of any temporary data * @return void * @see neos.flow:cache:warmup @@ -157,23 +154,6 @@ public function flushCommand(bool $force = false) $this->lockManager->unlockSite(); } - $frozenPackages = []; - foreach (array_keys($this->packageManager->getAvailablePackages()) as $packageKey) { - if ($this->packageManager->isPackageFrozen($packageKey)) { - $frozenPackages[] = $packageKey; - } - } - if ($frozenPackages !== []) { - $this->outputFormatted(PHP_EOL . 'Please note that the following package' . (count($frozenPackages) === 1 ? ' is' : 's are') . ' currently frozen: ' . PHP_EOL); - $this->outputFormatted(implode(PHP_EOL, $frozenPackages) . PHP_EOL, [], 2); - - $message = 'As code and configuration changes in these packages are not detected, the application may respond '; - $message .= 'unexpectedly if modifications were done anyway or the remaining code relies on these changes.' . PHP_EOL . PHP_EOL; - $message .= 'You may call package:refreeze all in order to refresh frozen packages or use the --force '; - $message .= 'option of this cache:flush command to flush caches if Flow becomes unresponsive.' . PHP_EOL; - $this->outputFormatted($message, [$frozenPackages]); - } - $this->sendAndExit(0); } diff --git a/Neos.Flow/Classes/Command/PackageCommandController.php b/Neos.Flow/Classes/Command/PackageCommandController.php index 25e6616f5a..79649b5d0c 100644 --- a/Neos.Flow/Classes/Command/PackageCommandController.php +++ b/Neos.Flow/Classes/Command/PackageCommandController.php @@ -14,8 +14,6 @@ use Neos\Flow\Annotations as Flow; use Neos\Flow\Cli\CommandController; use Neos\Flow\Composer\ComposerUtility; -use Neos\Flow\Core\Booting\Scripts; -use Neos\Flow\Core\Bootstrap; use Neos\Flow\Package\PackageInterface; use Neos\Flow\Package\PackageKeyAwareInterface; use Neos\Flow\Package\PackageManager; @@ -32,25 +30,6 @@ class PackageCommandController extends CommandController */ protected $packageManager; - /** - * @var array - */ - protected $settings; - - /** - * @var Bootstrap - */ - protected $bootstrap; - - /** - * @param array $settings The Flow settings - * @return void - */ - public function injectSettings(array $settings) - { - $this->settings = $settings; - } - /** * @param PackageManager $packageManager * @return void @@ -60,15 +39,6 @@ public function injectPackageManager(PackageManager $packageManager) $this->packageManager = $packageManager; } - /** - * @param Bootstrap $bootstrap - * @return void - */ - public function injectBootstrap(Bootstrap $bootstrap) - { - $this->bootstrap = $bootstrap; - } - /** * Create a new package * @@ -112,9 +82,7 @@ public function createCommand(string $packageKey, string $packageType = PackageI public function listCommand(bool $loadingOrder = false) { $availablePackages = []; - $frozenPackages = []; $longestPackageKey = 0; - $freezeSupported = $this->bootstrap->getContext()->isDevelopment(); foreach ($this->packageManager->getAvailablePackages() as $packageKey => $package) { if (strlen($packageKey) > $longestPackageKey) { @@ -122,10 +90,6 @@ public function listCommand(bool $loadingOrder = false) } $availablePackages[$packageKey] = $package; - - if ($this->packageManager->isPackageFrozen($packageKey)) { - $frozenPackages[$packageKey] = $package; - } } if ($loadingOrder === false) { @@ -135,13 +99,7 @@ public function listCommand(bool $loadingOrder = false) $this->outputLine('PACKAGES:'); /** @var PackageInterface|PackageKeyAwareInterface $package */ foreach ($availablePackages as $package) { - $frozenState = ($freezeSupported && isset($frozenPackages[$package->getPackageKey()]) ? '* ' : ' '); - $this->outputLine(' ' . str_pad($package->getPackageKey(), $longestPackageKey + 3) . $frozenState . str_pad($package->getInstalledVersion(), 15)); - } - - if (count($frozenPackages) > 0 && $freezeSupported) { - $this->outputLine(); - $this->outputLine(' * frozen package'); + $this->outputLine(' ' . str_pad($package->getPackageKey(), $longestPackageKey + 3) . str_pad($package->getInstalledVersion(), 15)); } } @@ -165,47 +123,11 @@ public function listCommand(bool $loadingOrder = false) * @return void * @see neos.flow:package:unfreeze * @see neos.flow:package:refreeze + * @deprecated since 8.4 */ public function freezeCommand(string $packageKey = 'all') { - if (!$this->bootstrap->getContext()->isDevelopment()) { - $this->outputLine('Package freezing is only supported in Development context.'); - $this->quit(3); - } - - $packagesToFreeze = []; - - if ($packageKey === 'all') { - foreach (array_keys($this->packageManager->getAvailablePackages()) as $packageKey) { - if (!$this->packageManager->isPackageFrozen($packageKey)) { - $packagesToFreeze[] = $packageKey; - } - } - if ($packagesToFreeze === []) { - $this->outputLine('Nothing to do, all packages were already frozen.'); - $this->quit(0); - } - } elseif ($packageKey === 'blackberry') { - $this->outputLine('http://bit.ly/freeze-blackberry'); - $this->quit(42); - } else { - if (!$this->packageManager->isPackageAvailable($packageKey)) { - $this->outputLine('Package "%s" is not available.', [$packageKey]); - $this->quit(2); - } - - if ($this->packageManager->isPackageFrozen($packageKey)) { - $this->outputLine('Package "%s" was already frozen.', [$packageKey]); - $this->quit(0); - } - - $packagesToFreeze = [$packageKey]; - } - - foreach ($packagesToFreeze as $packageKey) { - $this->packageManager->freezePackage($packageKey); - $this->outputLine('Froze package "%s".', [$packageKey]); - } + $this->outputLine('Package freezing is no longer supported, this command is deprecated and will be removed with 9.0.'); } /** @@ -222,47 +144,11 @@ public function freezeCommand(string $packageKey = 'all') * @return void * @see neos.flow:package:freeze * @see neos.flow:cache:flush + * @deprecated since 8.4 */ public function unfreezeCommand(string $packageKey = 'all') { - if (!$this->bootstrap->getContext()->isDevelopment()) { - $this->outputLine('Package freezing is only supported in Development context.'); - $this->quit(3); - } - - $packagesToUnfreeze = []; - - if ($packageKey === 'all') { - foreach (array_keys($this->packageManager->getAvailablePackages()) as $packageKey) { - if ($this->packageManager->isPackageFrozen($packageKey)) { - $packagesToUnfreeze[] = $packageKey; - } - } - if ($packagesToUnfreeze === []) { - $this->outputLine('Nothing to do, no packages were frozen.'); - $this->quit(0); - } - } else { - if ($packageKey === null) { - $this->outputLine('You must specify a package to unfreeze.'); - $this->quit(1); - } - - if (!$this->packageManager->isPackageAvailable($packageKey)) { - $this->outputLine('Package "%s" is not available.', [$packageKey]); - $this->quit(2); - } - if (!$this->packageManager->isPackageFrozen($packageKey)) { - $this->outputLine('Package "%s" was not frozen.', [$packageKey]); - $this->quit(0); - } - $packagesToUnfreeze = [$packageKey]; - } - - foreach ($packagesToUnfreeze as $packageKey) { - $this->packageManager->unfreezePackage($packageKey); - $this->outputLine('Unfroze package "%s".', [$packageKey]); - } + $this->outputLine('Package freezing is no longer supported, this command is deprecated and will be removed with 9.0.'); } /** @@ -280,50 +166,11 @@ public function unfreezeCommand(string $packageKey = 'all') * @return void * @see neos.flow:package:freeze * @see neos.flow:cache:flush + * @deprecated since 8.4 */ public function refreezeCommand(string $packageKey = 'all') { - if (!$this->bootstrap->getContext()->isDevelopment()) { - $this->outputLine('Package freezing is only supported in Development context.'); - $this->quit(3); - } - - $packagesToRefreeze = []; - - if ($packageKey === 'all') { - foreach (array_keys($this->packageManager->getAvailablePackages()) as $packageKey) { - if ($this->packageManager->isPackageFrozen($packageKey)) { - $packagesToRefreeze[] = $packageKey; - } - } - if ($packagesToRefreeze === []) { - $this->outputLine('Nothing to do, no packages were frozen.'); - $this->quit(0); - } - } else { - if ($packageKey === null) { - $this->outputLine('You must specify a package to refreeze.'); - $this->quit(1); - } - - if (!$this->packageManager->isPackageAvailable($packageKey)) { - $this->outputLine('Package "%s" is not available.', [$packageKey]); - $this->quit(2); - } - if (!$this->packageManager->isPackageFrozen($packageKey)) { - $this->outputLine('Package "%s" was not frozen.', [$packageKey]); - $this->quit(0); - } - $packagesToRefreeze = [$packageKey]; - } - - foreach ($packagesToRefreeze as $packageKey) { - $this->packageManager->refreezePackage($packageKey); - $this->outputLine('Refroze package "%s".', [$packageKey]); - } - - Scripts::executeCommand('neos.flow:cache:flush', $this->settings, false); - $this->sendAndExit(0); + $this->outputLine('Package freezing is no longer supported, this command is deprecated and will be removed with 9.0.'); } /** diff --git a/Neos.Flow/Classes/Core/Booting/Scripts.php b/Neos.Flow/Classes/Core/Booting/Scripts.php index 7614aedf75..74edfed2e0 100644 --- a/Neos.Flow/Classes/Core/Booting/Scripts.php +++ b/Neos.Flow/Classes/Core/Booting/Scripts.php @@ -247,7 +247,7 @@ public static function initializeSystemLogger(Bootstrap $bootstrap): void $throwableStorage = self::initializeExceptionStorage($bootstrap, $settings); $bootstrap->setEarlyInstance(ThrowableStorageInterface::class, $throwableStorage); - /** @var PsrLoggerFactoryInterface $psrLoggerFactoryName */ + /** @var class-string $psrLoggerFactoryName */ $psrLoggerFactoryName = $settings['log']['psr3']['loggerFactory']; $psrLogConfigurations = $settings['log']['psr3'][$psrLoggerFactoryName] ?? []; $psrLogFactory = $psrLoggerFactoryName::create($psrLogConfigurations); @@ -578,10 +578,6 @@ public static function initializeSystemFileMonitor(Bootstrap $bootstrap) /** @var FlowPackageInterface $package */ foreach ($packageManager->getFlowPackages() as $packageKey => $package) { - if ($packageManager->isPackageFrozen($packageKey)) { - continue; - } - self::monitorDirectoryIfItExists($fileMonitors['Flow_ConfigurationFiles'], $package->getConfigurationPath(), '\.y(a)?ml$'); self::monitorDirectoryIfItExists($fileMonitors['Flow_TranslationFiles'], $package->getResourcesPath() . 'Private/Translations/', '\.xlf'); diff --git a/Neos.Flow/Classes/Package.php b/Neos.Flow/Classes/Package.php index b69f7dd552..5b37920ffe 100644 --- a/Neos.Flow/Classes/Package.php +++ b/Neos.Flow/Classes/Package.php @@ -19,6 +19,7 @@ use Neos\Flow\ObjectManagement\Proxy; use Neos\Flow\Package\Package as BasePackage; use Neos\Flow\Package\PackageManager; +use Neos\Flow\Reflection\ReflectionService; use Neos\Flow\ResourceManagement\ResourceManager; use Neos\Flow\ResourceManagement\ResourceRepository; use Neos\Flow\Security\Authentication\AuthenticationProviderManager; @@ -88,10 +89,6 @@ public function boot(Core\Bootstrap $bootstrap) /** @var PackageManager $packageManager */ $packageManager = $bootstrap->getEarlyInstance(Package\PackageManager::class); foreach ($packageManager->getFlowPackages() as $packageKey => $package) { - if ($packageManager->isPackageFrozen($packageKey)) { - continue; - } - $publicResourcesPath = $package->getResourcesPath() . 'Public/'; if (is_dir($publicResourcesPath)) { $publicResourcesFileMonitor->monitorDirectory($publicResourcesPath); @@ -122,6 +119,7 @@ public function boot(Core\Bootstrap $bootstrap) $dispatcher->connect(Core\Bootstrap::class, 'bootstrapShuttingDown', ObjectManagement\ObjectManagerInterface::class, 'shutdown'); $dispatcher->connect(Core\Bootstrap::class, 'bootstrapShuttingDown', Configuration\ConfigurationManager::class, 'shutdown'); + /** @see ReflectionService::saveToCache() */ $dispatcher->connect(Core\Bootstrap::class, 'bootstrapShuttingDown', Reflection\ReflectionService::class, 'saveToCache'); $dispatcher->connect(Command\CoreCommandController::class, 'finishedCompilationRun', Security\Authorization\Privilege\Method\MethodPrivilegePointcutFilter::class, 'savePolicyCache'); diff --git a/Neos.Flow/Classes/Package/PackageManager.php b/Neos.Flow/Classes/Package/PackageManager.php index e59f351efc..c3de0490fb 100644 --- a/Neos.Flow/Classes/Package/PackageManager.php +++ b/Neos.Flow/Classes/Package/PackageManager.php @@ -15,7 +15,6 @@ use Neos\Flow\Composer\Exception\InvalidConfigurationException; use Neos\Flow\Composer\ComposerUtility; use Neos\Flow\Core\Bootstrap; -use Neos\Flow\Reflection\ReflectionService; use Neos\Flow\SignalSlot\Dispatcher; use Neos\Flow\SignalSlot\Exception\InvalidSlotException; use Neos\Utility\Exception\FilesException; @@ -229,6 +228,7 @@ public function getAvailablePackages(): array * precompiled reflection data in order to improve performance. * * @return array + * @deprecated since 8.4 */ public function getFrozenPackages(): array { @@ -403,6 +403,7 @@ public function createPackage($packageKey, array $manifest = [], $packagesPath = * @param string $toAbsolutePath * @return void * @throws FilesException + * @deprecated since 8.4 */ protected function movePackage($fromAbsolutePath, $toAbsolutePath): void { @@ -411,95 +412,16 @@ protected function movePackage($fromAbsolutePath, $toAbsolutePath): void Files::removeDirectoryRecursively($fromAbsolutePath); } - /** - * Freezes a package - * - * @param string $packageKey The package to freeze - * @return void - * @throws Exception\PackageStatesFileNotWritableException - * @throws Exception\UnknownPackageException - * @throws \Neos\Flow\Exception - * @throws FilesException - */ - public function freezePackage($packageKey): void - { - if (!$this->bootstrap->getContext()->isDevelopment()) { - throw new \LogicException('Package freezing is only supported in Development context.', 1338810870); - } - - if (!$this->isPackageAvailable($packageKey)) { - throw new Exception\UnknownPackageException('Package "' . $packageKey . '" is not available.', 1331715956); - } - if ($this->isPackageFrozen($packageKey)) { - return; - } - - $package = $this->packages[$packageKey]; - $this->bootstrap->getObjectManager()->get(ReflectionService::class)->freezePackageReflection($packageKey); - - $this->packageStatesConfiguration['packages'][$package->getComposerName()]['frozen'] = true; - $this->savePackageStates($this->packageStatesConfiguration); - } - /** * Tells if a package is frozen * * @param string $packageKey The package to check * @return boolean + * @deprecated since 8.4 */ public function isPackageFrozen($packageKey): bool { - if (!isset($this->packages[$packageKey])) { - return false; - } - $composerName = $this->packages[$packageKey]->getComposerName(); - - return ( - $this->bootstrap->getContext()->isDevelopment() - && isset($this->packageStatesConfiguration['packages'][$composerName]['frozen']) - && $this->packageStatesConfiguration['packages'][$composerName]['frozen'] === true - ); - } - - /** - * Unfreezes a package - * - * @param string $packageKey The package to unfreeze - * @return void - * @throws Exception\PackageStatesFileNotWritableException - * @throws \Neos\Flow\Exception - * @throws FilesException - */ - public function unfreezePackage($packageKey): void - { - if (!$this->isPackageFrozen($packageKey)) { - return; - } - if (!isset($this->packages[$packageKey])) { - return; - } - $composerName = $this->packages[$packageKey]->getComposerName(); - - $this->bootstrap->getObjectManager()->get(ReflectionService::class)->unfreezePackageReflection($packageKey); - - unset($this->packageStatesConfiguration['packages'][$composerName]['frozen']); - $this->savePackageStates($this->packageStatesConfiguration); - } - - /** - * Refreezes a package - * - * @param string $packageKey The package to refreeze - * @return void - * @throws \Neos\Flow\Exception - */ - public function refreezePackage($packageKey): void - { - if (!$this->isPackageFrozen($packageKey)) { - return; - } - - $this->bootstrap->getObjectManager()->get(ReflectionService::class)->unfreezePackageReflection($packageKey); + return false; } /** diff --git a/Neos.Flow/Classes/Reflection/ReflectionService.php b/Neos.Flow/Classes/Reflection/ReflectionService.php index 9119bf1143..e3d88610f0 100644 --- a/Neos.Flow/Classes/Reflection/ReflectionService.php +++ b/Neos.Flow/Classes/Reflection/ReflectionService.php @@ -18,24 +18,17 @@ use Doctrine\ORM\Mapping as ORM; use Doctrine\Persistence\Proxy as DoctrineProxy; use Neos\Cache\Exception; -use Neos\Cache\Frontend\StringFrontend; use Neos\Cache\Frontend\VariableFrontend; use Neos\Flow\Annotations as Flow; -use Neos\Flow\Core\ApplicationContext; use Neos\Flow\Log\Utility\LogEnvironment; use Neos\Flow\ObjectManagement\Proxy\ProxyInterface; -use Neos\Flow\Package; -use Neos\Flow\Package\PackageManager; use Neos\Flow\Persistence\RepositoryInterface; use Neos\Flow\Reflection\Exception\ClassLoadingForReflectionFailedException; use Neos\Flow\Reflection\Exception\ClassSchemaConstraintViolationException; use Neos\Flow\Reflection\Exception\InvalidClassException; use Neos\Flow\Reflection\Exception\InvalidPropertyTypeException; use Neos\Flow\Reflection\Exception\InvalidValueObjectException; -use Neos\Flow\Utility\Environment; -use Neos\Utility\Arrays; use Neos\Utility\Exception\FilesException; -use Neos\Utility\Files; use Neos\Utility\TypeHandling; use Psr\Log\LoggerInterface; use Psr\Log\LogLevel; @@ -74,15 +67,9 @@ class ReflectionService // Implementations of an interface protected const DATA_INTERFACE_IMPLEMENTATIONS = 1; - // Implemented interfaces of a class - protected const DATA_CLASS_INTERFACES = 2; - // Subclasses of a class protected const DATA_CLASS_SUBCLASSES = 3; - // Class tag values - protected const DATA_CLASS_TAGS_VALUES = 4; - // Class annotations protected const DATA_CLASS_ANNOTATIONS = 5; protected const DATA_CLASS_ABSTRACT = 6; @@ -111,14 +98,9 @@ class ReflectionService protected Reader $annotationReader; protected array $availableClassNames = []; - protected StringFrontend $statusCache; - protected VariableFrontend $reflectionDataCompiletimeCache; protected VariableFrontend $reflectionDataRuntimeCache; protected VariableFrontend $classSchemataRuntimeCache; protected ?LoggerInterface $logger = null; - protected PackageManager $packageManager; - protected Environment $environment; - protected ApplicationContext $context; /** * The doctrine PHP parser which can parse "use" statements. Is initialized @@ -133,11 +115,6 @@ class ReflectionService */ protected array $useStatementsForClassCache; - /** - * In Production context, with frozen caches, this flag will be true - */ - protected bool $loadFromClassSchemaRuntimeCache = false; - protected array $settings = []; /** @@ -179,24 +156,6 @@ class ReflectionService */ protected array $methodAnnotationsRuntimeCache = []; - /** - * Sets the status cache - * - * The cache must be set before initializing the Reflection Service - */ - public function setStatusCache(StringFrontend $cache): void - { - $this->statusCache = $cache; - $backend = $this->statusCache->getBackend(); - if (is_callable([$backend, 'initializeObject'])) { - $backend->initializeObject(); - } - } - - public function setReflectionDataCompiletimeCache(VariableFrontend $cache): void - { - $this->reflectionDataCompiletimeCache = $cache; - } public function setReflectionDataRuntimeCache(VariableFrontend $cache): void { @@ -218,16 +177,6 @@ public function injectLogger(LoggerInterface $logger): void $this->logger = $logger; } - public function injectPackageManager(PackageManager $packageManager): void - { - $this->packageManager = $packageManager; - } - - public function injectEnvironment(Environment $environment): void - { - $this->environment = $environment; - } - protected function getDoctrinePhpParser(): PhpParser { if ($this->doctrinePhpParser === null) { @@ -241,20 +190,14 @@ protected function getDoctrinePhpParser(): PhpParser * Initialize the reflection service lazily * * This method must be run only after all dependencies have been injected. - * - * @throws FilesException - * @throws \Neos\Flow\Utility\Exception */ protected function initialize(): void { - $this->context = $this->environment->getContext(); - - if ($this->hasFrozenCacheInProduction()) { - $this->classReflectionData = $this->reflectionDataRuntimeCache->get('__classNames'); + $classNames = $this->reflectionDataRuntimeCache->get('__classNames'); + if (is_array($classNames)) { + $this->classReflectionData = $classNames; $this->annotatedClasses = $this->reflectionDataRuntimeCache->get('__annotatedClasses'); - $this->loadFromClassSchemaRuntimeCache = true; - } else { - $this->loadClassReflectionCompiletimeCache(); + $this->classesByMethodAnnotations = $this->reflectionDataRuntimeCache->get('__classesByMethodAnnotations'); } $this->annotationReader = new AnnotationReader(); @@ -273,13 +216,14 @@ protected function initialize(): void * This method is called by the Compile Time Object Manager which also determines * the list of classes to consider for reflection. * + * @param array $availableClassNames * @throws ClassLoadingForReflectionFailedException * @throws ClassSchemaConstraintViolationException * @throws Exception - * @throws FilesException * @throws InvalidClassException + * @throws InvalidPropertyTypeException + * @throws InvalidValueObjectException * @throws ReflectionException - * @throws \Neos\Flow\Utility\Exception */ public function buildReflectionData(array $availableClassNames): void { @@ -295,6 +239,8 @@ public function buildReflectionData(array $availableClassNames): void * Tells if the specified class is known to this reflection service and * reflection information is available. * + * @param class-string $className + * * @api */ public function isClassReflected(string $className): bool @@ -304,7 +250,7 @@ public function isClassReflected(string $className): bool } $className = $this->cleanClassName($className); - return isset($this->classReflectionData[$className]); + return isset($this->classReflectionData[$className]) && is_array($this->classReflectionData[$className]); } /** @@ -330,15 +276,10 @@ public function getAllClassNames(): array */ public function getDefaultImplementationClassNameForInterface(string $interfaceName): string|bool { - if (!$this->initialized) { - $this->initialize(); - } - $interfaceName = $this->cleanClassName($interfaceName); - if (interface_exists($interfaceName) === false) { throw new \InvalidArgumentException('"' . $interfaceName . '" does not exist or is not the name of an interface.', 1238769559); } - $this->loadOrReflectClassIfNecessary($interfaceName); + $interfaceName = $this->prepareClassReflectionForUsage($interfaceName); $classNamesFound = isset($this->classReflectionData[$interfaceName][self::DATA_INTERFACE_IMPLEMENTATIONS]) ? array_keys($this->classReflectionData[$interfaceName][self::DATA_INTERFACE_IMPLEMENTATIONS]) : []; if (count($classNamesFound) === 1) { @@ -362,21 +303,16 @@ public function getDefaultImplementationClassNameForInterface(string $interfaceN * Searches for and returns all class names of implementations of the given object type * (interface name). If no class implementing the interface was found, an empty array is returned. * - * @throws ClassLoadingForReflectionFailedException - * @throws InvalidClassException + * @param class-string $interfaceName + * @return array * @api */ public function getAllImplementationClassNamesForInterface(string $interfaceName): array { - if (!$this->initialized) { - $this->initialize(); - } - $interfaceName = $this->cleanClassName($interfaceName); - if (interface_exists($interfaceName) === false) { throw new \InvalidArgumentException('"' . $interfaceName . '" does not exist or is not the name of an interface.', 1238769560); } - $this->loadOrReflectClassIfNecessary($interfaceName); + $interfaceName = $this->prepareClassReflectionForUsage($interfaceName); return (isset($this->classReflectionData[$interfaceName][self::DATA_INTERFACE_IMPLEMENTATIONS])) ? array_keys($this->classReflectionData[$interfaceName][self::DATA_INTERFACE_IMPLEMENTATIONS]) : []; } @@ -387,22 +323,14 @@ public function getAllImplementationClassNamesForInterface(string $interfaceName * * @psalm-param class-string $className * @psalm-return array - * @throws ClassLoadingForReflectionFailedException - * @throws InvalidClassException * @api */ public function getAllSubClassNamesForClass(string $className): array { - if (!$this->initialized) { - $this->initialize(); - } - $className = $this->cleanClassName($className); - if (class_exists($className) === false) { throw new \InvalidArgumentException('"' . $className . '" does not exist or is not the name of a class.', 1257168042); } - $this->loadOrReflectClassIfNecessary($className); - + $className = $this->prepareClassReflectionForUsage($className); return (isset($this->classReflectionData[$className][self::DATA_CLASS_SUBCLASSES])) ? array_keys($this->classReflectionData[$className][self::DATA_CLASS_SUBCLASSES]) : []; } @@ -762,7 +690,7 @@ public function hasMethod(string $className, string $methodName): bool * Returns all tags and their values the specified method is tagged with * * @throws ReflectionException - * @api + * @deprecated since 8.4 */ public function getMethodTagsValues(string $className, string $methodName): array { @@ -866,6 +794,7 @@ public function getPropertyTagValues(string $className, string $propertyName, st */ public function getPropertyType(string $className, string $propertyName): ?string { + $className = $this->prepareClassReflectionForUsage($className); return $this->classReflectionData[$className][self::DATA_CLASS_PROPERTIES][$propertyName][self::DATA_PROPERTY_TYPE] ?? null; } @@ -991,6 +920,12 @@ public function getClassSchema(string|object $classNameOrObject): ?ClassSchema /** * Initializes the ReflectionService, cleans the given class name and finally reflects the class if necessary. + * + * @param class-string $className + * @return string + * @throws ClassLoadingForReflectionFailedException + * @throws InvalidClassException + * @throws ReflectionException */ protected function prepareClassReflectionForUsage(string $className): string { @@ -1012,14 +947,17 @@ protected function prepareClassReflectionForUsage(string $className): string * @throws ClassSchemaConstraintViolationException * @throws Exception * @throws InvalidClassException + * @throws InvalidPropertyTypeException + * @throws InvalidValueObjectException * @throws ReflectionException */ protected function reflectEmergedClasses(): void { - $classNamesToReflect = []; - foreach ($this->availableClassNames as $classNamesInOnePackage) { - $classNamesToReflect = array_merge($classNamesToReflect, $classNamesInOnePackage); + $availableClassnames = []; + foreach ($this->availableClassNames as $classNamesInPackage) { + $availableClassnames[] = $classNamesInPackage; } + $classNamesToReflect = array_merge([], ...$availableClassnames); $reflectedClassNames = array_keys($this->classReflectionData); sort($classNamesToReflect); sort($reflectedClassNames); @@ -1032,7 +970,7 @@ protected function reflectEmergedClasses(): void $count = 0; $classNameFilterFunction = function ($className) use (&$count): bool { - $this->reflectClass($className); + $this->loadOrReflectClassIfNecessary($className); if ( !$this->isClassAnnotatedWith($className, Flow\Entity::class) && !$this->isClassAnnotatedWith($className, ORM\Entity::class) && @@ -1092,10 +1030,14 @@ protected function reflectClass(string $className): void } $class = new ClassReflection($className); - if (!isset($this->classReflectionData[$className])) { + if (!isset($this->classReflectionData[$className]) || !is_array($this->classReflectionData[$className])) { $this->classReflectionData[$className] = []; } + if (!isset($this->classReflectionData[$className][self::DATA_INTERFACE_IMPLEMENTATIONS]) && $class->isInterface()) { + $this->classReflectionData[$className][self::DATA_INTERFACE_IMPLEMENTATIONS] = []; + } + if ($class->isAbstract() || $class->isInterface()) { $this->classReflectionData[$className][self::DATA_CLASS_ABSTRACT] = true; } @@ -1140,7 +1082,6 @@ protected function reflectClass(string $className): void // important for comparisons when checking if classes have changed in a // Development context. ksort($this->classReflectionData); - $this->updatedReflectionData[$className] = true; } @@ -1159,9 +1100,10 @@ public function reflectClassProperty(string $className, PropertyReflection $prop $this->classReflectionData[$className][self::DATA_CLASS_PROPERTIES][$propertyName][self::DATA_PROPERTY_TYPE] = trim((string)$property->getType(), '?'); } - $visibility = $property->isPublic() ? self::VISIBILITY_PUBLIC : ($property->isProtected() ? self::VISIBILITY_PROTECTED : self::VISIBILITY_PRIVATE); + $visibility = $this->extractVisibility($property); $this->classReflectionData[$className][self::DATA_CLASS_PROPERTIES][$propertyName][self::DATA_PROPERTY_VISIBILITY] = $visibility; + foreach ($property->getTagsValues() as $tagName => $tagValues) { $tagValues = $this->reflectPropertyTag($className, $property, $tagName, $tagValues); if ($tagValues === null) { @@ -1217,8 +1159,8 @@ protected function reflectPropertyTag(string $className, PropertyReflection $pro protected function addParentClass(string $className, ClassReflection $parentClass): void { $parentClassName = $parentClass->getName(); - if (!isset($this->classReflectionData[$parentClassName])) { - $this->reflectClass($parentClassName); + if (!$this->isClassReflected($parentClassName)) { + $this->loadOrReflectClassIfNecessary($parentClassName); } $this->classReflectionData[$parentClassName][self::DATA_CLASS_SUBCLASSES][$className] = true; } @@ -1235,9 +1177,10 @@ protected function addImplementedInterface(string $className, ClassReflection $i } $interfaceName = $interface->getName(); - if (!isset($this->classReflectionData[$interfaceName])) { - $this->reflectClass($interfaceName); + if (!$this->isClassReflected($interfaceName)) { + $this->loadOrReflectClassIfNecessary($interfaceName); } + $this->classReflectionData[$interfaceName][self::DATA_INTERFACE_IMPLEMENTATIONS][$className] = true; } @@ -1255,8 +1198,7 @@ protected function reflectClassMethod(string $className, MethodReflection $metho if ($method->isStatic()) { $this->classReflectionData[$className][self::DATA_CLASS_METHODS][$methodName][self::DATA_METHOD_STATIC] = true; } - $visibility = $method->isPublic() ? self::VISIBILITY_PUBLIC : ($method->isProtected() ? self::VISIBILITY_PROTECTED : self::VISIBILITY_PRIVATE); - $this->classReflectionData[$className][self::DATA_CLASS_METHODS][$methodName][self::DATA_METHOD_VISIBILITY] = $visibility; + $this->classReflectionData[$className][self::DATA_CLASS_METHODS][$methodName][self::DATA_METHOD_VISIBILITY] = $this->extractVisibility($method); foreach ($this->getMethodAnnotations($className, $methodName) as $methodAnnotation) { $annotationClassName = get_class($methodAnnotation); @@ -1294,6 +1236,15 @@ protected function reflectClassMethod(string $className, MethodReflection $metho } } + protected function extractVisibility(MethodReflection|PropertyReflection $reflection): int + { + return match (true) { + $reflection->isPublic() => self::VISIBILITY_PUBLIC, + $reflection->isProtected() => self::VISIBILITY_PROTECTED, + default => self::VISIBILITY_PRIVATE + }; + } + /** * @param string $className * @param MethodReflection $method @@ -1369,7 +1320,7 @@ protected function expandType(ClassReflection $class, string $type): string // and then we try to find "use" statements for the class. $className = $class->getName(); if (!isset($this->useStatementsForClassCache[$className])) { - $this->useStatementsForClassCache[$className] = $this->getDoctrinePhpParser()->parseClass($class); + $this->useStatementsForClassCache[$className] = $this->getDoctrinePhpParser()->parseUseStatements($class); } $useStatementsForClass = $this->useStatementsForClassCache[$className]; @@ -1404,10 +1355,12 @@ protected function getParentClasses(ClassReflection $class, array $parentClasses /** * Builds class schemata from classes annotated as entities or value objects * + * @param array $classNames * @throws ClassLoadingForReflectionFailedException * @throws ClassSchemaConstraintViolationException * @throws Exception * @throws InvalidClassException + * @throws InvalidPropertyTypeException * @throws InvalidValueObjectException */ protected function buildClassSchemata(array $classNames): void @@ -1730,26 +1683,9 @@ protected function convertParameterReflectionToArray(ParameterReflection $parame */ protected function forgetChangedClasses(): void { - $frozenNamespaces = []; - foreach ($this->packageManager->getAvailablePackages() as $packageKey => $package) { - if ($this->packageManager->isPackageFrozen($packageKey)) { - $frozenNamespaces = array_merge($frozenNamespaces, $package->getNamespaces()); - } - } - $frozenNamespaces = array_unique($frozenNamespaces); - $classNames = array_keys($this->classReflectionData); - foreach ($frozenNamespaces as $namespace) { - $namespace .= '\\'; - foreach ($classNames as $index => $className) { - if (str_starts_with($className, $namespace)) { - unset($classNames[$index]); - } - } - } - foreach ($classNames as $className) { - if (!$this->statusCache->has($this->produceCacheIdentifierFromClassName($className))) { + if (!$this->reflectionDataRuntimeCache->has($this->produceCacheIdentifierFromClassName($className))) { $this->forgetClass($className); } } @@ -1812,57 +1748,6 @@ protected function forgetClass($className): void unset($this->classesCurrentlyBeingForgotten[$className]); } - /** - * Tries to load the reflection data from the compile time cache. - * - * The compile time cache is only supported for Development context and thus - * this function will return in any other context. - * - * If no reflection data was found, this method will at least load the precompiled - * reflection data of any possible frozen package. Even if precompiled reflection - * data could be loaded, false will be returned in order to signal that other - * packages still need to be reflected. - * - * @return bool true if reflection data could be loaded, otherwise false - * @throws FilesException - * @throws \Neos\Flow\Utility\Exception - */ - protected function loadClassReflectionCompiletimeCache(): bool - { - $data = $this->reflectionDataCompiletimeCache->get('ReflectionData'); - - if ($data !== false) { - foreach ($data as $propertyName => $propertyValue) { - $this->$propertyName = $propertyValue; - } - - return true; - } - - if (!$this->context->isDevelopment()) { - return false; - } - - $useIgBinary = extension_loaded('igbinary'); - foreach ($this->packageManager->getAvailablePackages() as $packageKey => $package) { - if (!$this->packageManager->isPackageFrozen($packageKey)) { - continue; - } - - $pathAndFilename = $this->getPrecompiledReflectionStoragePath() . $packageKey . '.dat'; - if (!file_exists($pathAndFilename)) { - continue; - } - - $data = ($useIgBinary ? igbinary_unserialize(file_get_contents($pathAndFilename)) : unserialize(file_get_contents($pathAndFilename))); - foreach ($data as $propertyName => $propertyValue) { - $this->$propertyName = Arrays::arrayMergeRecursiveOverrule($this->$propertyName, $propertyValue); - } - } - - return false; - } - /** * Loads reflection data from the cache or reflects the class if needed. * @@ -1879,155 +1764,38 @@ protected function loadClassReflectionCompiletimeCache(): bool */ protected function loadOrReflectClassIfNecessary(string $className): void { - if (!isset($this->classReflectionData[$className]) || is_array($this->classReflectionData[$className])) { + if ($this->isClassReflected($className)) { return; } - if ($this->loadFromClassSchemaRuntimeCache === true) { - $this->classReflectionData[$className] = $this->reflectionDataRuntimeCache->get($this->produceCacheIdentifierFromClassName($className)); + $this->classReflectionData[$className] = $this->reflectionDataRuntimeCache->get($this->produceCacheIdentifierFromClassName($className)); + if ($this->isClassReflected($className)) { return; } $this->reflectClass($className); } - /** - * Stores the current reflection data related to classes of the specified package - * in the PrecompiledReflectionData directory for the current context. - * - * This method is used by the package manager. - * - * @param (int|string) $packageKey - * - * @psalm-param array-key $packageKey - */ - public function freezePackageReflection($packageKey): void - { - if (!$this->initialized) { - $this->initialize(); - } - if (empty($this->availableClassNames)) { - $this->availableClassNames = $this->reflectionDataRuntimeCache->get('__availableClassNames'); - } - - $reflectionData = [ - 'classReflectionData' => $this->classReflectionData, - 'classSchemata' => $this->classSchemata, - 'annotatedClasses' => $this->annotatedClasses, - 'classesByMethodAnnotations' => $this->classesByMethodAnnotations - ]; - - $reflectionData['classReflectionData'] = $this->filterArrayByClassesInPackageNamespace($reflectionData['classReflectionData'], $packageKey); - $reflectionData['classSchemata'] = $this->filterArrayByClassesInPackageNamespace($reflectionData['classSchemata'], $packageKey); - $reflectionData['annotatedClasses'] = $this->filterArrayByClassesInPackageNamespace($reflectionData['annotatedClasses'], $packageKey); - - $methodAnnotationsFilters = function ($className) use ($packageKey): bool { - return (isset($this->availableClassNames[$packageKey]) && in_array($className, $this->availableClassNames[$packageKey], true)); - }; - - foreach ($reflectionData['classesByMethodAnnotations'] as $annotationClassName => $classNames) { - $reflectionData['classesByMethodAnnotations'][$annotationClassName] = array_filter($classNames, $methodAnnotationsFilters); - } - - $precompiledReflectionStoragePath = $this->getPrecompiledReflectionStoragePath(); - if (!is_dir($precompiledReflectionStoragePath)) { - Files::createDirectoryRecursively($precompiledReflectionStoragePath); - } - $pathAndFilename = $precompiledReflectionStoragePath . $packageKey . '.dat'; - file_put_contents($pathAndFilename, extension_loaded('igbinary') ? igbinary_serialize($reflectionData) : serialize($reflectionData)); - } - - /** - * Filter an array of entries where keys are class names by being in the given package namespace. - * - * @param int|string $packageKey - * - * @psalm-param array-key $packageKey - */ - protected function filterArrayByClassesInPackageNamespace(array $array, $packageKey): array - { - return array_filter($array, function ($className) use ($packageKey) { - return (isset($this->availableClassNames[$packageKey]) && in_array($className, $this->availableClassNames[$packageKey], true)); - }, ARRAY_FILTER_USE_KEY); - } - - /** - * Removes the precompiled reflection data of a frozen package - * - * This method is used by the package manager. - * - * @throws FilesException - * @throws \Neos\Flow\Utility\Exception - */ - public function unfreezePackageReflection(string $packageKey): void - { - if (!$this->initialized) { - $this->initialize(); - } - $pathAndFilename = $this->getPrecompiledReflectionStoragePath() . $packageKey . '.dat'; - if (file_exists($pathAndFilename)) { - unlink($pathAndFilename); - } - } - /** * Exports the internal reflection data into the ReflectionData cache * * This method is triggered by a signal which is connected to the bootstrap's * shutdown sequence. * - * If the reflection data has previously been loaded from the runtime cache, - * saving it is omitted as changes are not expected. - * - * In Production context the whole cache is written at once and then frozen in - * order to be consistent. Frozen cache data in Development is only produced for - * classes contained in frozen packages. * @throws Exception */ public function saveToCache(): void { - if ($this->hasFrozenCacheInProduction()) { - return; - } if (!$this->initialized) { $this->initialize(); } - if ($this->loadFromClassSchemaRuntimeCache === true) { - return; - } - if (!empty($this->availableClassNames)) { - $this->reflectionDataRuntimeCache->set('__availableClassNames', $this->availableClassNames); - } - - if ($this->updatedReflectionData !== []) { - $this->updateReflectionData(); - } - - if ($this->context->isProduction()) { - $this->saveProductionData(); + if (empty($this->updatedReflectionData)) { return; } - $this->saveDevelopmentData(); - } - - /** - * Save reflection data to cache in Development context. - * - * @throws FilesException - * @throws \Neos\Flow\Utility\Exception - */ - protected function saveDevelopmentData(): void - { - foreach (array_keys($this->packageManager->getFrozenPackages()) as $packageKey) { - $pathAndFilename = $this->getPrecompiledReflectionStoragePath() . $packageKey . '.dat'; - if (!file_exists($pathAndFilename)) { - $this->log(sprintf('Rebuilding precompiled reflection data for frozen package %s.', $packageKey), LogLevel::DEBUG); - $this->freezePackageReflection($packageKey); - } - } + $this->saveProductionData(); } /** @@ -2037,55 +1805,27 @@ protected function saveDevelopmentData(): void */ protected function saveProductionData(): void { - $this->reflectionDataRuntimeCache->flush(); - $this->classSchemataRuntimeCache->flush(); - $classNames = []; foreach ($this->classReflectionData as $className => $reflectionData) { - $classNames[$className] = true; + if ($this->isClassReflected($className)) { + $classNames[$className] = true; + } + } + + foreach (array_keys($this->updatedReflectionData) as $className) { + $reflectionData = $this->classReflectionData[$className]; $cacheIdentifier = $this->produceCacheIdentifierFromClassName($className); $this->reflectionDataRuntimeCache->set($cacheIdentifier, $reflectionData); if (isset($this->classSchemata[$className])) { $this->classSchemataRuntimeCache->set($cacheIdentifier, $this->classSchemata[$className]); } } + $this->reflectionDataRuntimeCache->set('__classNames', $classNames); $this->reflectionDataRuntimeCache->set('__annotatedClasses', $this->annotatedClasses); + $this->reflectionDataRuntimeCache->set('__classesByMethodAnnotations', $this->classesByMethodAnnotations); - /** @phpstan-ignore-next-line will be refactored with neos 9 */ - $this->reflectionDataRuntimeCache->getBackend()->freeze(); - /** @phpstan-ignore-next-line will be refactored with neos 9 */ - $this->classSchemataRuntimeCache->getBackend()->freeze(); - - $this->log(sprintf('Built and froze reflection runtime caches (%s classes).', count($this->classReflectionData)), LogLevel::INFO); - } - - /** - * Set updated reflection data to caches. - * - * @throws Exception - */ - protected function updateReflectionData(): void - { - $this->log(sprintf('Found %s classes whose reflection data was not cached previously.', count($this->updatedReflectionData)), LogLevel::DEBUG); - - foreach (array_keys($this->updatedReflectionData) as $className) { - $this->statusCache->set($this->produceCacheIdentifierFromClassName($className), ''); - } - - $data = []; - $propertyNames = [ - 'classReflectionData', - 'classSchemata', - 'annotatedClasses', - 'classesByMethodAnnotations' - ]; - - foreach ($propertyNames as $propertyName) { - $data[$propertyName] = $this->$propertyName; - } - - $this->reflectionDataCompiletimeCache->set('ReflectionData', $data); + $this->log(sprintf('Updated reflection caches (%s classes).', count($this->updatedReflectionData)), LogLevel::INFO); } /** @@ -2112,22 +1852,7 @@ protected function log(string $message, string $severity = LogLevel::INFO, array $this->logger?->log($severity, $message, $additionalData); } - /** - * @throws \Neos\Flow\Utility\Exception - * @throws FilesException - */ - protected function getPrecompiledReflectionStoragePath(): string - { - return Files::concatenatePaths([$this->environment->getPathToTemporaryDirectory(), 'PrecompiledReflectionData/']) . '/'; - } - - protected function hasFrozenCacheInProduction(): bool - { - /** @phpstan-ignore-next-line will be refactored with neos 9 */ - return $this->environment->getContext()->isProduction() && $this->reflectionDataRuntimeCache->getBackend()->isFrozen(); - } - - private function renderParameterType(?\ReflectionType $parameterType): ?string + private function renderParameterType(?\ReflectionType $parameterType = null): string|null { $that = $this; return match (true) { diff --git a/Neos.Flow/Classes/Reflection/ReflectionServiceFactory.php b/Neos.Flow/Classes/Reflection/ReflectionServiceFactory.php index c86ead9de3..e3e5ed1429 100644 --- a/Neos.Flow/Classes/Reflection/ReflectionServiceFactory.php +++ b/Neos.Flow/Classes/Reflection/ReflectionServiceFactory.php @@ -16,8 +16,6 @@ use Neos\Flow\Configuration\ConfigurationManager; use Neos\Flow\Core\Bootstrap; use Neos\Flow\Log\PsrLoggerFactoryInterface; -use Neos\Flow\Package\PackageManager; -use Neos\Flow\Utility\Environment; /** * Factory for getting an reflection service instance. @@ -64,12 +62,8 @@ public function create(): ReflectionService $reflectionService = new ReflectionService(); $reflectionService->injectLogger($this->bootstrap->getEarlyInstance(PsrLoggerFactoryInterface::class)->get('systemLogger')); $reflectionService->injectSettings($settings); - $reflectionService->injectPackageManager($this->bootstrap->getEarlyInstance(PackageManager::class)); - $reflectionService->setStatusCache($cacheManager->getCache('Flow_Reflection_Status')); - $reflectionService->setReflectionDataCompiletimeCache($cacheManager->getCache('Flow_Reflection_CompiletimeData')); $reflectionService->setReflectionDataRuntimeCache($cacheManager->getCache('Flow_Reflection_RuntimeData')); $reflectionService->setClassSchemataRuntimeCache($cacheManager->getCache('Flow_Reflection_RuntimeClassSchemata')); - $reflectionService->injectEnvironment($this->bootstrap->getEarlyInstance(Environment::class)); $this->reflectionService = $reflectionService; return $reflectionService; diff --git a/Neos.Flow/Configuration/Caches.yaml b/Neos.Flow/Configuration/Caches.yaml index b7238738c1..e9ea1501f1 100644 --- a/Neos.Flow/Configuration/Caches.yaml +++ b/Neos.Flow/Configuration/Caches.yaml @@ -93,16 +93,23 @@ Flow_Persistence_Doctrine_Results: Flow_Persistence_Doctrine_SecondLevel: backend: Neos\Cache\Backend\SimpleFileBackend -# Flow_Reflection* # +# DEPRECATED since 8.4 +# These caches will be removed in 9.0, they are unused so you can safely remove any configuration for them. # Flow_Reflection_Status: frontend: Neos\Cache\Frontend\StringFrontend backend: Neos\Cache\Backend\SimpleFileBackend Flow_Reflection_CompiletimeData: backend: Neos\Cache\Backend\SimpleFileBackend -Flow_Reflection_RuntimeData: [] -Flow_Reflection_RuntimeClassSchemata: [] + +# Flow_Reflection +# +# +Flow_Reflection_RuntimeData: + backend: Neos\Cache\Backend\SimpleFileBackend +Flow_Reflection_RuntimeClassSchemata: + backend: Neos\Cache\Backend\SimpleFileBackend # Flow_Resource_Status # diff --git a/Neos.Flow/Tests/Functional/Reflection/ReflectionServiceTest.php b/Neos.Flow/Tests/Functional/Reflection/ReflectionServiceTest.php index ac8807d3fe..a306e8ab96 100644 --- a/Neos.Flow/Tests/Functional/Reflection/ReflectionServiceTest.php +++ b/Neos.Flow/Tests/Functional/Reflection/ReflectionServiceTest.php @@ -57,6 +57,7 @@ public function classSchemaCanBeBuiltForAggregateRootsWithPlainOldPhpBaseClasses /** * @test * @throws + * @deprecated */ public function theReflectionServiceCorrectlyBuildsMethodTagsValues(): void { diff --git a/Neos.Flow/Tests/Unit/Cache/CacheManagerTest.php b/Neos.Flow/Tests/Unit/Cache/CacheManagerTest.php index 232e8e577a..5401b60a91 100644 --- a/Neos.Flow/Tests/Unit/Cache/CacheManagerTest.php +++ b/Neos.Flow/Tests/Unit/Cache/CacheManagerTest.php @@ -277,7 +277,7 @@ public function flushSystemCachesByChangedFilesWithChangedClassFileRemovesCacheE { $objectClassCache = $this->registerCache('Flow_Object_Classes'); $objectConfigurationCache = $this->registerCache('Flow_Object_Configuration'); - $this->registerCache('Flow_Reflection_Status'); + $this->registerCache('Flow_Reflection_RuntimeData'); $objectClassCache->expects(self::once())->method('remove')->with('Neos_Flow_Cache_CacheManager'); $objectConfigurationCache->expects(self::once())->method('remove')->with('allCompiledCodeUpToDate'); @@ -294,7 +294,7 @@ public function flushSystemCachesByChangedFilesWithChangedTestFileRemovesCacheEn { $objectClassCache = $this->registerCache('Flow_Object_Classes'); $objectConfigurationCache = $this->registerCache('Flow_Object_Configuration'); - $this->registerCache('Flow_Reflection_Status'); + $this->registerCache('Flow_Reflection_RuntimeData'); $objectClassCache->expects(self::once())->method('remove')->with('Neos_Flow_Tests_Unit_Cache_CacheManagerTest'); $objectConfigurationCache->expects(self::once())->method('remove')->with('allCompiledCodeUpToDate'); diff --git a/Neos.Flow/Tests/Unit/Package/PackageManagerTest.php b/Neos.Flow/Tests/Unit/Package/PackageManagerTest.php index ab32cb06e3..72bb58373c 100644 --- a/Neos.Flow/Tests/Unit/Package/PackageManagerTest.php +++ b/Neos.Flow/Tests/Unit/Package/PackageManagerTest.php @@ -470,36 +470,4 @@ public function createPackageEmitsPackageStatesUpdatedSignal() $this->mockDispatcher->expects(self::once())->method('dispatch')->with(PackageManager::class, 'packageStatesUpdated'); $this->packageManager->createPackage('Some.Package', [], 'vfs://Test/Packages/Application'); } - - /** - * @test - */ - public function freezePackageEmitsPackageStatesUpdatedSignal() - { - $this->mockApplicationContext->expects(self::atLeastOnce())->method('isDevelopment')->will(self::returnValue(true)); - - $this->packageManager->createPackage('Some.Package', [ - 'name' => 'some/package' - ], 'vfs://Test/Packages/Application'); - - $this->mockDispatcher->expects(self::once())->method('dispatch')->with(PackageManager::class, 'packageStatesUpdated'); - $this->packageManager->freezePackage('Some.Package'); - } - - /** - * @test - */ - public function unfreezePackageEmitsPackageStatesUpdatedSignal() - { - $this->mockApplicationContext->expects(self::atLeastOnce())->method('isDevelopment')->will(self::returnValue(true)); - - $this->packageManager->createPackage('Some.Package', [ - 'name' => 'some/package', - 'type' => 'neos-package' - ], 'vfs://Test/Packages/Application'); - $this->packageManager->freezePackage('Some.Package'); - - $this->mockDispatcher->expects(self::once())->method('dispatch')->with(PackageManager::class, 'packageStatesUpdated'); - $this->packageManager->unfreezePackage('Some.Package'); - } } diff --git a/Neos.FluidAdaptor/Classes/Package.php b/Neos.FluidAdaptor/Classes/Package.php index 9f6a9cd540..84a32d31ed 100644 --- a/Neos.FluidAdaptor/Classes/Package.php +++ b/Neos.FluidAdaptor/Classes/Package.php @@ -48,10 +48,6 @@ public function boot(Bootstrap $bootstrap) $packageManager = $bootstrap->getEarlyInstance(PackageManager::class); /** @var FlowPackageInterface $package */ foreach ($packageManager->getFlowPackages() as $packageKey => $package) { - if ($packageManager->isPackageFrozen($packageKey)) { - continue; - } - foreach (['Templates', 'Partials', 'Layouts'] as $path) { $templatesPath = $package->getResourcesPath() . 'Private/' . $path; From 82700f16f4bc7bc34a2047ec60d9e7a26811536f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Mu=CC=88ller?= Date: Tue, 11 Feb 2025 07:50:25 +0100 Subject: [PATCH 3/9] Make sure interface implementations are cleared The new caching loads lazily we need to make sure that the interfaces are actually loaded when trying to remove implementation classes. --- Neos.Flow/Classes/Reflection/ReflectionService.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Neos.Flow/Classes/Reflection/ReflectionService.php b/Neos.Flow/Classes/Reflection/ReflectionService.php index e3d88610f0..e6b40f3527 100644 --- a/Neos.Flow/Classes/Reflection/ReflectionService.php +++ b/Neos.Flow/Classes/Reflection/ReflectionService.php @@ -1710,6 +1710,7 @@ protected function forgetClass($className): void if (class_exists($className)) { $interfaceNames = class_implements($className); foreach ($interfaceNames as $interfaceName) { + $this->loadOrReflectClassIfNecessary($interfaceName); if (isset($this->classReflectionData[$interfaceName][self::DATA_INTERFACE_IMPLEMENTATIONS][$className])) { unset($this->classReflectionData[$interfaceName][self::DATA_INTERFACE_IMPLEMENTATIONS][$className]); } @@ -1717,6 +1718,7 @@ protected function forgetClass($className): void } else { foreach ($this->availableClassNames as $interfaceNames) { foreach ($interfaceNames as $interfaceName) { + $this->loadOrReflectClassIfNecessary($interfaceName); if (isset($this->classReflectionData[$interfaceName][self::DATA_INTERFACE_IMPLEMENTATIONS][$className])) { unset($this->classReflectionData[$interfaceName][self::DATA_INTERFACE_IMPLEMENTATIONS][$className]); } From 873a02c65ec898299e8699ad261d27585eda001a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Mu=CC=88ller?= Date: Tue, 11 Feb 2025 07:53:33 +0100 Subject: [PATCH 4/9] First check if cache updates are scheduled before initialize Otherwise the initialize might happen for no good reason if the ReflectionService was never used. --- Neos.Flow/Classes/Reflection/ReflectionService.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Neos.Flow/Classes/Reflection/ReflectionService.php b/Neos.Flow/Classes/Reflection/ReflectionService.php index e6b40f3527..d9aaaca0d5 100644 --- a/Neos.Flow/Classes/Reflection/ReflectionService.php +++ b/Neos.Flow/Classes/Reflection/ReflectionService.php @@ -1789,15 +1789,15 @@ protected function loadOrReflectClassIfNecessary(string $className): void */ public function saveToCache(): void { - if (!$this->initialized) { - $this->initialize(); - } - if (empty($this->updatedReflectionData)) { return; } - $this->saveProductionData(); + if (!$this->initialized) { + $this->initialize(); + } + + $this->updateCacheEntries(); } /** @@ -1805,7 +1805,7 @@ public function saveToCache(): void * * @throws Exception */ - protected function saveProductionData(): void + private function updateCacheEntries(): void { $classNames = []; foreach ($this->classReflectionData as $className => $reflectionData) { From 19abeedae836f2ed8c4877badb667d69f9be6327 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Mu=CC=88ller?= Date: Tue, 11 Feb 2025 07:55:15 +0100 Subject: [PATCH 5/9] Docblock and code improvements Some docblock changes and minimal code improvements, might be nice to have for future refactorings. --- .../Classes/Reflection/ReflectionService.php | 286 ++++++++++++++---- 1 file changed, 220 insertions(+), 66 deletions(-) diff --git a/Neos.Flow/Classes/Reflection/ReflectionService.php b/Neos.Flow/Classes/Reflection/ReflectionService.php index d9aaaca0d5..d4186300ab 100644 --- a/Neos.Flow/Classes/Reflection/ReflectionService.php +++ b/Neos.Flow/Classes/Reflection/ReflectionService.php @@ -17,6 +17,8 @@ use Doctrine\Common\Annotations\Reader; use Doctrine\ORM\Mapping as ORM; use Doctrine\Persistence\Proxy as DoctrineProxy; +use Error; +use InvalidArgumentException; use Neos\Cache\Exception; use Neos\Cache\Frontend\VariableFrontend; use Neos\Flow\Annotations as Flow; @@ -28,11 +30,15 @@ use Neos\Flow\Reflection\Exception\InvalidClassException; use Neos\Flow\Reflection\Exception\InvalidPropertyTypeException; use Neos\Flow\Reflection\Exception\InvalidValueObjectException; -use Neos\Utility\Exception\FilesException; use Neos\Utility\TypeHandling; use Psr\Log\LoggerInterface; use Psr\Log\LogLevel; use ReflectionException; +use ReflectionIntersectionType; +use ReflectionNamedType; +use ReflectionType; +use ReflectionUnionType; +use RuntimeException; /** * A service for acquiring reflection based information in a performant way. This @@ -272,12 +278,17 @@ public function getAllClassNames(): array * interface name. If no class implementing the interface was found or more than one * implementation was found in the package defining the interface, false is returned. * + * @param class-string $interfaceName + * @return string|bool + * @throws ClassLoadingForReflectionFailedException + * @throws InvalidClassException + * @throws ReflectionException * @api */ public function getDefaultImplementationClassNameForInterface(string $interfaceName): string|bool { if (interface_exists($interfaceName) === false) { - throw new \InvalidArgumentException('"' . $interfaceName . '" does not exist or is not the name of an interface.', 1238769559); + throw new InvalidArgumentException('"' . $interfaceName . '" does not exist or is not the name of an interface.', 1238769559); } $interfaceName = $this->prepareClassReflectionForUsage($interfaceName); @@ -305,12 +316,15 @@ public function getDefaultImplementationClassNameForInterface(string $interfaceN * * @param class-string $interfaceName * @return array + * @throws ClassLoadingForReflectionFailedException + * @throws InvalidClassException + * @throws ReflectionException * @api */ public function getAllImplementationClassNamesForInterface(string $interfaceName): array { if (interface_exists($interfaceName) === false) { - throw new \InvalidArgumentException('"' . $interfaceName . '" does not exist or is not the name of an interface.', 1238769560); + throw new InvalidArgumentException('"' . $interfaceName . '" does not exist or is not the name of an interface.', 1238769560); } $interfaceName = $this->prepareClassReflectionForUsage($interfaceName); @@ -321,14 +335,17 @@ public function getAllImplementationClassNamesForInterface(string $interfaceName * Searches for and returns all names of classes inheriting the specified class. * If no class inheriting the given class was found, an empty array is returned. * - * @psalm-param class-string $className - * @psalm-return array + * @param class-string $className + * @return array + * @throws ClassLoadingForReflectionFailedException + * @throws InvalidClassException + * @throws ReflectionException * @api */ public function getAllSubClassNamesForClass(string $className): array { if (class_exists($className) === false) { - throw new \InvalidArgumentException('"' . $className . '" does not exist or is not the name of a class.', 1257168042); + 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]) : []; @@ -368,9 +385,12 @@ public function isClassAnnotatedWith(string $className, string $annotationClassN /** * Returns the specified class annotations or an empty array * - * @param null|string $annotationClassName + * @param class-string $className + * @param null|class-string $annotationClassName * @return array - * + * @throws ClassLoadingForReflectionFailedException + * @throws InvalidClassException + * @throws ReflectionException */ public function getClassAnnotations(string $className, string|null $annotationClassName = null): array { @@ -399,6 +419,13 @@ public function getClassAnnotations(string $className, string|null $annotationCl * * If multiple annotations are set on the target you will * get the first instance of them. + * + * @param class-string $className + * @param class-string $annotationClassName + * @return object|null + * @throws ClassLoadingForReflectionFailedException + * @throws InvalidClassException + * @throws ReflectionException */ public function getClassAnnotation(string $className, string $annotationClassName): ?object { @@ -415,6 +442,7 @@ public function getClassAnnotation(string $className, string $annotationClassNam * * @throws ClassLoadingForReflectionFailedException * @throws InvalidClassException + * @throws ReflectionException * @api */ public function isClassImplementationOf(string $className, string $interfaceName): bool @@ -430,6 +458,10 @@ public function isClassImplementationOf(string $className, string $interfaceName /** * Tells if the specified class is abstract or not * + * @param class-string $className + * @throws ClassLoadingForReflectionFailedException + * @throws InvalidClassException + * @throws ReflectionException * @api */ public function isClassAbstract(string $className): bool @@ -441,8 +473,10 @@ public function isClassAbstract(string $className): bool /** * Tells if the specified class is final or not * - * @param string $className Name of the class to analyze - * @return bool true if the class is final, otherwise false + * @param class-string $className Name of the class to analyze + * @throws ClassLoadingForReflectionFailedException + * @throws InvalidClassException + * @throws ReflectionException * @api */ public function isClassFinal(string $className): bool @@ -456,6 +490,9 @@ public function isClassFinal(string $className): bool * * @param string $className Name of the class to analyze * @return bool true if the class is readonly, otherwise false + * @throws ClassLoadingForReflectionFailedException + * @throws InvalidClassException + * @throws ReflectionException * @api */ public function isClassReadonly(string $className): bool @@ -507,6 +544,12 @@ public function getMethodsAnnotatedWith(string $className, string $annotationCla /** * Tells if the specified method is final or not * + * @param string $className + * @param string $methodName + * @return bool + * @throws ClassLoadingForReflectionFailedException + * @throws InvalidClassException + * @throws ReflectionException * @api */ public function isMethodFinal(string $className, string $methodName): bool @@ -518,6 +561,10 @@ public function isMethodFinal(string $className, string $methodName): bool /** * Tells if the specified method is declared as static or not * + * @param class-string $className + * @throws ClassLoadingForReflectionFailedException + * @throws InvalidClassException + * @throws ReflectionException * @api */ public function isMethodStatic(string $className, string $methodName): bool @@ -527,8 +574,10 @@ public function isMethodStatic(string $className, string $methodName): bool } /** - * Tells if the specified method is public - * + * @param class-string $className + * @throws ClassLoadingForReflectionFailedException + * @throws InvalidClassException + * @throws ReflectionException * @api */ public function isMethodPublic(string $className, string $methodName): bool @@ -538,8 +587,10 @@ public function isMethodPublic(string $className, string $methodName): bool } /** - * Tells if the specified method is protected - * + * @param class-string $className + * @throws ClassLoadingForReflectionFailedException + * @throws InvalidClassException + * @throws ReflectionException * @api */ public function isMethodProtected(string $className, string $methodName): bool @@ -549,8 +600,10 @@ public function isMethodProtected(string $className, string $methodName): bool } /** - * Tells if the specified method is private - * + * @param class-string $className + * @throws ClassLoadingForReflectionFailedException + * @throws InvalidClassException + * @throws ReflectionException * @api */ public function isMethodPrivate(string $className, string $methodName): bool @@ -589,6 +642,10 @@ public function isAttributeIgnored(string $attributeName): bool /** * Tells if the specified method has the given annotation * + * @param class-string $className + * @param string $methodName + * @param class-string $annotationClassName + * @return bool * @throws ReflectionException * @api */ @@ -600,14 +657,11 @@ public function isMethodAnnotatedWith(string $className, string $methodName, str /** * Returns the specified method annotations or an empty array * - * @param string $className - * @param string $methodName - * @param null|string $annotationClassName + * @param class-string $className + * @param class-string|null $annotationClassName * @return array * - * @throws FilesException * @throws ReflectionException - * @throws \Neos\Flow\Utility\Exception * @api * */ @@ -667,6 +721,11 @@ public function getMethodAnnotation(string $className, string $methodName, strin /** * Returns the names of all properties of the specified class * + * @param class-string $className + * @return array + * @throws ClassLoadingForReflectionFailedException + * @throws InvalidClassException + * @throws ReflectionException * @api */ public function getClassPropertyNames(string $className): array @@ -678,6 +737,10 @@ public function getClassPropertyNames(string $className): array /** * Wrapper for method_exists() which tells if the given method exists. * + * @param class-string $className + * @throws ClassLoadingForReflectionFailedException + * @throws InvalidClassException + * @throws ReflectionException * @api */ public function hasMethod(string $className, string $methodName): bool @@ -706,7 +769,11 @@ public function getMethodTagsValues(string $className, string $methodName): arra * Returns an array of parameters of the given method. Each entry contains * additional information about the parameter position, type hint etc. * + * @param class-string $className * @return array An array of parameter names and additional information or an empty array of no parameters were found + * @throws ClassLoadingForReflectionFailedException + * @throws InvalidClassException + * @throws ReflectionException * @api */ public function getMethodParameters(string $className, string $methodName): array @@ -723,8 +790,10 @@ public function getMethodParameters(string $className, string $methodName): arra * Returns the declared return type of a method (for PHP < 7.0 this will always return null) * * @param class-string $className - * @return ?string The declared return type of the method or null if none was declared - * + * @return string|null The declared return type of the method or null if none was declared + * @throws ClassLoadingForReflectionFailedException + * @throws InvalidClassException + * @throws ReflectionException */ public function getMethodDeclaredReturnType(string $className, string $methodName): ?string { @@ -736,9 +805,13 @@ public function getMethodDeclaredReturnType(string $className, string $methodNam * Searches for and returns all names of class properties which are tagged by the specified tag. * If no properties were found, an empty array is returned. * + * @param class-string $className + * @return array + * @throws ClassLoadingForReflectionFailedException + * @throws InvalidClassException + * @throws ReflectionException * @api * - * @psalm-param 'id'|'var' $tag */ public function getPropertyNamesByTag(string $className, string $tag): array { @@ -760,9 +833,15 @@ public function getPropertyNamesByTag(string $className, string $tag): array /** * Returns all tags and their values the specified class property is tagged with * + * @param class-string $className + * @param string $propertyName + * @return array + * @throws ClassLoadingForReflectionFailedException + * @throws InvalidClassException + * @throws ReflectionException * @api */ - public function getPropertyTagsValues(string $className, string $propertyName): mixed + public function getPropertyTagsValues(string $className, string $propertyName): array { $className = $this->prepareClassReflectionForUsage($className); if (!isset($this->classReflectionData[$className][self::DATA_CLASS_PROPERTIES][$propertyName])) { @@ -775,11 +854,17 @@ public function getPropertyTagsValues(string $className, string $propertyName): /** * Returns the values of the specified class property tag * + * @param class-string $className + * @param string $propertyName + * @param string $tag + * @return array + * @throws ClassLoadingForReflectionFailedException + * @throws InvalidClassException + * @throws ReflectionException * @api * - * @psalm-param 'var' $tag */ - public function getPropertyTagValues(string $className, string $propertyName, string $tag) + public function getPropertyTagValues(string $className, string $propertyName, string $tag): array { $className = $this->prepareClassReflectionForUsage($className); if (!isset($this->classReflectionData[$className][self::DATA_CLASS_PROPERTIES][$propertyName])) { @@ -791,6 +876,13 @@ public function getPropertyTagValues(string $className, string $propertyName, st /** * Returns the property type + * + * @param class-string $className + * @param string $propertyName + * @return string|null + * @throws ClassLoadingForReflectionFailedException + * @throws InvalidClassException + * @throws ReflectionException */ public function getPropertyType(string $className, string $propertyName): ?string { @@ -799,8 +891,12 @@ public function getPropertyType(string $className, string $propertyName): ?strin } /** - * Tells if the specified property is private - * + * @param class-string $className + * @param string $propertyName + * @return bool + * @throws ClassLoadingForReflectionFailedException + * @throws InvalidClassException + * @throws ReflectionException * @api */ public function isPropertyPrivate(string $className, string $propertyName): bool @@ -813,6 +909,13 @@ public function isPropertyPrivate(string $className, string $propertyName): bool /** * Tells if the specified class property is tagged with the given tag * + * @param class-string $className + * @param string $propertyName + * @param string $tag + * @return bool + * @throws ClassLoadingForReflectionFailedException + * @throws InvalidClassException + * @throws ReflectionException * @api */ public function isPropertyTaggedWith(string $className, string $propertyName, string $tag): bool @@ -824,6 +927,13 @@ public function isPropertyTaggedWith(string $className, string $propertyName, st /** * Tells if the specified property has the given annotation * + * @param class-string $className + * @param string $propertyName + * @param class-string $annotationClassName + * @return bool + * @throws ClassLoadingForReflectionFailedException + * @throws InvalidClassException + * @throws ReflectionException * @api */ public function isPropertyAnnotatedWith(string $className, string $propertyName, string $annotationClassName): bool @@ -836,6 +946,12 @@ public function isPropertyAnnotatedWith(string $className, string $propertyName, * Searches for and returns all names of class properties which are marked by the * specified annotation. If no properties were found, an empty array is returned. * + * @param class-string $className + * @param class-string $annotationClassName + * @return array + * @throws ClassLoadingForReflectionFailedException + * @throws InvalidClassException + * @throws ReflectionException * @api */ public function getPropertyNamesByAnnotation(string $className, string $annotationClassName): array @@ -858,7 +974,13 @@ public function getPropertyNamesByAnnotation(string $className, string $annotati /** * Returns the specified property annotations or an empty array * + * @param class-string $className + * @param string $propertyName + * @param class-string|null $annotationClassName * @return array + * @throws ClassLoadingForReflectionFailedException + * @throws InvalidClassException + * @throws ReflectionException * @api */ public function getPropertyAnnotations(string $className, string $propertyName, string $annotationClassName = null): array @@ -872,11 +994,7 @@ public function getPropertyAnnotations(string $className, string $propertyName, return $this->classReflectionData[$className][self::DATA_CLASS_PROPERTIES][$propertyName][self::DATA_PROPERTY_ANNOTATIONS]; } - if (isset($this->classReflectionData[$className][self::DATA_CLASS_PROPERTIES][$propertyName][self::DATA_PROPERTY_ANNOTATIONS][$annotationClassName])) { - return $this->classReflectionData[$className][self::DATA_CLASS_PROPERTIES][$propertyName][self::DATA_PROPERTY_ANNOTATIONS][$annotationClassName]; - } - - return []; + return $this->classReflectionData[$className][self::DATA_CLASS_PROPERTIES][$propertyName][self::DATA_PROPERTY_ANNOTATIONS][$annotationClassName] ?? []; } /** @@ -884,6 +1002,14 @@ public function getPropertyAnnotations(string $className, string $propertyName, * * If multiple annotations are set on the target you will * get the first instance of them. + * + * @param string $className + * @param string $propertyName + * @param string $annotationClassName + * @return object|null + * @throws ClassLoadingForReflectionFailedException + * @throws InvalidClassException + * @throws ReflectionException */ public function getPropertyAnnotation(string $className, string $propertyName, string $annotationClassName): ?object { @@ -896,7 +1022,9 @@ public function getPropertyAnnotation(string $className, string $propertyName, s /** * Returns the class schema for the given class - * @param class-string|string|object $classNameOrObject + * + * @param class-string|object $classNameOrObject + * @return ClassSchema|null */ public function getClassSchema(string|object $classNameOrObject): ?ClassSchema { @@ -1016,6 +1144,7 @@ protected function isTagIgnored(string $tagName): bool /** * Reflects the given class and stores the results in this service's properties. * + * @param class-string $className * @throws ClassLoadingForReflectionFailedException * @throws InvalidClassException * @throws ReflectionException @@ -1122,8 +1251,8 @@ public function reflectClassProperty(string $className, PropertyReflection $prop } try { $attributeInstance = $attribute->newInstance(); - } catch (\Error $error) { - throw new \RuntimeException(sprintf('Attribute "%s" used in class "%s" was not found.', $attribute->getName(), $className), 1695635128, $error); + } catch (Error $error) { + throw new RuntimeException(sprintf('Attribute "%s" used in class "%s" was not found.', $attribute->getName(), $className), 1695635128, $error); } $this->classReflectionData[$className][self::DATA_CLASS_PROPERTIES][$propertyName][self::DATA_PROPERTY_ANNOTATIONS][$attribute->getName()][] = $attributeInstance; } @@ -1185,9 +1314,9 @@ protected function addImplementedInterface(string $className, ClassReflection $i } /** - * @throws FilesException + * @param class-string $className + * @param MethodReflection $method * @throws ReflectionException - * @throws \Neos\Flow\Utility\Exception */ protected function reflectClassMethod(string $className, MethodReflection $method): void { @@ -1246,10 +1375,7 @@ protected function extractVisibility(MethodReflection|PropertyReflection $reflec } /** - * @param string $className - * @param MethodReflection $method - * @param ParameterReflection $parameter - * @return void + * @param class-string $className */ protected function reflectClassMethodParameter(string $className, MethodReflection $method, ParameterReflection $parameter): void { @@ -1362,6 +1488,7 @@ protected function getParentClasses(ClassReflection $class, array $parentClasses * @throws InvalidClassException * @throws InvalidPropertyTypeException * @throws InvalidValueObjectException + * @throws ReflectionException */ protected function buildClassSchemata(array $classNames): void { @@ -1376,9 +1503,12 @@ protected function buildClassSchemata(array $classNames): void /** * @param class-string $className * @return ClassSchema + * @throws ClassLoadingForReflectionFailedException * @throws ClassSchemaConstraintViolationException + * @throws InvalidClassException * @throws InvalidPropertyTypeException * @throws InvalidValueObjectException + * @throws ReflectionException */ protected function buildClassSchema(string $className): ClassSchema { @@ -1416,8 +1546,12 @@ protected function buildClassSchema(string $className): ClassSchema * * Invalid annotations will cause an exception to be thrown. * - * @throws InvalidPropertyTypeException + * @param ClassSchema $classSchema + * @throws ClassLoadingForReflectionFailedException * @throws ClassSchemaConstraintViolationException + * @throws InvalidClassException + * @throws InvalidPropertyTypeException + * @throws ReflectionException */ protected function addPropertiesToClassSchema(ClassSchema $classSchema): void { @@ -1442,9 +1576,14 @@ protected function addPropertiesToClassSchema(ClassSchema $classSchema): void } /** - * @throws InvalidPropertyTypeException - * @throws \InvalidArgumentException + * @param ClassSchema $classSchema + * @param string $propertyName + * @return bool + * @throws ClassLoadingForReflectionFailedException * @throws ClassSchemaConstraintViolationException + * @throws InvalidClassException + * @throws InvalidPropertyTypeException + * @throws ReflectionException */ protected function evaluateClassPropertyAnnotationsForSchema(ClassSchema $classSchema, string $propertyName): bool { @@ -1501,6 +1640,7 @@ protected function evaluateClassPropertyAnnotationsForSchema(ClassSchema $classS * @throws ClassLoadingForReflectionFailedException * @throws ClassSchemaConstraintViolationException * @throws InvalidClassException + * @throws ReflectionException */ protected function completeRepositoryAssignments(): void { @@ -1532,9 +1672,11 @@ protected function completeRepositoryAssignments(): void * Assigns the repository of any aggregate root to all it's * subclasses, unless they are aggregate root already. * + * @param ClassSchema $classSchema * @throws ClassLoadingForReflectionFailedException * @throws ClassSchemaConstraintViolationException * @throws InvalidClassException + * @throws ReflectionException */ protected function makeChildClassesAggregateRoot(ClassSchema $classSchema): void { @@ -1552,7 +1694,10 @@ protected function makeChildClassesAggregateRoot(ClassSchema $classSchema): void * Checks whether all aggregate roots having superclasses * have a repository assigned up to the tip of their hierarchy. * + * @throws ClassLoadingForReflectionFailedException * @throws Exception + * @throws InvalidClassException + * @throws ReflectionException */ protected function ensureAggregateRootInheritanceChainConsistency(): void { @@ -1586,7 +1731,7 @@ protected function checkValueObjectRequirements(string $className): void throw new InvalidValueObjectException('A value object must have a constructor, "' . $className . '" does not have one.', 1268740874); } - $setterMethods = array_filter($methods, function ($method) { + $setterMethods = array_filter($methods, static function ($method) { return str_starts_with($method, 'set'); }); @@ -1680,12 +1825,15 @@ protected function convertParameterReflectionToArray(ParameterReflection $parame * accordingly. * * @return void + * @throws ClassLoadingForReflectionFailedException + * @throws InvalidClassException + * @throws ReflectionException */ protected function forgetChangedClasses(): void { $classNames = array_keys($this->classReflectionData); foreach ($classNames as $className) { - if (!$this->reflectionDataRuntimeCache->has($this->produceCacheIdentifierFromClassName($className))) { + if (is_string($className) && !$this->reflectionDataRuntimeCache->has($this->produceCacheIdentifierFromClassName($className))) { $this->forgetClass($className); } } @@ -1694,11 +1842,13 @@ protected function forgetChangedClasses(): void /** * Forgets all reflection data related to the specified class * - * @param (int|string) $className + * @param class-string $className * - * @psalm-param array-key $className + * @throws ClassLoadingForReflectionFailedException + * @throws InvalidClassException + * @throws ReflectionException */ - protected function forgetClass($className): void + private function forgetClass(string $className): void { $this->log('Forget class ' . $className, LogLevel::DEBUG); if (isset($this->classesCurrentlyBeingForgotten[$className])) { @@ -1728,7 +1878,7 @@ protected function forgetClass($className): void if (isset($this->classReflectionData[$className][self::DATA_CLASS_SUBCLASSES])) { foreach (array_keys($this->classReflectionData[$className][self::DATA_CLASS_SUBCLASSES]) as $subClassName) { - $this->forgetClass($subClassName); + $this->forgetClass((string)$subClassName); } } @@ -1738,6 +1888,7 @@ protected function forgetClass($className): void } } + $this->getClassSchema($className); if (isset($this->classSchemata[$className])) { unset($this->classSchemata[$className]); } @@ -1746,8 +1897,7 @@ protected function forgetClass($className): void unset($this->classesByMethodAnnotations[$annotationClassName][$className]); } - unset($this->classReflectionData[$className]); - unset($this->classesCurrentlyBeingForgotten[$className]); + unset($this->classReflectionData[$className], $this->classesCurrentlyBeingForgotten[$className]); } /** @@ -1760,11 +1910,12 @@ protected function forgetClass($className): void * 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 * @throws ReflectionException */ - protected function loadOrReflectClassIfNecessary(string $className): void + private function loadOrReflectClassIfNecessary(string $className): void { if ($this->isClassReflected($className)) { return; @@ -1827,7 +1978,7 @@ private function updateCacheEntries(): void $this->reflectionDataRuntimeCache->set('__annotatedClasses', $this->annotatedClasses); $this->reflectionDataRuntimeCache->set('__classesByMethodAnnotations', $this->classesByMethodAnnotations); - $this->log(sprintf('Updated reflection caches (%s classes).', count($this->updatedReflectionData)), LogLevel::INFO); + $this->log(sprintf('Updated reflection caches (%s classes).', count($this->updatedReflectionData))); } /** @@ -1854,26 +2005,29 @@ protected function log(string $message, string $severity = LogLevel::INFO, array $this->logger?->log($severity, $message, $additionalData); } - private function renderParameterType(?\ReflectionType $parameterType = null): string|null + /** + * @param ReflectionType|null $parameterType + * @return string|null + */ + private function renderParameterType(?ReflectionType $parameterType = null): string|null { - $that = $this; return match (true) { - $parameterType instanceof \ReflectionUnionType => implode('|', array_map( - static function (\ReflectionNamedType | \ReflectionIntersectionType $type) use ($that) { - if ($type instanceof \ReflectionNamedType) { + $parameterType instanceof ReflectionUnionType => implode('|', array_map( + function (ReflectionNamedType | ReflectionIntersectionType $type) { + if ($type instanceof ReflectionNamedType) { return $type->getName(); } - return '(' . $that->renderParameterType($type) . ')'; + return '(' . $this->renderParameterType($type) . ')'; }, $parameterType->getTypes() )), - $parameterType instanceof \ReflectionIntersectionType => implode('&', array_map( - static function (\ReflectionNamedType $type) use ($that) { - return $that->renderParameterType($type); + $parameterType instanceof ReflectionIntersectionType => implode('&', array_map( + function (ReflectionNamedType $type) { + return $this->renderParameterType($type); }, $parameterType->getTypes() )), - $parameterType instanceof \ReflectionNamedType => $parameterType->getName(), + $parameterType instanceof ReflectionNamedType => $parameterType->getName(), default => null, }; } From 2de5bcb732fe9b051f0ac63e9914d375109b9cf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20M=C3=BCller?= Date: Thu, 13 Feb 2025 15:30:42 +0100 Subject: [PATCH 6/9] Remove getFrozenPackages implementation The method was deprecated and the concept gutted, this would return empty array anyways. --- Neos.Flow/Classes/Package/PackageManager.php | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/Neos.Flow/Classes/Package/PackageManager.php b/Neos.Flow/Classes/Package/PackageManager.php index c3de0490fb..c9dfdc7084 100644 --- a/Neos.Flow/Classes/Package/PackageManager.php +++ b/Neos.Flow/Classes/Package/PackageManager.php @@ -232,19 +232,7 @@ public function getAvailablePackages(): array */ public function getFrozenPackages(): array { - $frozenPackages = []; - if ($this->bootstrap->getContext()->isDevelopment()) { - /** @var PackageInterface $package */ - foreach ($this->packages as $packageKey => $package) { - if (isset($this->packageStatesConfiguration['packages'][$package->getComposerName()]['frozen']) && - $this->packageStatesConfiguration['packages'][$package->getComposerName()]['frozen'] === true - ) { - $frozenPackages[$packageKey] = $package; - } - } - } - - return $frozenPackages; + return []; } /** From 5ff89d5ee34f939c28d0287d5d101558c9e9465b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20M=C3=BCller?= Date: Thu, 13 Feb 2025 15:31:23 +0100 Subject: [PATCH 7/9] Apply suggestions from code review Co-authored-by: Karsten Dambekalns --- Neos.Flow/Tests/Functional/Reflection/ReflectionServiceTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Neos.Flow/Tests/Functional/Reflection/ReflectionServiceTest.php b/Neos.Flow/Tests/Functional/Reflection/ReflectionServiceTest.php index a306e8ab96..237bdc031e 100644 --- a/Neos.Flow/Tests/Functional/Reflection/ReflectionServiceTest.php +++ b/Neos.Flow/Tests/Functional/Reflection/ReflectionServiceTest.php @@ -57,7 +57,7 @@ public function classSchemaCanBeBuiltForAggregateRootsWithPlainOldPhpBaseClasses /** * @test * @throws - * @deprecated + * @deprecated since 8.4 */ public function theReflectionServiceCorrectlyBuildsMethodTagsValues(): void { From 4335da78a7f4bdd4c6542339e0323d833fa5eb78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20M=C3=BCller?= Date: Thu, 13 Feb 2025 15:42:28 +0100 Subject: [PATCH 8/9] Update Neos.Flow/Classes/Reflection/ReflectionService.php Co-authored-by: Karsten Dambekalns --- Neos.Flow/Classes/Reflection/ReflectionService.php | 1 - 1 file changed, 1 deletion(-) diff --git a/Neos.Flow/Classes/Reflection/ReflectionService.php b/Neos.Flow/Classes/Reflection/ReflectionService.php index d4186300ab..19242a074e 100644 --- a/Neos.Flow/Classes/Reflection/ReflectionService.php +++ b/Neos.Flow/Classes/Reflection/ReflectionService.php @@ -1232,7 +1232,6 @@ public function reflectClassProperty(string $className, PropertyReflection $prop $visibility = $this->extractVisibility($property); $this->classReflectionData[$className][self::DATA_CLASS_PROPERTIES][$propertyName][self::DATA_PROPERTY_VISIBILITY] = $visibility; - foreach ($property->getTagsValues() as $tagName => $tagValues) { $tagValues = $this->reflectPropertyTag($className, $property, $tagName, $tagValues); if ($tagValues === null) { From 82452e8bd1121949f963b8efd625a9f86866d8f4 Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Thu, 13 Feb 2025 17:25:18 +0100 Subject: [PATCH 9/9] TASK: Global php space symbols should be used fully qualified via backslash As discussed in https://neos-project.slack.com/archives/C04Q0TC15/p1683558218323329 And voted (8 vs 5) https://neos-project.slack.com/archives/C04Q0TC15/p1683558099679779 We want to use FQN instead --- .../Classes/Reflection/ReflectionService.php | 134 ++++++++---------- 1 file changed, 63 insertions(+), 71 deletions(-) diff --git a/Neos.Flow/Classes/Reflection/ReflectionService.php b/Neos.Flow/Classes/Reflection/ReflectionService.php index 19242a074e..1067650605 100644 --- a/Neos.Flow/Classes/Reflection/ReflectionService.php +++ b/Neos.Flow/Classes/Reflection/ReflectionService.php @@ -17,8 +17,6 @@ use Doctrine\Common\Annotations\Reader; use Doctrine\ORM\Mapping as ORM; use Doctrine\Persistence\Proxy as DoctrineProxy; -use Error; -use InvalidArgumentException; use Neos\Cache\Exception; use Neos\Cache\Frontend\VariableFrontend; use Neos\Flow\Annotations as Flow; @@ -33,12 +31,6 @@ use Neos\Utility\TypeHandling; use Psr\Log\LoggerInterface; use Psr\Log\LogLevel; -use ReflectionException; -use ReflectionIntersectionType; -use ReflectionNamedType; -use ReflectionType; -use ReflectionUnionType; -use RuntimeException; /** * A service for acquiring reflection based information in a performant way. This @@ -229,7 +221,7 @@ protected function initialize(): void * @throws InvalidClassException * @throws InvalidPropertyTypeException * @throws InvalidValueObjectException - * @throws ReflectionException + * @throws \ReflectionException */ public function buildReflectionData(array $availableClassNames): void { @@ -282,13 +274,13 @@ public function getAllClassNames(): array * @return string|bool * @throws ClassLoadingForReflectionFailedException * @throws InvalidClassException - * @throws ReflectionException + * @throws \ReflectionException * @api */ public function getDefaultImplementationClassNameForInterface(string $interfaceName): string|bool { if (interface_exists($interfaceName) === false) { - throw new InvalidArgumentException('"' . $interfaceName . '" does not exist or is not the name of an interface.', 1238769559); + throw new \InvalidArgumentException('"' . $interfaceName . '" does not exist or is not the name of an interface.', 1238769559); } $interfaceName = $this->prepareClassReflectionForUsage($interfaceName); @@ -318,13 +310,13 @@ public function getDefaultImplementationClassNameForInterface(string $interfaceN * @return array * @throws ClassLoadingForReflectionFailedException * @throws InvalidClassException - * @throws ReflectionException + * @throws \ReflectionException * @api */ public function getAllImplementationClassNamesForInterface(string $interfaceName): array { if (interface_exists($interfaceName) === false) { - throw new InvalidArgumentException('"' . $interfaceName . '" does not exist or is not the name of an interface.', 1238769560); + throw new \InvalidArgumentException('"' . $interfaceName . '" does not exist or is not the name of an interface.', 1238769560); } $interfaceName = $this->prepareClassReflectionForUsage($interfaceName); @@ -339,13 +331,13 @@ public function getAllImplementationClassNamesForInterface(string $interfaceName * @return array * @throws ClassLoadingForReflectionFailedException * @throws InvalidClassException - * @throws ReflectionException + * @throws \ReflectionException * @api */ public function getAllSubClassNamesForClass(string $className): array { if (class_exists($className) === false) { - throw new InvalidArgumentException('"' . $className . '" does not exist or is not the name of a class.', 1257168042); + 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]) : []; @@ -390,7 +382,7 @@ public function isClassAnnotatedWith(string $className, string $annotationClassN * @return array * @throws ClassLoadingForReflectionFailedException * @throws InvalidClassException - * @throws ReflectionException + * @throws \ReflectionException */ public function getClassAnnotations(string $className, string|null $annotationClassName = null): array { @@ -425,7 +417,7 @@ public function getClassAnnotations(string $className, string|null $annotationCl * @return object|null * @throws ClassLoadingForReflectionFailedException * @throws InvalidClassException - * @throws ReflectionException + * @throws \ReflectionException */ public function getClassAnnotation(string $className, string $annotationClassName): ?object { @@ -442,7 +434,7 @@ public function getClassAnnotation(string $className, string $annotationClassNam * * @throws ClassLoadingForReflectionFailedException * @throws InvalidClassException - * @throws ReflectionException + * @throws \ReflectionException * @api */ public function isClassImplementationOf(string $className, string $interfaceName): bool @@ -461,7 +453,7 @@ public function isClassImplementationOf(string $className, string $interfaceName * @param class-string $className * @throws ClassLoadingForReflectionFailedException * @throws InvalidClassException - * @throws ReflectionException + * @throws \ReflectionException * @api */ public function isClassAbstract(string $className): bool @@ -476,7 +468,7 @@ public function isClassAbstract(string $className): bool * @param class-string $className Name of the class to analyze * @throws ClassLoadingForReflectionFailedException * @throws InvalidClassException - * @throws ReflectionException + * @throws \ReflectionException * @api */ public function isClassFinal(string $className): bool @@ -492,7 +484,7 @@ public function isClassFinal(string $className): bool * @return bool true if the class is readonly, otherwise false * @throws ClassLoadingForReflectionFailedException * @throws InvalidClassException - * @throws ReflectionException + * @throws \ReflectionException * @api */ public function isClassReadonly(string $className): bool @@ -549,7 +541,7 @@ public function getMethodsAnnotatedWith(string $className, string $annotationCla * @return bool * @throws ClassLoadingForReflectionFailedException * @throws InvalidClassException - * @throws ReflectionException + * @throws \ReflectionException * @api */ public function isMethodFinal(string $className, string $methodName): bool @@ -564,7 +556,7 @@ public function isMethodFinal(string $className, string $methodName): bool * @param class-string $className * @throws ClassLoadingForReflectionFailedException * @throws InvalidClassException - * @throws ReflectionException + * @throws \ReflectionException * @api */ public function isMethodStatic(string $className, string $methodName): bool @@ -577,7 +569,7 @@ public function isMethodStatic(string $className, string $methodName): bool * @param class-string $className * @throws ClassLoadingForReflectionFailedException * @throws InvalidClassException - * @throws ReflectionException + * @throws \ReflectionException * @api */ public function isMethodPublic(string $className, string $methodName): bool @@ -590,7 +582,7 @@ public function isMethodPublic(string $className, string $methodName): bool * @param class-string $className * @throws ClassLoadingForReflectionFailedException * @throws InvalidClassException - * @throws ReflectionException + * @throws \ReflectionException * @api */ public function isMethodProtected(string $className, string $methodName): bool @@ -603,7 +595,7 @@ public function isMethodProtected(string $className, string $methodName): bool * @param class-string $className * @throws ClassLoadingForReflectionFailedException * @throws InvalidClassException - * @throws ReflectionException + * @throws \ReflectionException * @api */ public function isMethodPrivate(string $className, string $methodName): bool @@ -615,7 +607,7 @@ public function isMethodPrivate(string $className, string $methodName): bool /** * Tells if the specified method is tagged with the given tag * - * @throws ReflectionException + * @throws \ReflectionException * @api */ public function isMethodTaggedWith(string $className, string $methodName, string $tag): bool @@ -646,7 +638,7 @@ public function isAttributeIgnored(string $attributeName): bool * @param string $methodName * @param class-string $annotationClassName * @return bool - * @throws ReflectionException + * @throws \ReflectionException * @api */ public function isMethodAnnotatedWith(string $className, string $methodName, string $annotationClassName): bool @@ -661,7 +653,7 @@ public function isMethodAnnotatedWith(string $className, string $methodName, str * @param class-string|null $annotationClassName * @return array * - * @throws ReflectionException + * @throws \ReflectionException * @api * */ @@ -706,7 +698,7 @@ public function getMethodAnnotations(string $className, string $methodName, stri * If multiple annotations are set on the target you will * get the first instance of them. * - * @throws ReflectionException + * @throws \ReflectionException */ public function getMethodAnnotation(string $className, string $methodName, string $annotationClassName): ?object { @@ -725,7 +717,7 @@ public function getMethodAnnotation(string $className, string $methodName, strin * @return array * @throws ClassLoadingForReflectionFailedException * @throws InvalidClassException - * @throws ReflectionException + * @throws \ReflectionException * @api */ public function getClassPropertyNames(string $className): array @@ -740,7 +732,7 @@ public function getClassPropertyNames(string $className): array * @param class-string $className * @throws ClassLoadingForReflectionFailedException * @throws InvalidClassException - * @throws ReflectionException + * @throws \ReflectionException * @api */ public function hasMethod(string $className, string $methodName): bool @@ -752,7 +744,7 @@ public function hasMethod(string $className, string $methodName): bool /** * Returns all tags and their values the specified method is tagged with * - * @throws ReflectionException + * @throws \ReflectionException * @deprecated since 8.4 */ public function getMethodTagsValues(string $className, string $methodName): array @@ -773,7 +765,7 @@ public function getMethodTagsValues(string $className, string $methodName): arra * @return array An array of parameter names and additional information or an empty array of no parameters were found * @throws ClassLoadingForReflectionFailedException * @throws InvalidClassException - * @throws ReflectionException + * @throws \ReflectionException * @api */ public function getMethodParameters(string $className, string $methodName): array @@ -793,7 +785,7 @@ public function getMethodParameters(string $className, string $methodName): arra * @return string|null The declared return type of the method or null if none was declared * @throws ClassLoadingForReflectionFailedException * @throws InvalidClassException - * @throws ReflectionException + * @throws \ReflectionException */ public function getMethodDeclaredReturnType(string $className, string $methodName): ?string { @@ -809,7 +801,7 @@ public function getMethodDeclaredReturnType(string $className, string $methodNam * @return array * @throws ClassLoadingForReflectionFailedException * @throws InvalidClassException - * @throws ReflectionException + * @throws \ReflectionException * @api * */ @@ -838,7 +830,7 @@ public function getPropertyNamesByTag(string $className, string $tag): array * @return array * @throws ClassLoadingForReflectionFailedException * @throws InvalidClassException - * @throws ReflectionException + * @throws \ReflectionException * @api */ public function getPropertyTagsValues(string $className, string $propertyName): array @@ -860,7 +852,7 @@ public function getPropertyTagsValues(string $className, string $propertyName): * @return array * @throws ClassLoadingForReflectionFailedException * @throws InvalidClassException - * @throws ReflectionException + * @throws \ReflectionException * @api * */ @@ -882,7 +874,7 @@ public function getPropertyTagValues(string $className, string $propertyName, st * @return string|null * @throws ClassLoadingForReflectionFailedException * @throws InvalidClassException - * @throws ReflectionException + * @throws \ReflectionException */ public function getPropertyType(string $className, string $propertyName): ?string { @@ -896,7 +888,7 @@ public function getPropertyType(string $className, string $propertyName): ?strin * @return bool * @throws ClassLoadingForReflectionFailedException * @throws InvalidClassException - * @throws ReflectionException + * @throws \ReflectionException * @api */ public function isPropertyPrivate(string $className, string $propertyName): bool @@ -915,7 +907,7 @@ public function isPropertyPrivate(string $className, string $propertyName): bool * @return bool * @throws ClassLoadingForReflectionFailedException * @throws InvalidClassException - * @throws ReflectionException + * @throws \ReflectionException * @api */ public function isPropertyTaggedWith(string $className, string $propertyName, string $tag): bool @@ -933,7 +925,7 @@ public function isPropertyTaggedWith(string $className, string $propertyName, st * @return bool * @throws ClassLoadingForReflectionFailedException * @throws InvalidClassException - * @throws ReflectionException + * @throws \ReflectionException * @api */ public function isPropertyAnnotatedWith(string $className, string $propertyName, string $annotationClassName): bool @@ -951,7 +943,7 @@ public function isPropertyAnnotatedWith(string $className, string $propertyName, * @return array * @throws ClassLoadingForReflectionFailedException * @throws InvalidClassException - * @throws ReflectionException + * @throws \ReflectionException * @api */ public function getPropertyNamesByAnnotation(string $className, string $annotationClassName): array @@ -980,7 +972,7 @@ public function getPropertyNamesByAnnotation(string $className, string $annotati * @return array * @throws ClassLoadingForReflectionFailedException * @throws InvalidClassException - * @throws ReflectionException + * @throws \ReflectionException * @api */ public function getPropertyAnnotations(string $className, string $propertyName, string $annotationClassName = null): array @@ -1009,7 +1001,7 @@ public function getPropertyAnnotations(string $className, string $propertyName, * @return object|null * @throws ClassLoadingForReflectionFailedException * @throws InvalidClassException - * @throws ReflectionException + * @throws \ReflectionException */ public function getPropertyAnnotation(string $className, string $propertyName, string $annotationClassName): ?object { @@ -1053,7 +1045,7 @@ public function getClassSchema(string|object $classNameOrObject): ?ClassSchema * @return string * @throws ClassLoadingForReflectionFailedException * @throws InvalidClassException - * @throws ReflectionException + * @throws \ReflectionException */ protected function prepareClassReflectionForUsage(string $className): string { @@ -1077,7 +1069,7 @@ protected function prepareClassReflectionForUsage(string $className): string * @throws InvalidClassException * @throws InvalidPropertyTypeException * @throws InvalidValueObjectException - * @throws ReflectionException + * @throws \ReflectionException */ protected function reflectEmergedClasses(): void { @@ -1147,7 +1139,7 @@ protected function isTagIgnored(string $tagName): bool * @param class-string $className * @throws ClassLoadingForReflectionFailedException * @throws InvalidClassException - * @throws ReflectionException + * @throws \ReflectionException */ protected function reflectClass(string $className): void { @@ -1250,8 +1242,8 @@ public function reflectClassProperty(string $className, PropertyReflection $prop } try { $attributeInstance = $attribute->newInstance(); - } catch (Error $error) { - throw new RuntimeException(sprintf('Attribute "%s" used in class "%s" was not found.', $attribute->getName(), $className), 1695635128, $error); + } catch (\Error $error) { + throw new \RuntimeException(sprintf('Attribute "%s" used in class "%s" was not found.', $attribute->getName(), $className), 1695635128, $error); } $this->classReflectionData[$className][self::DATA_CLASS_PROPERTIES][$propertyName][self::DATA_PROPERTY_ANNOTATIONS][$attribute->getName()][] = $attributeInstance; } @@ -1282,7 +1274,7 @@ protected function reflectPropertyTag(string $className, PropertyReflection $pro /** * @throws InvalidClassException * @throws ClassLoadingForReflectionFailedException - * @throws ReflectionException + * @throws \ReflectionException */ protected function addParentClass(string $className, ClassReflection $parentClass): void { @@ -1296,7 +1288,7 @@ protected function addParentClass(string $className, ClassReflection $parentClas /** * @throws ClassLoadingForReflectionFailedException * @throws InvalidClassException - * @throws ReflectionException + * @throws \ReflectionException */ protected function addImplementedInterface(string $className, ClassReflection $interface): void { @@ -1315,7 +1307,7 @@ protected function addImplementedInterface(string $className, ClassReflection $i /** * @param class-string $className * @param MethodReflection $method - * @throws ReflectionException + * @throws \ReflectionException */ protected function reflectClassMethod(string $className, MethodReflection $method): void { @@ -1487,7 +1479,7 @@ protected function getParentClasses(ClassReflection $class, array $parentClasses * @throws InvalidClassException * @throws InvalidPropertyTypeException * @throws InvalidValueObjectException - * @throws ReflectionException + * @throws \ReflectionException */ protected function buildClassSchemata(array $classNames): void { @@ -1507,7 +1499,7 @@ protected function buildClassSchemata(array $classNames): void * @throws InvalidClassException * @throws InvalidPropertyTypeException * @throws InvalidValueObjectException - * @throws ReflectionException + * @throws \ReflectionException */ protected function buildClassSchema(string $className): ClassSchema { @@ -1550,7 +1542,7 @@ protected function buildClassSchema(string $className): ClassSchema * @throws ClassSchemaConstraintViolationException * @throws InvalidClassException * @throws InvalidPropertyTypeException - * @throws ReflectionException + * @throws \ReflectionException */ protected function addPropertiesToClassSchema(ClassSchema $classSchema): void { @@ -1582,7 +1574,7 @@ protected function addPropertiesToClassSchema(ClassSchema $classSchema): void * @throws ClassSchemaConstraintViolationException * @throws InvalidClassException * @throws InvalidPropertyTypeException - * @throws ReflectionException + * @throws \ReflectionException */ protected function evaluateClassPropertyAnnotationsForSchema(ClassSchema $classSchema, string $propertyName): bool { @@ -1639,7 +1631,7 @@ protected function evaluateClassPropertyAnnotationsForSchema(ClassSchema $classS * @throws ClassLoadingForReflectionFailedException * @throws ClassSchemaConstraintViolationException * @throws InvalidClassException - * @throws ReflectionException + * @throws \ReflectionException */ protected function completeRepositoryAssignments(): void { @@ -1675,7 +1667,7 @@ protected function completeRepositoryAssignments(): void * @throws ClassLoadingForReflectionFailedException * @throws ClassSchemaConstraintViolationException * @throws InvalidClassException - * @throws ReflectionException + * @throws \ReflectionException */ protected function makeChildClassesAggregateRoot(ClassSchema $classSchema): void { @@ -1696,7 +1688,7 @@ protected function makeChildClassesAggregateRoot(ClassSchema $classSchema): void * @throws ClassLoadingForReflectionFailedException * @throws Exception * @throws InvalidClassException - * @throws ReflectionException + * @throws \ReflectionException */ protected function ensureAggregateRootInheritanceChainConsistency(): void { @@ -1826,7 +1818,7 @@ protected function convertParameterReflectionToArray(ParameterReflection $parame * @return void * @throws ClassLoadingForReflectionFailedException * @throws InvalidClassException - * @throws ReflectionException + * @throws \ReflectionException */ protected function forgetChangedClasses(): void { @@ -1845,7 +1837,7 @@ protected function forgetChangedClasses(): void * * @throws ClassLoadingForReflectionFailedException * @throws InvalidClassException - * @throws ReflectionException + * @throws \ReflectionException */ private function forgetClass(string $className): void { @@ -1912,7 +1904,7 @@ private function forgetClass(string $className): void * @param class-string $className * @throws ClassLoadingForReflectionFailedException * @throws InvalidClassException - * @throws ReflectionException + * @throws \ReflectionException */ private function loadOrReflectClassIfNecessary(string $className): void { @@ -2005,28 +1997,28 @@ protected function log(string $message, string $severity = LogLevel::INFO, array } /** - * @param ReflectionType|null $parameterType + * @param \ReflectionType|null $parameterType * @return string|null */ - private function renderParameterType(?ReflectionType $parameterType = null): string|null + private function renderParameterType(?\ReflectionType $parameterType = null): string|null { return match (true) { - $parameterType instanceof ReflectionUnionType => implode('|', array_map( - function (ReflectionNamedType | ReflectionIntersectionType $type) { - if ($type instanceof ReflectionNamedType) { + $parameterType instanceof \ReflectionUnionType => implode('|', array_map( + function (\ReflectionNamedType|\ReflectionIntersectionType $type) { + if ($type instanceof \ReflectionNamedType) { return $type->getName(); } return '(' . $this->renderParameterType($type) . ')'; }, $parameterType->getTypes() )), - $parameterType instanceof ReflectionIntersectionType => implode('&', array_map( - function (ReflectionNamedType $type) { + $parameterType instanceof \ReflectionIntersectionType => implode('&', array_map( + function (\ReflectionNamedType $type) { return $this->renderParameterType($type); }, $parameterType->getTypes() )), - $parameterType instanceof ReflectionNamedType => $parameterType->getName(), + $parameterType instanceof \ReflectionNamedType => $parameterType->getName(), default => null, }; }