[TASK] Add require() to php capable cache backend 86/57686/6
authorChristian Kuhn <lolli@schwarzbu.ch>
Tue, 5 Jun 2018 23:12:07 +0000 (01:12 +0200)
committerAndreas Fernandez <a.fernandez@scripting-base.de>
Wed, 25 Jul 2018 20:01:24 +0000 (22:01 +0200)
To allow loading php cache files more than once it is
necessary to add a require() method next to requireOnce()
to cache backends. This can be used if cache entries do
not execute one-time-per-process-only code, if they for
instance do not declare classes. This is needed to execute
for instance the functional test suite multiple times in
one process and to still make use of caching.
The cache_core php cache is affected by this: Those calls
are changed to require() instead of requireOnce() to make
them multi-loadable per request.
Note require() is not yet added to the PhpCapableBackendInterface
since that would be breaking, but marked as todo for v10.0

Resolves: #85648
Releases: master
Change-Id: Id711044e9554587dba38f976bd1c5bab23826de2
Reviewed-on: https://review.typo3.org/57686
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Wouter Wolters <typo3@wouterwolters.nl>
Tested-by: Wouter Wolters <typo3@wouterwolters.nl>
Reviewed-by: Andreas Fernandez <a.fernandez@scripting-base.de>
Tested-by: Andreas Fernandez <a.fernandez@scripting-base.de>
12 files changed:
typo3/sysext/core/Classes/Cache/Backend/FileBackend.php
typo3/sysext/core/Classes/Cache/Backend/NullBackend.php
typo3/sysext/core/Classes/Cache/Backend/PhpCapableBackendInterface.php
typo3/sysext/core/Classes/Cache/Backend/SimpleFileBackend.php
typo3/sysext/core/Classes/Cache/Frontend/PhpFrontend.php
typo3/sysext/core/Classes/Http/MiddlewareStackResolver.php
typo3/sysext/core/Classes/Package/PackageManager.php
typo3/sysext/core/Classes/Utility/ExtensionManagementUtility.php
typo3/sysext/core/Tests/Unit/Cache/Backend/FileBackendTest.php
typo3/sysext/core/Tests/Unit/Cache/Frontend/PhpFrontendTest.php
typo3/sysext/core/Tests/Unit/Utility/ExtensionManagementUtilityTest.php
typo3/sysext/lowlevel/Classes/Controller/ConfigurationController.php

index 3adc246..c70cd96 100644 (file)
@@ -402,4 +402,27 @@ class FileBackend extends SimpleFileBackend implements FreezableBackendInterface
         $pathAndFilename = $this->cacheDirectory . $entryIdentifier . $this->cacheEntryFileExtension;
         return $this->isCacheFileExpired($pathAndFilename) ? false : require_once $pathAndFilename;
     }
+
+    /**
+     * Loads PHP code from the cache and require it right away.
+     *
+     * @param string $entryIdentifier An identifier which describes the cache entry to load
+     * @throws \InvalidArgumentException
+     * @return mixed Potential return value from the include operation
+     * @api
+     */
+    public function require(string $entryIdentifier)
+    {
+        if ($this->frozen) {
+            if (isset($this->cacheEntryIdentifiers[$entryIdentifier])) {
+                return require $this->cacheDirectory . $entryIdentifier . $this->cacheEntryFileExtension;
+            }
+            return false;
+        }
+        if ($entryIdentifier !== PathUtility::basename($entryIdentifier)) {
+            throw new \InvalidArgumentException('The specified entry identifier must not contain a path segment.', 1532528246);
+        }
+        $pathAndFilename = $this->cacheDirectory . $entryIdentifier . $this->cacheEntryFileExtension;
+        return $this->isCacheFileExpired($pathAndFilename) ? false : require $pathAndFilename;
+    }
 }
index 62cc4c6..8acc596 100644 (file)
@@ -128,4 +128,14 @@ class NullBackend extends AbstractBackend implements PhpCapableBackendInterface,
     public function requireOnce($identifier)
     {
     }
+
+    /**
+     * Does nothing
+     *
+     * @param string $identifier An identifier which describes the cache entry to load
+     * @api
+     */
+    public function require(string $identifier)
+    {
+    }
 }
index b71cc2b..c207c1b 100644 (file)
@@ -30,4 +30,6 @@ interface PhpCapableBackendInterface extends BackendInterface
      * @api
      */
     public function requireOnce($entryIdentifier);
+
+    // @todo: Add require() as breaking patch in v10.0 to the interface
 }
index 3c73591..ea4ff39 100644 (file)
@@ -362,4 +362,21 @@ class SimpleFileBackend extends AbstractBackend implements PhpCapableBackendInte
         }
         return file_exists($pathAndFilename) ? require_once $pathAndFilename : false;
     }
+
+    /**
+     * Loads PHP code from the cache and require it right away.
+     *
+     * @param string $entryIdentifier An identifier which describes the cache entry to load
+     * @return mixed Potential return value from the include operation
+     * @throws \InvalidArgumentException
+     * @api
+     */
+    public function require(string $entryIdentifier)
+    {
+        $pathAndFilename = $this->cacheDirectory . $entryIdentifier . $this->cacheEntryFileExtension;
+        if ($entryIdentifier !== PathUtility::basename($entryIdentifier)) {
+            throw new \InvalidArgumentException('The specified entry identifier must not contain a path segment.', 1532528267);
+        }
+        return file_exists($pathAndFilename) ? require $pathAndFilename : false;
+    }
 }
index 9baebfd..ee5b895 100644 (file)
@@ -113,4 +113,19 @@ class PhpFrontend extends AbstractFrontend
     {
         return $this->backend->requireOnce($entryIdentifier);
     }
+
+    /**
+     * Loads PHP code from the cache and require() it right away. Note require()
+     * in comparison to requireOnce() is only "safe" if the cache entry only contain stuff
+     * that can be required multiple times during one request. For instance a class definition
+     * would fail here.
+     *
+     * @param string $entryIdentifier An identifier which describes the cache entry to load
+     * @return mixed Potential return value from the include operation
+     * @api
+     */
+    public function require(string $entryIdentifier)
+    {
+        return $this->backend->require($entryIdentifier);
+    }
 }
index 8d33e41..0163d73 100644 (file)
@@ -64,7 +64,7 @@ class MiddlewareStackResolver
         // Check if the registered middlewares from all active packages have already been cached
         $cacheIdentifier = $this->getCacheIdentifier($stackName);
         if ($this->cache->has($cacheIdentifier)) {
-            return $this->cache->requireOnce($cacheIdentifier);
+            return $this->cache->require($cacheIdentifier);
         }
 
         $allMiddlewares = $this->loadConfiguration();
index c632c63..9df6c56 100644 (file)
@@ -207,7 +207,7 @@ class PackageManager implements SingletonInterface
     protected function loadPackageManagerStatesFromCache()
     {
         $cacheEntryIdentifier = $this->getCacheEntryIdentifier();
-        if ($cacheEntryIdentifier === null || !$this->coreCache->has($cacheEntryIdentifier) || !($packageCache = $this->coreCache->requireOnce($cacheEntryIdentifier))) {
+        if ($cacheEntryIdentifier === null || !$this->coreCache->has($cacheEntryIdentifier) || !($packageCache = $this->coreCache->require($cacheEntryIdentifier))) {
             throw new Exception\PackageManagerCacheUnavailableException('The package state cache could not be loaded.', 1393883342);
         }
         $this->packageStatesConfiguration = $packageCache['packageStatesConfiguration'];
index e2894e8..c6af44f 100644 (file)
@@ -1534,7 +1534,7 @@ tt_content.' . $key . $suffix . ' {
             /** @var $codeCache \TYPO3\CMS\Core\Cache\Frontend\FrontendInterface */
             $codeCache = self::getCacheManager()->getCache('cache_core');
             if ($codeCache->has($cacheIdentifier)) {
-                $codeCache->requireOnce($cacheIdentifier);
+                $codeCache->require($cacheIdentifier);
             } else {
                 self::loadSingleExtLocalconfFiles();
                 self::createExtLocalconfCacheEntry();
@@ -1632,7 +1632,7 @@ tt_content.' . $key . $suffix . ' {
             $cacheIdentifier = static::getBaseTcaCacheIdentifier();
             /** @var $codeCache \TYPO3\CMS\Core\Cache\Frontend\FrontendInterface */
             $codeCache = static::getCacheManager()->getCache('cache_core');
-            $cacheData = $codeCache->requireOnce($cacheIdentifier);
+            $cacheData = $codeCache->require($cacheIdentifier);
             if ($cacheData) {
                 $GLOBALS['TCA'] = $cacheData['tca'];
                 GeneralUtility::setSingletonInstance(
@@ -1778,7 +1778,7 @@ tt_content.' . $key . $suffix . ' {
             /** @var $codeCache \TYPO3\CMS\Core\Cache\Frontend\FrontendInterface */
             $codeCache = self::getCacheManager()->getCache('cache_core');
             if ($codeCache->has($cacheIdentifier)) {
-                $codeCache->requireOnce($cacheIdentifier);
+                $codeCache->require($cacheIdentifier);
             } else {
                 self::loadSingleExtTablesFiles();
                 self::createExtTablesCacheEntry();
index 21382e8..55c706e 100644 (file)
@@ -696,6 +696,93 @@ class FileBackendTest extends UnitTestCase
 
     /**
      * @test
+     * @dataProvider invalidEntryIdentifiers
+     * @param string $identifier
+     */
+    public function requireThrowsExceptionForInvalidIdentifier(string $identifier): void
+    {
+        $this->expectException(\InvalidArgumentException::class);
+        $this->expectExceptionCode(1532528246);
+        $mockCache = $this->createMock(AbstractFrontend::class);
+        $mockCache->expects($this->atLeastOnce())->method('getIdentifier')->will($this->returnValue('UnitTestCache'));
+
+        $backend = $this->getMockBuilder(FileBackend::class)
+            ->setMethods(['dummy'])
+            ->disableOriginalConstructor()
+            ->getMock();
+        $backend->setCacheDirectory('vfs://Foo/');
+        $backend->setCache($mockCache);
+
+        $backend->require($identifier);
+    }
+
+    /**
+     * @test
+     */
+    public function requireIncludesAndReturnsResultOfIncludedPhpFile(): void
+    {
+        $mockCache = $this->createMock(AbstractFrontend::class);
+        $mockCache->expects($this->atLeastOnce())->method('getIdentifier')->will($this->returnValue('UnitTestCache'));
+        $backend = $this->getMockBuilder(FileBackend::class)
+            ->setMethods(['dummy'])
+            ->disableOriginalConstructor()
+            ->getMock();
+        $backend->setCacheDirectory('vfs://Foo/');
+        $backend->setCache($mockCache);
+
+        $entryIdentifier = 'SomePhpEntry2';
+
+        $data = '<?php return "foo2"; ?>';
+        $backend->set($entryIdentifier, $data);
+
+        $loadedData = $backend->require($entryIdentifier);
+        $this->assertEquals('foo2', $loadedData);
+    }
+
+    /**
+     * @test
+     */
+    public function requireDoesNotCheckExpiryTimeIfBackendIsFrozen(): void
+    {
+        $mockCache = $this->createMock(AbstractFrontend::class);
+        $mockCache->expects($this->atLeastOnce())->method('getIdentifier')->will($this->returnValue('UnitTestCache'));
+        $backend = $this->getMockBuilder(FileBackend::class)
+            ->setMethods(['isCacheFileExpired'])
+            ->disableOriginalConstructor()
+            ->getMock();
+        $backend->setCacheDirectory('vfs://Foo/');
+        $backend->setCache($mockCache);
+
+        $backend->expects($this->once())->method('isCacheFileExpired'); // Indirectly called by freeze() -> get()
+
+        $data = '<?php return "foo"; ?>';
+        $backend->set('FooEntry2', $data);
+
+        $backend->freeze();
+
+        $loadedData = $backend->require('FooEntry2');
+        $this->assertEquals('foo', $loadedData);
+    }
+
+    /**
+     * @test
+     */
+    public function requireCanLoadSameEntryMultipleTimes(): void
+    {
+        $frontendProphecy = $this->prophesize(AbstractFrontend::class);
+        $frontendProphecy->getIdentifier()->willReturn('UnitTestCache');
+        $subject = new FileBackend('Testing');
+        $subject->setCacheDirectory('vfs://Foo/');
+        $subject->setCache($frontendProphecy->reveal());
+        $subject->set('BarEntry', '<?php return "foo"; ?>');
+        $loadedData = $subject->require('BarEntry');
+        $this->assertEquals('foo', $loadedData);
+        $loadedData = $subject->require('BarEntry');
+        $this->assertEquals('foo', $loadedData);
+    }
+
+    /**
+     * @test
      * @throws Exception
      */
     public function findIdentifiersByTagFindsCacheEntriesWithSpecifiedTag(): void
index d1e7f96..3f2618e 100644 (file)
@@ -75,4 +75,16 @@ class PhpFrontendTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase
         $result = $cache->requireOnce('Foo-Bar');
         $this->assertSame('hello world!', $result);
     }
+
+    /**
+     * @test
+     */
+    public function requireCallsTheBackendsRequireMethod()
+    {
+        $mockBackend = $this->createMock(\TYPO3\CMS\Core\Cache\Backend\SimpleFileBackend::class);
+        $mockBackend->expects($this->once())->method('require')->with('Foo-Bar')->will($this->returnValue('hello world!'));
+        $cache = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Core\Cache\Frontend\PhpFrontend::class, 'PhpFrontend', $mockBackend);
+        $result = $cache->require('Foo-Bar');
+        $this->assertSame('hello world!', $result);
+    }
 }
index 54d2296..6e1f83c 100644 (file)
@@ -1216,7 +1216,7 @@ class ExtensionManagementUtilityTest extends UnitTestCase
     public function loadExtLocalconfRequiresCacheFileIfExistsAndCachingIsAllowed()
     {
         $mockCache = $this->getMockBuilder(PhpFrontend::class)
-            ->setMethods(['getIdentifier', 'set', 'get', 'getByTag', 'has', 'remove', 'flush', 'flushByTag', 'requireOnce'])
+            ->setMethods(['getIdentifier', 'set', 'get', 'getByTag', 'has', 'remove', 'flush', 'flushByTag', 'require'])
             ->disableOriginalConstructor()
             ->getMock();
 
@@ -1227,7 +1227,7 @@ class ExtensionManagementUtilityTest extends UnitTestCase
         $mockCacheManager->expects($this->any())->method('getCache')->will($this->returnValue($mockCache));
         ExtensionManagementUtilityAccessibleProxy::setCacheManager($mockCacheManager);
         $mockCache->expects($this->any())->method('has')->will($this->returnValue(true));
-        $mockCache->expects($this->once())->method('requireOnce');
+        $mockCache->expects($this->once())->method('require');
         ExtensionManagementUtility::loadExtLocalconf(true);
     }
 
@@ -1340,7 +1340,7 @@ class ExtensionManagementUtilityTest extends UnitTestCase
         file_put_contents($extLocalconfLocation, "<?php\n\n" . $uniqueStringInLocalconf . "\n\n?>");
         $GLOBALS['TYPO3_LOADED_EXT'] = new LoadedExtensionsArray($packageManager);
         $mockCache = $this->getMockBuilder(PhpFrontend::class)
-            ->setMethods(['getIdentifier', 'set', 'get', 'getByTag', 'has', 'remove', 'flush', 'flushByTag', 'requireOnce'])
+            ->setMethods(['getIdentifier', 'set', 'get', 'getByTag', 'has', 'remove', 'flush', 'flushByTag', 'require'])
             ->disableOriginalConstructor()
             ->getMock();
 
@@ -1363,7 +1363,7 @@ class ExtensionManagementUtilityTest extends UnitTestCase
         $packageManager = $this->createMockPackageManagerWithMockPackage($extensionName);
         $GLOBALS['TYPO3_LOADED_EXT'] = new LoadedExtensionsArray($packageManager);
         $mockCache = $this->getMockBuilder(PhpFrontend::class)
-            ->setMethods(['getIdentifier', 'set', 'get', 'getByTag', 'has', 'remove', 'flush', 'flushByTag', 'requireOnce'])
+            ->setMethods(['getIdentifier', 'set', 'get', 'getByTag', 'has', 'remove', 'flush', 'flushByTag', 'require'])
             ->disableOriginalConstructor()
             ->getMock();
 
@@ -1385,7 +1385,7 @@ class ExtensionManagementUtilityTest extends UnitTestCase
     public function createExtLocalconfCacheEntryWritesCacheEntryWithNoTags()
     {
         $mockCache = $this->getMockBuilder(PhpFrontend::class)
-            ->setMethods(['getIdentifier', 'set', 'get', 'getByTag', 'has', 'remove', 'flush', 'flushByTag', 'requireOnce'])
+            ->setMethods(['getIdentifier', 'set', 'get', 'getByTag', 'has', 'remove', 'flush', 'flushByTag', 'require'])
             ->disableOriginalConstructor()
             ->getMock();
 
@@ -1439,7 +1439,7 @@ class ExtensionManagementUtilityTest extends UnitTestCase
     public function loadBaseTcaRequiresCacheFileIfExistsAndCachingIsAllowed()
     {
         $mockCache = $this->getMockBuilder(PhpFrontend::class)
-            ->setMethods(['getIdentifier', 'set', 'get', 'getByTag', 'has', 'remove', 'flush', 'flushByTag', 'requireOnce'])
+            ->setMethods(['getIdentifier', 'set', 'get', 'getByTag', 'has', 'remove', 'flush', 'flushByTag', 'require'])
             ->disableOriginalConstructor()
             ->getMock();
 
@@ -1449,7 +1449,7 @@ class ExtensionManagementUtilityTest extends UnitTestCase
             ->getMock();
         $mockCacheManager->expects($this->any())->method('getCache')->will($this->returnValue($mockCache));
         ExtensionManagementUtilityAccessibleProxy::setCacheManager($mockCacheManager);
-        $mockCache->expects($this->once())->method('requireOnce')->willReturn(['tca' => [], 'categoryRegistry' => \serialize(CategoryRegistry::getInstance())]);
+        $mockCache->expects($this->once())->method('require')->willReturn(['tca' => [], 'categoryRegistry' => \serialize(CategoryRegistry::getInstance())]);
         ExtensionManagementUtilityAccessibleProxy::loadBaseTca(true);
     }
 
@@ -1471,7 +1471,7 @@ class ExtensionManagementUtilityTest extends UnitTestCase
         $tableConfiguration = '<?php return array(\'foo\' => \'' . $uniqueStringInTableConfiguration . '\'); ?>';
         file_put_contents($packagePath . 'Configuration/TCA/' . $uniqueTableName . '.php', $tableConfiguration);
         $mockCache = $this->getMockBuilder(PhpFrontend::class)
-            ->setMethods(['getIdentifier', 'set', 'get', 'getByTag', 'has', 'remove', 'flush', 'flushByTag', 'requireOnce'])
+            ->setMethods(['getIdentifier', 'set', 'get', 'getByTag', 'has', 'remove', 'flush', 'flushByTag', 'require'])
             ->disableOriginalConstructor()
             ->getMock();
 
@@ -1481,7 +1481,7 @@ class ExtensionManagementUtilityTest extends UnitTestCase
             ->getMock();
         $mockCacheManager->expects($this->any())->method('getCache')->will($this->returnValue($mockCache));
         ExtensionManagementUtilityAccessibleProxy::setCacheManager($mockCacheManager);
-        $mockCache->expects($this->once())->method('requireOnce')->will($this->returnValue(false));
+        $mockCache->expects($this->once())->method('require')->will($this->returnValue(false));
         $mockCache->expects($this->once())->method('set')->with($this->anything(), $this->stringContains($uniqueStringInTableConfiguration), $this->anything());
         ExtensionManagementUtility::loadBaseTca(true);
     }
@@ -1492,7 +1492,7 @@ class ExtensionManagementUtilityTest extends UnitTestCase
     public function loadBaseTcaWritesCacheEntryWithNoTags()
     {
         $mockCache = $this->getMockBuilder(PhpFrontend::class)
-            ->setMethods(['getIdentifier', 'set', 'get', 'getByTag', 'has', 'remove', 'flush', 'flushByTag', 'requireOnce'])
+            ->setMethods(['getIdentifier', 'set', 'get', 'getByTag', 'has', 'remove', 'flush', 'flushByTag', 'require'])
             ->disableOriginalConstructor()
             ->getMock();
 
@@ -1502,7 +1502,7 @@ class ExtensionManagementUtilityTest extends UnitTestCase
             ->getMock();
         $mockCacheManager->expects($this->any())->method('getCache')->will($this->returnValue($mockCache));
         ExtensionManagementUtilityAccessibleProxy::setCacheManager($mockCacheManager);
-        $mockCache->expects($this->once())->method('requireOnce')->will($this->returnValue(false));
+        $mockCache->expects($this->once())->method('require')->will($this->returnValue(false));
         $mockCache->expects($this->once())->method('set')->with($this->anything(), $this->anything(), $this->equalTo([]));
         ExtensionManagementUtilityAccessibleProxy::loadBaseTca();
     }
@@ -1547,7 +1547,7 @@ class ExtensionManagementUtilityTest extends UnitTestCase
     public function loadExtTablesRequiresCacheFileIfExistsAndCachingIsAllowed()
     {
         $mockCache = $this->getMockBuilder(PhpFrontend::class)
-            ->setMethods(['getIdentifier', 'set', 'get', 'getByTag', 'has', 'remove', 'flush', 'flushByTag', 'requireOnce'])
+            ->setMethods(['getIdentifier', 'set', 'get', 'getByTag', 'has', 'remove', 'flush', 'flushByTag', 'require'])
             ->disableOriginalConstructor()
             ->getMock();
 
@@ -1558,7 +1558,7 @@ class ExtensionManagementUtilityTest extends UnitTestCase
         $mockCacheManager->expects($this->any())->method('getCache')->will($this->returnValue($mockCache));
         ExtensionManagementUtilityAccessibleProxy::setCacheManager($mockCacheManager);
         $mockCache->expects($this->any())->method('has')->will($this->returnValue(true));
-        $mockCache->expects($this->once())->method('requireOnce');
+        $mockCache->expects($this->once())->method('require');
         // Reset the internal cache access tracking variable of extMgm
         // This method is only in the ProxyClass!
         ExtensionManagementUtilityAccessibleProxy::resetExtTablesWasReadFromCacheOnceBoolean();
@@ -1584,7 +1584,7 @@ class ExtensionManagementUtilityTest extends UnitTestCase
             ]
         ];
         $mockCache = $this->getMockBuilder(PhpFrontend::class)
-            ->setMethods(['getIdentifier', 'set', 'get', 'getByTag', 'has', 'remove', 'flush', 'flushByTag', 'requireOnce'])
+            ->setMethods(['getIdentifier', 'set', 'get', 'getByTag', 'has', 'remove', 'flush', 'flushByTag', 'require'])
             ->disableOriginalConstructor()
             ->getMock();
 
@@ -1608,7 +1608,7 @@ class ExtensionManagementUtilityTest extends UnitTestCase
             $extensionName => [],
         ];
         $mockCache = $this->getMockBuilder(PhpFrontend::class)
-            ->setMethods(['getIdentifier', 'set', 'get', 'getByTag', 'has', 'remove', 'flush', 'flushByTag', 'requireOnce'])
+            ->setMethods(['getIdentifier', 'set', 'get', 'getByTag', 'has', 'remove', 'flush', 'flushByTag', 'require'])
             ->disableOriginalConstructor()
             ->getMock();
 
@@ -1630,7 +1630,7 @@ class ExtensionManagementUtilityTest extends UnitTestCase
     public function createExtTablesCacheEntryWritesCacheEntryWithNoTags()
     {
         $mockCache = $this->getMockBuilder(PhpFrontend::class)
-            ->setMethods(['getIdentifier', 'set', 'get', 'getByTag', 'has', 'remove', 'flush', 'flushByTag', 'requireOnce'])
+            ->setMethods(['getIdentifier', 'set', 'get', 'getByTag', 'has', 'remove', 'flush', 'flushByTag', 'require'])
             ->disableOriginalConstructor()
             ->getMock();
 
index 50c9e32..e6b647b 100644 (file)
@@ -21,8 +21,7 @@ use TYPO3\CMS\Backend\Configuration\SiteTcaConfiguration;
 use TYPO3\CMS\Backend\Routing\Router;
 use TYPO3\CMS\Backend\Template\ModuleTemplate;
 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
-use TYPO3\CMS\Core\Cache\Backend\NullBackend;
-use TYPO3\CMS\Core\Cache\Frontend\PhpFrontend;
+use TYPO3\CMS\Core\Cache\CacheManager;
 use TYPO3\CMS\Core\Http\HtmlResponse;
 use TYPO3\CMS\Core\Http\MiddlewareStackResolver;
 use TYPO3\CMS\Core\Localization\LanguageService;
@@ -225,18 +224,11 @@ class ConfigurationController
         } elseif ($selectedTreeDetails['type'] === 'httpMiddlewareStacks') {
             // Keep the order of the keys
             $sortKeysByName = false;
-            // Fake a PHP frontend with a null backend to avoid PHP Opcache conflicts
-            // When using >requireOnce() multiple times in one request
-            $cache = GeneralUtility::makeInstance(
-                PhpFrontend::class,
-                'middleware',
-                GeneralUtility::makeInstance(NullBackend::class, 'Production')
-            );
             $stackResolver = GeneralUtility::makeInstance(
                 MiddlewareStackResolver::class,
                 GeneralUtility::makeInstance(PackageManager::class),
                 GeneralUtility::makeInstance(DependencyOrderingService::class),
-                $cache
+                GeneralUtility::makeInstance(CacheManager::class)->getCache('cache_core')
             );
             $renderArray = [];
             foreach (['frontend', 'backend'] as $stackName) {