From 1d4256f2d33b095b136d928fdb9a1b6c752adcf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Fr=C3=B6mken?= Date: Thu, 12 Dec 2024 15:31:10 +0100 Subject: [PATCH] Use PHP generator to prevent processing of all available site Use PHP generator with yield to process just the available sites needed How to test With over 300 root pages it needs over 30 seconds to build up all available sites. With help of a PHP generator we can stop processing all available sites if just the first site is requested. It's also helpful to just check, if there are available sites. This should speed up performance a lot. * Remove cache storing from methods of SiteRepository * Use hasAvailableSites in ModuleController * Remove cache-set check from tests for SiteRepository * Repair unit tests * Move check for duplicate sites to getAvailableSites. PhpStan * Remove unused imports Fixes: #3939 Ports: #4154 Replaces: #4145 --- .../Search/AbstractModuleController.php | 7 +- Classes/Domain/Site/SiteRepository.php | 77 +++++++++++++++---- Classes/Report/SiteHandlingStatus.php | 6 +- Classes/ViewHelpers/SearchFormViewHelper.php | 6 +- Tests/Unit/Domain/Site/SiteRepositoryTest.php | 1 - .../ViewHelpers/SearchFormViewHelperTest.php | 3 +- 6 files changed, 68 insertions(+), 32 deletions(-) diff --git a/Classes/Controller/Backend/Search/AbstractModuleController.php b/Classes/Controller/Backend/Search/AbstractModuleController.php index 14cbefb872..8a2b8c7900 100644 --- a/Classes/Controller/Backend/Search/AbstractModuleController.php +++ b/Classes/Controller/Backend/Search/AbstractModuleController.php @@ -125,9 +125,8 @@ protected function initializeAction(): void */ protected function autoSelectFirstSiteAndRootPageWhenOnlyOneSiteIsAvailable(): bool { - $solrConfiguredSites = $this->siteRepository->getAvailableSites(); $availableSites = $this->siteFinder->getAllSites(); - if (count($solrConfiguredSites) === 1 && count($availableSites) === 1) { + if (count($availableSites) === 1 && $this->siteRepository->hasExactlyOneAvailableSite()) { $this->selectedSite = $this->siteRepository->getFirstAvailableSite(); // we only overwrite the selected pageUid when no id was passed @@ -148,9 +147,7 @@ protected function autoSelectFirstSiteAndRootPageWhenOnlyOneSiteIsAvailable(): b */ protected function initializeView(ViewInterface|FluidStandaloneViewInterface $view): void { - $sites = $this->siteRepository->getAvailableSites(); - - $selectOtherPage = count($sites) > 0 || $this->selectedPageUID < 1; + $selectOtherPage = $this->siteRepository->hasAvailableSites() || $this->selectedPageUID < 1; $this->moduleTemplate->assign('showSelectOtherPage', $selectOtherPage); $this->moduleTemplate->assign('selectedPageUID', $this->selectedPageUID); if ($this->selectedPageUID < 1) { diff --git a/Classes/Domain/Site/SiteRepository.php b/Classes/Domain/Site/SiteRepository.php index 092f732430..93b2dc0429 100644 --- a/Classes/Domain/Site/SiteRepository.php +++ b/Classes/Domain/Site/SiteRepository.php @@ -103,8 +103,12 @@ public function getSiteByRootPageId(int $rootPageId): ?Site */ public function getFirstAvailableSite(bool $stopOnInvalidSite = false): ?Site { - $sites = $this->getAvailableSites($stopOnInvalidSite); - return array_shift($sites); + $siteGenerator = $this->getAvailableTYPO3ManagedSites($stopOnInvalidSite); + $siteGenerator->rewind(); + + $site = $siteGenerator->current(); + + return $site instanceof Site ? $site : null; } /** @@ -119,37 +123,81 @@ public function getAvailableSites(bool $stopOnInvalidSite = false): array $cacheId = 'SiteRepository' . '_' . 'getAvailableSites'; $sites = $this->runtimeCache->get($cacheId); - if (!empty($sites)) { + if (is_array($sites) && $sites !== []) { return $sites; } - $sites = $this->getAvailableTYPO3ManagedSites($stopOnInvalidSite); + $siteGenerator = $this->getAvailableTYPO3ManagedSites($stopOnInvalidSite); + $siteGenerator->rewind(); + + $sites = []; + foreach ($siteGenerator as $rootPageId => $site) { + if (isset($sites[$rootPageId])) { + //get each site only once + continue; + } + $sites[$rootPageId] = $site; + } $this->runtimeCache->set($cacheId, $sites); return $sites; } + /** + * Check, if there are any managed sites available + * + * @throws UnexpectedTYPO3SiteInitializationException + */ + public function hasAvailableSites(bool $stopOnInvalidSite = false): bool + { + $siteGenerator = $this->getAvailableTYPO3ManagedSites($stopOnInvalidSite); + $siteGenerator->rewind(); + + return ($site = $siteGenerator->current()) && $site instanceof Site; + } + + /** + * Check, if there is exactly one managed site available + * Needed in AbstractModuleController::autoSelectFirstSiteAndRootPageWhenOnlyOneSiteIsAvailable + * + * @throws UnexpectedTYPO3SiteInitializationException + */ + public function hasExactlyOneAvailableSite(bool $stopOnInvalidSite = false): bool + { + if (!$this->hasAvailableSites($stopOnInvalidSite)) { + return false; + } + + $siteGenerator = $this->getAvailableTYPO3ManagedSites($stopOnInvalidSite); + $siteGenerator->rewind(); + + // We start with 1 here as we know from hasAvailableSites() above we have at least one site + $counter = 1; + foreach ($siteGenerator as $_) { + if ($counter > 1) { + return false; + } + $counter++; + } + + return true; + } + /** * Returns available TYPO3 sites * - * @return Site[] + * @return Site[]|\Generator * * @throws UnexpectedTYPO3SiteInitializationException */ - protected function getAvailableTYPO3ManagedSites(bool $stopOnInvalidSite): array + protected function getAvailableTYPO3ManagedSites(bool $stopOnInvalidSite): \Generator { - $typo3ManagedSolrSites = []; - $typo3Sites = $this->siteFinder->getAllSites(); - foreach ($typo3Sites as $typo3Site) { + foreach ($this->siteFinder->getAllSites() as $typo3Site) { try { $rootPageId = $typo3Site->getRootPageId(); - if (isset($typo3ManagedSolrSites[$rootPageId])) { - //get each site only once - continue; - } $typo3ManagedSolrSite = $this->buildSite($rootPageId); if ($typo3ManagedSolrSite->isEnabled()) { - $typo3ManagedSolrSites[$rootPageId] = $typo3ManagedSolrSite; + yield $rootPageId => $typo3ManagedSolrSite; } } catch (Throwable $e) { if ($stopOnInvalidSite) { @@ -161,7 +209,6 @@ protected function getAvailableTYPO3ManagedSites(bool $stopOnInvalidSite): array } } } - return $typo3ManagedSolrSites; } /** diff --git a/Classes/Report/SiteHandlingStatus.php b/Classes/Report/SiteHandlingStatus.php index 51f31585fa..bb2ee42da2 100644 --- a/Classes/Report/SiteHandlingStatus.php +++ b/Classes/Report/SiteHandlingStatus.php @@ -66,8 +66,7 @@ public function __construct( public function getStatus(): array { $reports = []; - $sites = $this->siteRepository->getAvailableSites(); - if (empty($sites)) { + if (!$this->siteRepository->hasAvailableSites()) { $reports[] = GeneralUtility::makeInstance( Status::class, self::TITLE_SITE_HANDLING_CONFIGURATION, @@ -79,8 +78,7 @@ public function getStatus(): array return $reports; } - /** @var Site $site */ - foreach ($sites as $site) { + foreach ($this->siteRepository->getAvailableSites() as $site) { if (!($site instanceof Site)) { $reports[] = GeneralUtility::makeInstance( Status::class, diff --git a/Classes/ViewHelpers/SearchFormViewHelper.php b/Classes/ViewHelpers/SearchFormViewHelper.php index 8334b32d3a..0a7319f138 100644 --- a/Classes/ViewHelpers/SearchFormViewHelper.php +++ b/Classes/ViewHelpers/SearchFormViewHelper.php @@ -24,14 +24,10 @@ use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Mvc\RequestInterface; use TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder; -use TYPO3\CMS\Fluid\Core\Rendering\RenderingContext; use TYPO3Fluid\Fluid\Core\Variables\VariableProviderInterface; /** * Class SearchFormViewHelper - * - * - * @property RenderingContext $renderingContext */ class SearchFormViewHelper extends AbstractSolrFrontendTagBasedViewHelper { @@ -87,7 +83,7 @@ public function initializeArguments(): void public function render() { /** @var RequestInterface $request */ - $request = $this->renderingContext->getAttribute(ServerRequestInterface::class); + $request = $this->renderingContext->getRequest(); $this->uriBuilder->setRequest($request); $pageUid = $this->arguments['pageUid'] ?? null; if ($pageUid === null && !empty($this->getTypoScriptConfiguration()->getSearchTargetPage())) { diff --git a/Tests/Unit/Domain/Site/SiteRepositoryTest.php b/Tests/Unit/Domain/Site/SiteRepositoryTest.php index 254919bed4..aa127c06f5 100644 --- a/Tests/Unit/Domain/Site/SiteRepositoryTest.php +++ b/Tests/Unit/Domain/Site/SiteRepositoryTest.php @@ -89,7 +89,6 @@ public function canGetFirstAvailableSite(): void $this->assertThatSitesAreCreatedWithPageIds([333], [ 0 => ['language' => 0], ]); - $this->assertCacheIsWritten(); $site = $this->siteRepository->getFirstAvailableSite(); self::assertInstanceOf(Site::class, $site); diff --git a/Tests/Unit/ViewHelpers/SearchFormViewHelperTest.php b/Tests/Unit/ViewHelpers/SearchFormViewHelperTest.php index fda6a252ee..c3f93a416a 100644 --- a/Tests/Unit/ViewHelpers/SearchFormViewHelperTest.php +++ b/Tests/Unit/ViewHelpers/SearchFormViewHelperTest.php @@ -21,7 +21,6 @@ use ApacheSolrForTypo3\Solr\ViewHelpers\SearchFormViewHelper; use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\MockObject\MockObject; -use Psr\Http\Message\ServerRequestInterface; use TYPO3\CMS\Core\Http\ServerRequest; use TYPO3\CMS\Extbase\Mvc\ExtbaseRequestParameters; use TYPO3\CMS\Extbase\Mvc\Request; @@ -64,7 +63,7 @@ protected function setUp(): void new TemplatePaths() ); $request = new Request((new ServerRequest())->withAttribute('extbase', new ExtbaseRequestParameters(SearchController::class))); - $renderingContext->setAttribute(ServerRequestInterface::class, $request); + $renderingContext->setRequest($request); $this->viewHelper->setRenderingContext($renderingContext); $this->viewHelper->expects(self::any())->method('getTypoScriptConfiguration')->willReturn($this->typoScriptConfigurationMock); $this->viewHelper->expects(self::any())->method('getTemplateVariableContainer')->willReturn($this->createMock(VariableProviderInterface::class));