From aebc8b87f7247375cf682543528434a3cdb33f9f Mon Sep 17 00:00:00 2001
From: Stefan Froemken <froemken@gmail.com>
Date: Mon, 27 Apr 2020 15:29:59 +0200
Subject: [PATCH] [BUGFIX] Cache various where clauses of PageRepository

Since TYPO3 v9, PageRepository works independently
from TSFE and can and should be instantiated on its
own, depending on the context.

This leads to multiple initializiation calls which can
and should be cached, as a page with 100 links will
re-trigger the properties all the time, even if they
are not needed (groupWhereClause for instance).

Caching these values reduces the time needed for
typolink generation a lot.

Resolves: #91208
Releases: master, 9.5
Change-Id: Ied7e2b78519754af2e10eeb4e9c1c9fcbbffe2e8
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/64321
Tested-by: TYPO3com <noreply@typo3.com>
Tested-by: Benni Mack <benni@typo3.org>
Tested-by: Georg Ringer <georg.ringer@gmail.com>
Reviewed-by: Markus Klein <markus.klein@typo3.org>
Reviewed-by: Benni Mack <benni@typo3.org>
Reviewed-by: Georg Ringer <georg.ringer@gmail.com>
---
 .../Domain/Repository/PageRepository.php      | 71 +++++++++++--------
 1 file changed, 43 insertions(+), 28 deletions(-)

diff --git a/typo3/sysext/core/Classes/Domain/Repository/PageRepository.php b/typo3/sysext/core/Classes/Domain/Repository/PageRepository.php
index 01db1b4e0724..aa7c252f078e 100644
--- a/typo3/sysext/core/Classes/Domain/Repository/PageRepository.php
+++ b/typo3/sysext/core/Classes/Domain/Repository/PageRepository.php
@@ -152,35 +152,42 @@ class PageRepository implements LoggerAwareInterface
      */
     protected function init($show_hidden)
     {
-        $this->where_groupAccess = '';
-
-        $expressionBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
-            ->getQueryBuilderForTable('pages')
-            ->expr();
-        if ($this->versioningWorkspaceId > 0) {
-            // For version previewing, make sure that enable-fields are not
-            // de-selecting hidden pages - we need versionOL() to unset them only
-            // if the overlay record instructs us to.
-            // Clear where_hid_del and restrict to live and current workspaces
-            $this->where_hid_del = ' AND ' . $expressionBuilder->andX(
-                $expressionBuilder->eq('pages.deleted', 0),
-                $expressionBuilder->orX(
-                    $expressionBuilder->eq('pages.t3ver_wsid', 0),
-                    $expressionBuilder->eq('pages.t3ver_wsid', (int)$this->versioningWorkspaceId)
-                ),
-                $expressionBuilder->neq('pages.doktype', self::DOKTYPE_RECYCLER)
-            );
+        $cache = $this->getRuntimeCache();
+        $cacheIdentifier = 'PageRepository_hidDelWhere' . ($show_hidden ? 'ShowHidden' : '') . '_' . (int)$this->versioningWorkspaceId;
+        $cacheEntry = $cache->get($cacheIdentifier);
+        if ($cacheEntry) {
+            $this->where_hid_del = $cacheEntry;
         } else {
-            // add starttime / endtime, and check for hidden/deleted
-            // Filter out new/deleted place-holder pages in case we are NOT in a
-            // versioning preview (that means we are online!)
-            $this->where_hid_del = ' AND ' . (string)$expressionBuilder->andX(
-                QueryHelper::stripLogicalOperatorPrefix(
-                    $this->enableFields('pages', $show_hidden, ['fe_group' => true])
-                ),
-                $expressionBuilder->neq('pages.doktype', self::DOKTYPE_RECYCLER)
-            );
+            $expressionBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
+                ->getQueryBuilderForTable('pages')
+                ->expr();
+            if ($this->versioningWorkspaceId > 0) {
+                // For version previewing, make sure that enable-fields are not
+                // de-selecting hidden pages - we need versionOL() to unset them only
+                // if the overlay record instructs us to.
+                // Clear where_hid_del and restrict to live and current workspaces
+                $this->where_hid_del = ' AND ' . $expressionBuilder->andX(
+                    $expressionBuilder->eq('pages.deleted', 0),
+                    $expressionBuilder->orX(
+                        $expressionBuilder->eq('pages.t3ver_wsid', 0),
+                        $expressionBuilder->eq('pages.t3ver_wsid', (int)$this->versioningWorkspaceId)
+                    ),
+                    $expressionBuilder->neq('pages.doktype', self::DOKTYPE_RECYCLER)
+                );
+            } else {
+                // add starttime / endtime, and check for hidden/deleted
+                // Filter out new/deleted place-holder pages in case we are NOT in a
+                // versioning preview (that means we are online!)
+                $this->where_hid_del = ' AND ' . (string)$expressionBuilder->andX(
+                    QueryHelper::stripLogicalOperatorPrefix(
+                        $this->enableFields('pages', $show_hidden, ['fe_group' => true])
+                    ),
+                    $expressionBuilder->neq('pages.doktype', self::DOKTYPE_RECYCLER)
+                );
+            }
+            $cache->set($cacheIdentifier, $this->where_hid_del);
         }
+
         if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][self::class]['init'] ?? false)) {
             foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][self::class]['init'] as $classRef) {
                 $hookObject = GeneralUtility::makeInstance($classRef);
@@ -1377,6 +1384,12 @@ class PageRepository implements LoggerAwareInterface
         /** @var UserAspect $userAspect */
         $userAspect = $this->context->getAspect('frontend.user');
         $memberGroups = $userAspect->getGroupIds();
+        $cache = $this->getRuntimeCache();
+        $cacheIdentifier = 'PageRepository_groupAccessWhere_' . str_replace('.', '_', $field) . '_' . $table . '_' . implode('_', $memberGroups);
+        $cacheEntry = $cache->get($cacheIdentifier);
+        if ($cacheEntry) {
+            return $cacheEntry;
+        }
 
         $expressionBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
             ->getQueryBuilderForTable($table)
@@ -1392,7 +1405,9 @@ class PageRepository implements LoggerAwareInterface
             $orChecks[] = $expressionBuilder->inSet($field, $expressionBuilder->literal($value));
         }
 
-        return' AND (' . $expressionBuilder->orX(...$orChecks) . ')';
+        $accessGroupWhere = ' AND (' . $expressionBuilder->orX(...$orChecks) . ')';
+        $cache->set($cacheIdentifier, $accessGroupWhere);
+        return $accessGroupWhere;
     }
 
     /**********************
-- 
GitLab