From 4690528e012a7d6bd4f1756d24856c7f8f6d8b32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20K=C3=A4hm?= Date: Mon, 11 Nov 2024 12:11:56 +0100 Subject: [PATCH 1/3] [TASK] improve exception handling Re-throw exceptions an provide `$previous` Exceptions to error handler in configuration stack. Relates: #3995 --- Classes/ConnectionManager.php | 130 +++++++++++--- .../RecordMonitor/Helper/RootPageResolver.php | 23 +++ Classes/Domain/Site/Site.php | 160 ++++++++++++++---- Classes/Domain/Site/SiteRepository.php | 18 +- .../Configuration/ConfigurationManager.php | 23 +++ .../System/Records/Pages/PagesRepository.php | 65 +++++-- Classes/System/Util/SiteUtility.php | 4 + Tests/Integration/GarbageCollectorTest.php | 8 +- 8 files changed, 351 insertions(+), 80 deletions(-) diff --git a/Classes/ConnectionManager.php b/Classes/ConnectionManager.php index 7e325e6df0..5d9b4db0ff 100644 --- a/Classes/ConnectionManager.php +++ b/Classes/ConnectionManager.php @@ -25,8 +25,9 @@ use ApacheSolrForTypo3\Solr\System\Records\Pages\PagesRepository as PagesRepositoryAtExtSolr; use ApacheSolrForTypo3\Solr\System\Solr\SolrConnection; use ApacheSolrForTypo3\Solr\System\Util\SiteUtility; -use Doctrine\DBAL\Exception as DBALException; use Solarium\Core\Client\Endpoint; +use Throwable; +use TYPO3\CMS\Core\Exception\SiteNotFoundException; use TYPO3\CMS\Core\SingletonInterface; use TYPO3\CMS\Core\Site\Entity\Site as Typo3Site; @@ -50,7 +51,7 @@ class ConnectionManager implements SingletonInterface public function __construct( PagesRepositoryAtExtSolr $pagesRepositoryAtExtSolr = null, - SiteRepository $siteRepository = null + SiteRepository $siteRepository = null, ) { $this->siteRepository = $siteRepository ?? GeneralUtility::makeInstance(SiteRepository::class); $this->pagesRepositoryAtExtSolr = $pagesRepositoryAtExtSolr ?? GeneralUtility::makeInstance(PagesRepositoryAtExtSolr::class); @@ -59,10 +60,39 @@ public function __construct( /** * Creates a Solr connection for read and write endpoints * - * @throw InvalidConnectionException + * See: {@link Endpoint} + * + * @param array{ + * 'scheme': string, + * 'host': string, + * 'port': int, + * 'path': string, + * 'context'?: string, + * 'collection'?: string, + * 'core': ?string, + * 'leader'?: bool, + * 'username'?: string, + * 'password'?: string + * } $readEndpointConfiguration + * @param array{ + * 'scheme': string, + * 'host': string, + * 'port': int, + * 'path': string, + * 'context'?: string, + * 'collection'?: string, + * 'core': ?string, + * 'leader'?: bool, + * 'username'?: string, + * 'password'?: string + * } $writeEndpointConfiguration + * + * @throws InvalidConnectionException */ - public function getSolrConnectionForEndpoints(array $readEndpointConfiguration, array $writeEndpointConfiguration): SolrConnection - { + public function getSolrConnectionForEndpoints( + array $readEndpointConfiguration, + array $writeEndpointConfiguration, + ): SolrConnection { $connectionHash = md5(json_encode($readEndpointConfiguration) . json_encode($writeEndpointConfiguration)); if (!isset(self::$connections[$connectionHash])) { $readEndpoint = new Endpoint($readEndpointConfiguration); @@ -95,6 +125,35 @@ protected function isValidEndpoint(Endpoint $endpoint): bool /** * Creates a solr configuration from the configuration array and returns it. + * + * @param array{ + * 'read': array{ + * 'scheme': string, + * 'host': string, + * 'port': int, + * 'path': string, + * 'context'?: string, + * 'collection'?: string, + * 'core': ?string, + * 'leader'?: bool, + * 'username'?: string, + * 'password'?: string + * }, + * 'write': array{ + * 'scheme': string, + * 'host': string, + * 'port': int, + * 'path': string, + * 'context'?: string, + * 'collection'?: string, + * 'core': ?string, + * 'leader'?: bool, + * 'username'?: string, + * 'password'?: string + * } + * } $solrConfiguration + * + * @throws InvalidConnectionException */ public function getConnectionFromConfiguration(array $solrConfiguration): SolrConnection { @@ -104,18 +163,24 @@ public function getConnectionFromConfiguration(array $solrConfiguration): SolrCo /** * Gets a Solr connection for a page ID. * - * @throws DBALException * @throws NoSolrConnectionFoundException */ public function getConnectionByPageId(int $pageId, int $language = 0, string $mountPointParametersList = ''): SolrConnection { try { $site = $this->siteRepository->getSiteByPageId($pageId, $mountPointParametersList); - $this->throwExceptionOnInvalidSite($site, 'No site for pageId ' . $pageId); + $this->throwExceptionOnInvalidSite( + $site, + 'No site for pageId ' . $pageId, + ); $config = $site->getSolrConnectionConfiguration($language); return $this->getConnectionFromConfiguration($config); - } catch (InvalidArgumentException) { - throw $this->buildNoConnectionExceptionForPageAndLanguage($pageId, $language); + } catch (Throwable $unexpectedError) { + throw $this->buildNoConnectionExceptionForPageAndLanguage( + $pageId, + $language, + $unexpectedError, + ); } } @@ -136,10 +201,11 @@ public function getConnectionByTypo3Site(Typo3Site $typo3Site, int $languageUid try { return $this->getConnectionFromConfiguration($config); - } catch (InvalidArgumentException) { + } catch (Throwable $unexpectedError) { throw $this->buildNoConnectionExceptionForPageAndLanguage( $typo3Site->getRootPageId(), - $languageUid + $languageUid, + $unexpectedError, ); } } @@ -147,8 +213,9 @@ public function getConnectionByTypo3Site(Typo3Site $typo3Site, int $languageUid /** * Gets a Solr connection for a root page ID. * - * @throws DBALException + * @throws InvalidConnectionException * @throws NoSolrConnectionFoundException + * @throws SiteNotFoundException */ public function getConnectionByRootPageId(int $pageId, ?int $language = 0): SolrConnection { @@ -167,6 +234,7 @@ public function getConnectionByRootPageId(int $pageId, ?int $language = 0): Solr * * @return SolrConnection[] An array of initialized {@link SolrConnection} connections * + * @throws InvalidConnectionException * @throws UnexpectedTYPO3SiteInitializationException */ public function getAllConnections(): array @@ -185,6 +253,8 @@ public function getAllConnections(): array * Gets all connections configured for a given site. * * @return SolrConnection[] An array of Solr connection objects {@link SolrConnection} + * + * @throws InvalidConnectionException */ public function getConnectionsBySite(Site $site): array { @@ -200,10 +270,16 @@ public function getConnectionsBySite(Site $site): array /** * Builds and returns the exception instance of {@link NoSolrConnectionFoundException} */ - protected function buildNoConnectionExceptionForPageAndLanguage(int $pageId, int $language): NoSolrConnectionFoundException - { + protected function buildNoConnectionExceptionForPageAndLanguage( + int $pageId, + int $language, + ?Throwable $previous = null, + ): NoSolrConnectionFoundException { $message = 'Could not find a Solr connection for page [' . $pageId . '] and language [' . $language . '].'; - $noSolrConnectionException = $this->buildNoConnectionException($message); + $noSolrConnectionException = $this->buildNoConnectionException( + $message, + $previous, + ); $noSolrConnectionException->setLanguageId($language); return $noSolrConnectionException; @@ -214,24 +290,32 @@ protected function buildNoConnectionExceptionForPageAndLanguage(int $pageId, int * * @throws NoSolrConnectionFoundException */ - protected function throwExceptionOnInvalidSite(?Site $site, string $message): void - { + protected function throwExceptionOnInvalidSite( + ?Site $site, + string $message, + ?Throwable $previous = null, + ): void { if (!is_null($site)) { return; } - throw $this->buildNoConnectionException($message); + throw $this->buildNoConnectionException( + $message, + $previous + ); } /** * Build a NoSolrConnectionFoundException with the passed message. */ - protected function buildNoConnectionException(string $message): NoSolrConnectionFoundException - { - return GeneralUtility::makeInstance( - NoSolrConnectionFoundException::class, + protected function buildNoConnectionException( + string $message, + ?Throwable $previous = null, + ): NoSolrConnectionFoundException { + return new NoSolrConnectionFoundException( $message, - 1575396474 + 1575396474, + $previous ); } } diff --git a/Classes/Domain/Index/Queue/RecordMonitor/Helper/RootPageResolver.php b/Classes/Domain/Index/Queue/RecordMonitor/Helper/RootPageResolver.php index fa6a144430..62e6a54063 100644 --- a/Classes/Domain/Index/Queue/RecordMonitor/Helper/RootPageResolver.php +++ b/Classes/Domain/Index/Queue/RecordMonitor/Helper/RootPageResolver.php @@ -24,6 +24,7 @@ use ApacheSolrForTypo3\Solr\System\Configuration\ExtensionConfiguration; use ApacheSolrForTypo3\Solr\System\Page\Rootline; use ApacheSolrForTypo3\Solr\System\Util\SiteUtility; +use Doctrine\DBAL\Exception as DBALException; use RuntimeException; use TYPO3\CMS\Backend\Utility\BackendUtility; use TYPO3\CMS\Core\SingletonInterface; @@ -58,8 +59,11 @@ public function __construct( * if the pid is references in another site with additionalPageIds and returning those rootPageIds as well. * The result is cached by the caching framework. * + * @return int[] + * * @throws UnexpectedTYPO3SiteInitializationException * @throws RootPageRecordNotFoundException + * @throws DBALException */ public function getResponsibleRootPageIds(string $table, int $uid): array { @@ -118,6 +122,12 @@ public function getIsRootPageId(int $pageId): bool /** * Returns record of page for given conditions or empty array if nothing found. + * + * @return array{ + * 'uid'?: int, + * 'pid'?: int, + * 'is_siteroot'?: int + * } */ protected function getPageRecordByPageId(int $pageId, string $fieldList = 'is_siteroot'): array { @@ -126,6 +136,8 @@ protected function getPageRecordByPageId(int $pageId, string $fieldList = 'is_si /** * Determines the root page ID for a given page. + * + * @throws DBALException */ public function getRootPageId( int $pageId = 0, @@ -163,8 +175,11 @@ public function getRootPageId( * This method determines the responsible site roots for a record by getting the rootPage of the record and checking * if the pid is references in another site with additionalPageIds and returning those rootPageIds as well. * + * @return int[] + * * @throws UnexpectedTYPO3SiteInitializationException * @throws RootPageRecordNotFoundException + * @throws DBALException */ protected function buildResponsibleRootPageIds(string $table, int $uid): array { @@ -188,6 +203,8 @@ protected function buildResponsibleRootPageIds(string $table, int $uid): array /** * This method checks if the record is a pages record or another one and determines the rootPageId from the records * rootline. + * + * @throws DBALException */ protected function getRootPageIdByTableAndUid(string $table, int $uid): int { @@ -215,6 +232,8 @@ protected function getRecordPageId(string $table, int $uid): int * configuration of another site, if so we return the rootPageId of this site. * The result is cached by the caching framework. * + * @return int[] + * * @throws UnexpectedTYPO3SiteInitializationException */ public function getAlternativeSiteRootPagesIds(string $table, int $uid, int $recordPageId): array @@ -230,6 +249,8 @@ public function getAlternativeSiteRootPagesIds(string $table, int $uid, int $rec /** * Retrieves an optimized array structure with the monitored pageId as key and the relevant site rootIds as value. * + * @return array + * * @throws UnexpectedTYPO3SiteInitializationException */ protected function getSiteRootsByObservedPageIds(string $table, int $uid): array @@ -250,6 +271,8 @@ protected function getSiteRootsByObservedPageIds(string $table, int $uid): array * This method builds an array with observer page id as key and rootPageIds as values to determine which root pages * are responsible for this record by referencing the pageId in additionalPageIds configuration. * + * @return array + * * @throws UnexpectedTYPO3SiteInitializationException */ protected function buildSiteRootsByObservedPageIds(string $table, int $uid): array diff --git a/Classes/Domain/Site/Site.php b/Classes/Domain/Site/Site.php index 725f1cbb01..c71a7a37d7 100644 --- a/Classes/Domain/Site/Site.php +++ b/Classes/Domain/Site/Site.php @@ -20,6 +20,7 @@ use ApacheSolrForTypo3\Solr\NoSolrConnectionFoundException; use ApacheSolrForTypo3\Solr\System\Configuration\TypoScriptConfiguration; use ApacheSolrForTypo3\Solr\System\Records\Pages\PagesRepository; +use Doctrine\DBAL\Exception as DBALException; use TYPO3\CMS\Backend\Utility\BackendUtility; use TYPO3\CMS\Core\Site\Entity\Site as Typo3Site; use TYPO3\CMS\Core\Utility\GeneralUtility; @@ -30,59 +31,106 @@ */ class Site { - protected TypoScriptConfiguration $configuration; - - protected array $rootPageRecord = []; - - protected string $domain = ''; - - protected string $siteHash = ''; - protected PagesRepository $pagesRepository; - protected int $defaultLanguageId = 0; + protected ?Typo3Site $typo3SiteObject = null; /** - * @var int[] Available language ids + * @var int[] */ - protected array $availableLanguageIds = []; - - protected ?Typo3Site $typo3SiteObject = null; - - protected array $solrConnectionConfigurations = []; - protected array $freeContentModeLanguages = []; /** - * Constructor of Site - * - * @todo Use dependency injection instead. + * @param TypoScriptConfiguration $configuration + * @param array{ + * 'uid': int, + * 'pid': int, + * 'title': string + * } $rootPageRecord + * @param string $domain + * @param string $siteHash + * @param PagesRepository|null $pagesRepository + * @param int $defaultLanguageId + * @param int[] $availableLanguageIds Available language ids + * @param array $solrConnectionConfigurations + * @param Typo3Site|null $typo3SiteObject */ public function __construct( - TypoScriptConfiguration $configuration, - array $page, - string $domain, - string $siteHash, - PagesRepository $pagesRepository = null, - int $defaultLanguageId = 0, - array $availableLanguageIds = [], - array $solrConnectionConfigurations = [], + protected TypoScriptConfiguration $configuration, + protected array $rootPageRecord, + protected string $domain, + protected string $siteHash, + ?PagesRepository $pagesRepository = null, + protected int $defaultLanguageId = 0, + protected array $availableLanguageIds = [], + protected array $solrConnectionConfigurations = [], Typo3Site $typo3SiteObject = null, ) { - $this->configuration = $configuration; - $this->rootPageRecord = $page; - $this->domain = $domain; - $this->siteHash = $siteHash; $this->pagesRepository = $pagesRepository ?? GeneralUtility::makeInstance(PagesRepository::class); - $this->defaultLanguageId = $defaultLanguageId; - $this->availableLanguageIds = $availableLanguageIds; - $this->solrConnectionConfigurations = $solrConnectionConfigurations; $this->typo3SiteObject = $typo3SiteObject; } /** * Returns Solr connection configurations indexed by language id. * + * @return array{ + * 'connectionKey': string, + * 'language': string, + * 'rootPageUid': int, + * 'read': array{ + * 'scheme': string, + * 'host': string, + * 'port': int, + * 'path': string, + * 'context': string, + * 'collection': string, + * 'core': string, + * 'leader': bool, + * 'username': string, + * 'password': string + * }, + * 'write': array{ + * 'scheme': string, + * 'host': string, + * 'port': int, + * 'path': string, + * 'context': string, + * 'collection': string, + * 'core': string, + * 'leader': bool, + * 'username': string, + * 'password': string + * } + * } + * * @throws NoSolrConnectionFoundException */ public function getSolrConnectionConfiguration(int $language = 0): array @@ -122,6 +170,9 @@ public function hasFreeContentModeLanguages(): bool * Note: There is no "fallback type" nor "fallbacks" for default language 0 * See "displayCond" on https://github.com/TYPO3/typo3/blob/1394a4cff5369df3f835dae254b3d4ada2f83c7b/typo3/sysext/backend/Configuration/SiteConfiguration/site_language.php#L403-L416 * or https://review.typo3.org/c/Packages/TYPO3.CMS/+/56505/ for more information. + * + * + * @return array */ public function getFreeContentModeLanguages(): array { @@ -151,6 +202,8 @@ public function getRootPageId(): int /** * Gets available language id's for this site + * + * @return int[] */ public function getAvailableLanguageIds(): array { @@ -205,7 +258,9 @@ public function getDefaultLanguageId(): int * @param int|null $pageId Page ID from where to start collection sub-pages. Uses and includes the root page if none given. * @param string|null $indexQueueConfigurationName The name of index queue. * - * @return array Array of pages (IDs) in this site + * @return int[] Array of pages (IDs) in this site + * + * @throws DBALException */ public function getPages( ?int $pageId = null, @@ -247,7 +302,11 @@ public function getDomain(): string /** * Gets the site's root page record. * - * @return array The site's root page. + * @return array{ + * 'uid': int, + * 'pid': int, + * 'title': string + * } The site's root page. */ public function getRootPageRecord(): array { @@ -266,6 +325,35 @@ public function getTitle(): string /** * Returns all Solr connection configurations. + * @return array */ public function getAllSolrConnectionConfigurations(): array { diff --git a/Classes/Domain/Site/SiteRepository.php b/Classes/Domain/Site/SiteRepository.php index 38ec6d6aaf..320713a5d3 100644 --- a/Classes/Domain/Site/SiteRepository.php +++ b/Classes/Domain/Site/SiteRepository.php @@ -21,7 +21,6 @@ use ApacheSolrForTypo3\Solr\Domain\Site\Exception\UnexpectedTYPO3SiteInitializationException; use ApacheSolrForTypo3\Solr\Exception\InvalidArgumentException; use ApacheSolrForTypo3\Solr\FrontendEnvironment; -use ApacheSolrForTypo3\Solr\FrontendEnvironment\Tsfe; use ApacheSolrForTypo3\Solr\System\Cache\TwoLevelCache; use ApacheSolrForTypo3\Solr\System\Configuration\ExtensionConfiguration; use ApacheSolrForTypo3\Solr\System\Records\Pages\PagesRepository; @@ -29,6 +28,7 @@ use Doctrine\DBAL\Exception as DBALException; use Throwable; use TYPO3\CMS\Backend\Utility\BackendUtility; +use TYPO3\CMS\Core\Exception\SiteNotFoundException; use TYPO3\CMS\Core\Site\Entity\Site as CoreSite; use TYPO3\CMS\Core\Site\SiteFinder; use TYPO3\CMS\Core\Utility\GeneralUtility; @@ -67,6 +67,7 @@ public function __construct( * * @throws DBALException * @throws InvalidArgumentException + * @throws SiteNotFoundException */ public function getSiteByPageId(int $pageId, string $mountPointIdentifier = ''): ?Site { @@ -77,8 +78,8 @@ public function getSiteByPageId(int $pageId, string $mountPointIdentifier = ''): /** * Gets the Site for a specific root page-id. * - * @throws DBALException * @throws InvalidArgumentException + * @throws SiteNotFoundException */ public function getSiteByRootPageId(int $rootPageId): ?Site { @@ -166,8 +167,8 @@ protected function getAvailableTYPO3ManagedSites(bool $stopOnInvalidSite): array /** * Creates an instance of the Site object. * - * @throws DBALException * @throws InvalidArgumentException + * @throws SiteNotFoundException */ protected function buildSite(int $rootPageId): ?Site { @@ -197,6 +198,11 @@ protected function getSiteHashForDomain(string $domain): string /** * Validates given root page record, if it fits requirements. * + * @param array{ + * 'uid'?: int, + * 'is_siteroot'?: int + * } $rootPageRecord + * * @throws InvalidArgumentException */ protected function validateRootPageRecord(array $rootPageRecord): void @@ -211,8 +217,12 @@ protected function validateRootPageRecord(array $rootPageRecord): void /** * Builds a TYPO3 managed site with TypoScript configuration. + * @param array{ + * 'uid': int, + * 'pid'?: int + * } $rootPageRecord * - * @throws DBALException + * @throws SiteNotFoundException */ protected function buildTypo3ManagedSite(array $rootPageRecord): ?Site { diff --git a/Classes/System/Configuration/ConfigurationManager.php b/Classes/System/Configuration/ConfigurationManager.php index a9e0275905..362537fec4 100644 --- a/Classes/System/Configuration/ConfigurationManager.php +++ b/Classes/System/Configuration/ConfigurationManager.php @@ -148,6 +148,29 @@ public function getCoreTypoScriptFrontendByRequest(ServerRequestInterface $reque } /** + * @return array{ + * 'uid': int, + * 'pid': int, + * 'tstamp': int, + * 'crdate': int, + * 'deleted': int, + * 'hidden': int, + * 'starttime': int, + * 'endtime': int, + * 'sorting': int, + * 'description': string, + * 'tx_impexp_origuid': int, + * 'title': string, + * 'root': int, + * 'clear': int, + * 'constants': string, + * 'include_static_file': string, + * 'basedOn': string, + * 'includeStaticAfterBasedOn': int, + * 'config': string, + * 'static_file_mode': int, + * } + * * @throws DBALException */ protected function getSysTemplateRowsForAssociatedContextPageId(ServerRequestInterface $request): array diff --git a/Classes/System/Records/Pages/PagesRepository.php b/Classes/System/Records/Pages/PagesRepository.php index f9c94f4257..60f76ff609 100644 --- a/Classes/System/Records/Pages/PagesRepository.php +++ b/Classes/System/Records/Pages/PagesRepository.php @@ -49,7 +49,10 @@ public function __construct(TwoLevelCache $transientVariableCache = null) * Gets the site's root pages. The "Is root of website" flag must be set, * which usually is the case for pages with pid = 0. * - * @return array An array of (partial) root page records, containing the uid and title fields + * @return array{array{ + * 'uid': int, + * 'title': string + * }} An array of (partial) root page records, containing the uid and title fields * * @throws DBALException */ @@ -75,10 +78,21 @@ public function findAllRootPages(): array /** * Finds the MountPointProperties array for mount points(destinations) by mounted page UID(source) or by the rootline array of mounted page. * + * @param int $mountedPageUid + * @param int[] $rootLineParentPageIds + * @return array{array{ + * 'uid': int, + * 'mountPageDestination': int, + * 'mountPageSource': int, + * 'mountPageOverlayed': int + * }} * @throws DBALException + * @throws InvalidArgumentException */ - public function findMountPointPropertiesByPageIdOrByRootLineParentPageIds(int $mountedPageUid, array $rootLineParentPageIds = []): array - { + public function findMountPointPropertiesByPageIdOrByRootLineParentPageIds( + int $mountedPageUid, + array $rootLineParentPageIds = [], + ): array { if (array_filter($rootLineParentPageIds, 'is_int') !== $rootLineParentPageIds) { throw new InvalidArgumentException('Given $rootLineParentPageIds array is not valid. Allowed only the arrays with the root line page UIDs as integers.', 1502459711); } @@ -94,11 +108,13 @@ public function findMountPointPropertiesByPageIdOrByRootLineParentPageIds(int $m /** * This method builds the where clause for the mountpoint destinations. It retrieves all records where the mount_pid = $mountedPageUid or the mount_pid is * in the rootLineParentPageIds. + * + * @param int[] $rootLineParentPageIds */ protected function addWhereClauseForMountpointDestinationProperties( QueryBuilder $queryBuilder, int $mountedPageUid, - array $rootLineParentPageIds + array $rootLineParentPageIds, ): QueryBuilder { if (empty($rootLineParentPageIds)) { $queryBuilder->andWhere( @@ -132,7 +148,7 @@ protected function addWhereClauseForMountpointDestinationProperties( * * Includes all page types except deleted pages! * * @param int $rootPageId Page ID from where to start collection sub-pages - * @return array Array of pages (IDs) in this site + * @return int[] Array of pages (IDs) in this site * * @throws DBALException */ @@ -160,11 +176,14 @@ public function findAllSubPageIdsByRootPage( * This method retrieves the pages ids from the current tree level a calls getPages recursive, * when the maxDepth has not been reached. * + * @param int[] $pageIds + * @param string $initialPagesAdditionalWhereClause + * @return int[] * @throws DBALException */ protected function filterPageIdsByInitialPagesAdditionalWhereClause( array $pageIds, - string $initialPagesAdditionalWhereClause + string $initialPagesAdditionalWhereClause, ): array { $queryBuilder = $this->getQueryBuilder(); $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class)); @@ -186,6 +205,8 @@ protected function filterPageIdsByInitialPagesAdditionalWhereClause( /** * Finds all PIDs within no_search_sub_entries=1 marked pages in all sites. + * + * @return int[] */ public function findAllPagesWithinNoSearchSubEntriesMarkedPages(): array { @@ -197,7 +218,7 @@ public function findAllPagesWithinNoSearchSubEntriesMarkedPages(): array ->select('uid') ->from($this->table) ->where( - $queryBuilder->expr()->eq('no_search_sub_entries', $queryBuilder->createNamedParameter(1, \Doctrine\DBAL\ParameterType::INTEGER)) + $queryBuilder->expr()->eq('no_search_sub_entries', $queryBuilder->createNamedParameter(1, ParameterType::INTEGER)) )->executeQuery(); while (($pageRow = $noSearchSubEntriesEnabledPagesStatement->fetchAssociative()) !== false) { $pageIds = array_merge($pageIds, $this->findAllSubPageIdsByRootPage((int)$pageRow['uid'])); @@ -211,6 +232,12 @@ public function findAllPagesWithinNoSearchSubEntriesMarkedPages(): array /** * Finds translation overlays by given page Id. * + * @return array{array{ + * 'pid': int, + * 'l10n_parent': int, + * 'sys_language_uid': int, + * }} + * * @throws DBALException */ public function findTranslationOverlaysByPageId(int $pageId): array @@ -233,7 +260,9 @@ public function findTranslationOverlaysByPageId(int $pageId): array * Finds Pages, which are showing content from the page currently being updated. * * @param int $pageId UID of the page currently being updated - * @return array with page Uids from pages, which are showing contents from given Page Id + * @return array{array{ + * 'uid': int + * }} with page Uids from pages, which are showing contents from given Page Id * * @throws DBALException */ @@ -260,6 +289,13 @@ public function findPageUidsWithContentsFromPid(int $pageId): array /** * Finds all pages by given where clause * + * @return array{array{ + * 'uid': int, + * 'mountPageSource': int, + * 'mountPageDestination': int, + * 'mountPageOverlayed': int + * }} + * * @throws DBALException */ public function findAllMountPagesByWhereClause(string $whereClause): array @@ -287,9 +323,18 @@ public function findAllMountPagesByWhereClause(string $whereClause): array /** * Returns a specific page + * + * @return null|array{ + * 'uid': int, + * 'pid': int + * } */ - public function getPage(int $uid, string $fields = '*', string $additionalWhereClause = '', bool $useDeleteClause = true): ?array - { + public function getPage( + int $uid, + string $fields = '*', + string $additionalWhereClause = '', + bool $useDeleteClause = true, + ): ?array { if (!$uid > 0) { return null; } diff --git a/Classes/System/Util/SiteUtility.php b/Classes/System/Util/SiteUtility.php index 1cdd9a5595..d33bd9795b 100644 --- a/Classes/System/Util/SiteUtility.php +++ b/Classes/System/Util/SiteUtility.php @@ -220,6 +220,10 @@ protected static function evaluateConfigurationData(string|bool|null $value): st /** * Takes a page record and checks whether the page is marked as root page. + * + * @param array{ + * 'is_siteroot'?: int + * } $pageRecord */ public static function isRootPage(array $pageRecord): bool { diff --git a/Tests/Integration/GarbageCollectorTest.php b/Tests/Integration/GarbageCollectorTest.php index 6f0d004fb8..51ed8b7fec 100644 --- a/Tests/Integration/GarbageCollectorTest.php +++ b/Tests/Integration/GarbageCollectorTest.php @@ -107,10 +107,7 @@ protected function assertNotEmptyIndexQueue(): void ); } - /** - * @param $amount - */ - protected function assertIndexQueueContainsItemAmount($amount): void + protected function assertIndexQueueContainsItemAmount(int $amount): void { $itemsInQueue = $this->indexQueue->getAllItemsCount(); self::assertEquals( @@ -125,9 +122,6 @@ protected function assertEmptyEventQueue(): void self::assertEquals(0, $this->eventQueue->count(), 'Event queue is not empty as expected'); } - /** - * @param int $amount - */ protected function assertEventQueueContainsItemAmount(int $amount): void { $itemsInQueue = $this->eventQueue->count(); From b928022a017b8500d11ca1ac68f1d5dbe8f9588c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20K=C3=A4hm?= Date: Mon, 11 Nov 2024 12:25:01 +0100 Subject: [PATCH 2/3] [FIX] Garbage collector does not get configuration Garbage collector and RecordMonitor triggered by RecordMonitor do not have infos about PID or Site. This change contains first step for refactoring of configuration retrieval. The delegation of TypoScriptConfiguration object, which will be moved to strict aggregation structure. Fixes: #4203, #4207, #4208 Relates: #3995 --- Classes/ConnectionManager.php | 46 ++++++++++++++++++++++------ Classes/Domain/Site/Site.php | 2 -- Classes/Report/SolrStatus.php | 6 +++- Tests/Unit/ConnectionManagerTest.php | 6 +++- 4 files changed, 47 insertions(+), 13 deletions(-) diff --git a/Classes/ConnectionManager.php b/Classes/ConnectionManager.php index 5d9b4db0ff..1b8fc35012 100644 --- a/Classes/ConnectionManager.php +++ b/Classes/ConnectionManager.php @@ -22,6 +22,7 @@ use ApacheSolrForTypo3\Solr\Domain\Site\SiteRepository; use ApacheSolrForTypo3\Solr\Exception\InvalidArgumentException; use ApacheSolrForTypo3\Solr\Exception\InvalidConnectionException; +use ApacheSolrForTypo3\Solr\System\Configuration\TypoScriptConfiguration; use ApacheSolrForTypo3\Solr\System\Records\Pages\PagesRepository as PagesRepositoryAtExtSolr; use ApacheSolrForTypo3\Solr\System\Solr\SolrConnection; use ApacheSolrForTypo3\Solr\System\Util\SiteUtility; @@ -92,6 +93,7 @@ public function __construct( public function getSolrConnectionForEndpoints( array $readEndpointConfiguration, array $writeEndpointConfiguration, + TypoScriptConfiguration $typoScriptConfiguration, ): SolrConnection { $connectionHash = md5(json_encode($readEndpointConfiguration) . json_encode($writeEndpointConfiguration)); if (!isset(self::$connections[$connectionHash])) { @@ -105,7 +107,12 @@ public function getSolrConnectionForEndpoints( throw new InvalidConnectionException('Invalid write endpoint'); } - self::$connections[$connectionHash] = GeneralUtility::makeInstance(SolrConnection::class, $readEndpoint, $writeEndpoint); + self::$connections[$connectionHash] = GeneralUtility::makeInstance( + SolrConnection::class, + $readEndpoint, + $writeEndpoint, + $typoScriptConfiguration, + ); } return self::$connections[$connectionHash]; @@ -155,9 +162,15 @@ protected function isValidEndpoint(Endpoint $endpoint): bool * * @throws InvalidConnectionException */ - public function getConnectionFromConfiguration(array $solrConfiguration): SolrConnection - { - return $this->getSolrConnectionForEndpoints($solrConfiguration['read'], $solrConfiguration['write']); + public function getConnectionFromConfiguration( + array $solrConfiguration, + TypoScriptConfiguration $typoScriptConfiguration, + ): SolrConnection { + return $this->getSolrConnectionForEndpoints( + $solrConfiguration['read'], + $solrConfiguration['write'], + $typoScriptConfiguration + ); } /** @@ -174,7 +187,10 @@ public function getConnectionByPageId(int $pageId, int $language = 0, string $mo 'No site for pageId ' . $pageId, ); $config = $site->getSolrConnectionConfiguration($language); - return $this->getConnectionFromConfiguration($config); + return $this->getConnectionFromConfiguration( + $config, + $site->getSolrConfiguration(), + ); } catch (Throwable $unexpectedError) { throw $this->buildNoConnectionExceptionForPageAndLanguage( $pageId, @@ -200,7 +216,10 @@ public function getConnectionByTypo3Site(Typo3Site $typo3Site, int $languageUid } try { - return $this->getConnectionFromConfiguration($config); + return $this->getConnectionFromConfiguration( + $config, + $this->siteRepository->getSiteByRootPageId($typo3Site->getRootPageId())->getSolrConfiguration(), + ); } catch (Throwable $unexpectedError) { throw $this->buildNoConnectionExceptionForPageAndLanguage( $typo3Site->getRootPageId(), @@ -223,7 +242,10 @@ public function getConnectionByRootPageId(int $pageId, ?int $language = 0): Solr $site = $this->siteRepository->getSiteByRootPageId($pageId); $this->throwExceptionOnInvalidSite($site, 'No site for pageId ' . $pageId); $config = $site->getSolrConnectionConfiguration($language ?? 0); - return $this->getConnectionFromConfiguration($config); + return $this->getConnectionFromConfiguration( + $config, + $site->getSolrConfiguration(), + ); } catch (InvalidArgumentException) { throw $this->buildNoConnectionExceptionForPageAndLanguage($pageId, $language); } @@ -242,7 +264,10 @@ public function getAllConnections(): array $solrConnections = []; foreach ($this->siteRepository->getAvailableSites() as $site) { foreach ($site->getAllSolrConnectionConfigurations() as $solrConfiguration) { - $solrConnections[] = $this->getConnectionFromConfiguration($solrConfiguration); + $solrConnections[] = $this->getConnectionFromConfiguration( + $solrConfiguration, + $site->getSolrConfiguration(), + ); } } @@ -261,7 +286,10 @@ public function getConnectionsBySite(Site $site): array $connections = []; foreach ($site->getAllSolrConnectionConfigurations() as $languageId => $solrConnectionConfiguration) { - $connections[$languageId] = $this->getConnectionFromConfiguration($solrConnectionConfiguration); + $connections[$languageId] = $this->getConnectionFromConfiguration( + $solrConnectionConfiguration, + $site->getSolrConfiguration(), + ); } return $connections; diff --git a/Classes/Domain/Site/Site.php b/Classes/Domain/Site/Site.php index c71a7a37d7..de56e23417 100644 --- a/Classes/Domain/Site/Site.php +++ b/Classes/Domain/Site/Site.php @@ -228,8 +228,6 @@ public function getLabel(): string /** * Gets the site's Solr TypoScript configuration (plugin.tx_solr.*) - * - * Purpose: Interface and Unit test mocking helper method. */ public function getSolrConfiguration(): TypoScriptConfiguration { diff --git a/Classes/Report/SolrStatus.php b/Classes/Report/SolrStatus.php index 8b35de06b5..39dd353058 100644 --- a/Classes/Report/SolrStatus.php +++ b/Classes/Report/SolrStatus.php @@ -111,7 +111,11 @@ protected function getConnectionStatus(Site $site, array $solrConnection): Statu $this->responseStatus = ContextualFeedbackSeverity::OK; $solrAdmin = $this->connectionManager - ->getSolrConnectionForEndpoints($solrConnection['read'], $solrConnection['write']) + ->getSolrConnectionForEndpoints( + $solrConnection['read'], + $solrConnection['write'], + $site->getSolrConfiguration() + ) ->getAdminService(); $solrVersion = $this->checkSolrVersion($solrAdmin); diff --git a/Tests/Unit/ConnectionManagerTest.php b/Tests/Unit/ConnectionManagerTest.php index 21a41b99ef..423fdabebe 100644 --- a/Tests/Unit/ConnectionManagerTest.php +++ b/Tests/Unit/ConnectionManagerTest.php @@ -19,6 +19,7 @@ use ApacheSolrForTypo3\Solr\Domain\Site\SiteRepository; use ApacheSolrForTypo3\Solr\Exception\InvalidConnectionException; use ApacheSolrForTypo3\Solr\System\Configuration\ConfigurationManager; +use ApacheSolrForTypo3\Solr\System\Configuration\TypoScriptConfiguration; use ApacheSolrForTypo3\Solr\System\Logging\SolrLogManager; use ApacheSolrForTypo3\Solr\System\Records\Pages\PagesRepository; use PHPUnit\Framework\Attributes\DataProvider; @@ -141,7 +142,10 @@ public function canConnect( ]; $configuration['write'] = $configuration['read']; - $solrService = $this->connectionManager->getConnectionFromConfiguration($configuration); + $solrService = $this->connectionManager->getConnectionFromConfiguration( + $configuration, + $this->createMock(TypoScriptConfiguration::class) + ); self::assertEquals($expectedConnectionString, $solrService->getReadService()->__toString()); } catch (InvalidConnectionException $exception) { $exceptionOccurred = true; From 24ce88d43501f965d0dfbbc11fda1b30a9ff7c31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20K=C3=A4hm?= Date: Thu, 28 Nov 2024 15:15:48 +0100 Subject: [PATCH 3/3] [FIX] CS in Configuration/Backend/Modules.php --- Configuration/Backend/Modules.php | 1 + 1 file changed, 1 insertion(+) diff --git a/Configuration/Backend/Modules.php b/Configuration/Backend/Modules.php index bbe17613cd..22fef82fba 100644 --- a/Configuration/Backend/Modules.php +++ b/Configuration/Backend/Modules.php @@ -1,4 +1,5 @@