[FEATURE] Speed up typoLink function by caching domain records 23/9023/14
authorSteffen Gebert <steffen.gebert@typo3.org>
Tue, 14 Feb 2012 18:56:02 +0000 (19:56 +0100)
committerAndreas Wolf <andreas.wolf@ikt-werk.de>
Thu, 7 Feb 2013 13:33:37 +0000 (14:33 +0100)
Cache the domain records in a runtime cache for improved rendering
of links generated with typolink function.

This change takes only effect, when
  config.typolinkCheckRootline = 1
is set, which otherwise costs immense performance in terms of huge
number of SQL queries.

Resolves: #24389
Releases: 6.1
Change-Id: I7c6bee1cd6ee1cb0901d926dd6ce9a22c00501ab
Reviewed-on: https://review.typo3.org/9023
Reviewed-by: Dmitry Dulepov
Tested-by: Dmitry Dulepov
Reviewed-by: Simon Schaufelberger
Tested-by: Simon Schaufelberger
typo3/sysext/frontend/Classes/ContentObject/ContentObjectRenderer.php
typo3/sysext/frontend/Classes/Controller/TypoScriptFrontendController.php
typo3/sysext/frontend/Tests/Unit/Controller/TypoScriptFrontendControllerTest.php

index ede78e7..f9ac75a 100644 (file)
@@ -5867,44 +5867,11 @@ class ContentObjectRenderer {
                                                                        $page = $page2;
                                                                }
                                                        }
-                                                       // Find all domain records in the rootline of the target page
-                                                       $targetPageRootline = $GLOBALS['TSFE']->sys_page->getRootLine($page['uid']);
-                                                       $foundDomains = array();
-                                                       $firstFoundDomains = array();
-                                                       $firstFoundForcedDomains = array();
-                                                       $targetPageRootlinePids = array();
-                                                       foreach ($targetPageRootline as $data) {
-                                                               $targetPageRootlinePids[] = intval($data['uid']);
-                                                       }
-                                                       $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('pid, domainName, forced', 'sys_domain', 'pid IN (' . implode(',', $targetPageRootlinePids) . ') ' . ' AND redirectTo=\'\' ' . $this->enableFields('sys_domain'), '', 'sorting ASC');
-                                                       // TODO maybe it makes sense to hold all sys_domain records in a cache to save additional DB querys on each typolink
-                                                       while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
-                                                               $foundDomains[] = preg_replace('/\\/$/', '', $row['domainName']);
-                                                               if (!isset($firstFoundDomains[$row['pid']])) {
-                                                                       $firstFoundDomains[$row['pid']] = preg_replace('/\\/$/', '', $row['domainName']);
-                                                               }
-                                                               if ($row['forced'] && !isset($firstFoundForcedDomains[$row['pid']])) {
-                                                                       $firstFoundForcedDomains[$row['pid']] = preg_replace('/\\/$/', '', $row['domainName']);
-                                                               }
-                                                       }
-                                                       $GLOBALS['TYPO3_DB']->sql_free_result($res);
-                                                       // Set targetDomain to first found domain record if the target page cannot be reached within the current domain
-                                                       if (count($foundDomains) > 0 && (!in_array($currentDomain, $foundDomains) && !in_array(($currentDomain . trim(preg_replace('/\\/[^\\/]*$/', '', \TYPO3\CMS\Core\Utility\GeneralUtility::getIndpEnv('SCRIPT_NAME')))), $foundDomains) || count($firstFoundForcedDomains) > 0)) {
-                                                               foreach ($targetPageRootlinePids as $pid) {
-                                                                       // Always use the 'forced' domain if we found one
-                                                                       if (isset($firstFoundForcedDomains[$pid])) {
-                                                                               $targetDomain = $firstFoundForcedDomains[$pid];
-                                                                               break;
-                                                                       }
-                                                                       // Use the first found domain record
-                                                                       if ($targetDomain === '' && isset($firstFoundDomains[$pid])) {
-                                                                               $targetDomain = $firstFoundDomains[$pid];
-                                                                       }
-                                                               }
-                                                               // Do not prepend the domain if its the current hostname
-                                                               if ($targetDomain === $currentDomain) {
-                                                                       $targetDomain = '';
-                                                               }
+
+                                                       $targetDomain = $GLOBALS['TSFE']->getDomainNameForPid($page['uid']);
+                                                       // Do not prepend the domain if it is the current hostname
+                                                       if (!$targetDomain || $targetDomain === $currentDomain) {
+                                                               $targetDomain = '';
                                                        }
                                                }
                                                $absoluteUrlScheme = 'http';
index 9703cb6..77ace77 100644 (file)
@@ -4723,6 +4723,91 @@ if (version == "n3") {
                return $result;
        }
 
+
+       /**
+        * Fetches/returns the cached contents of the sys_domain database table.
+        *
+        * @return array Domain data
+        */
+       protected function getSysDomainCache() {
+               $entryIdentifier = 'core-database-sys_domain-complete';
+               /** @var $runtimeCache t3lib_cache_frontend_AbstractFrontend */
+               $runtimeCache = $GLOBALS['typo3CacheManager']->getCache('cache_runtime');
+
+               $sysDomainData = array();
+               if ($runtimeCache->has($entryIdentifier)) {
+                       $sysDomainData = $runtimeCache->get($entryIdentifier);
+               } else {
+                       $result = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
+                               'pid, domainName, forced',
+                               'sys_domain',
+                               'redirectTo=\'\' ' . $GLOBALS['TSFE']->sys_page->enableFields('sys_domain'),
+                               '',
+                               'sorting ASC'
+                       );
+
+                       while (FALSE !== ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($result))) {
+                               // if there is already an entry for this pid, check if we should overwrite it
+                               if (isset($sysDomainData[$row['pid']])) {
+                                       // there is already a "forced" entry, which must not be overwritten
+                                       if ($sysDomainData[$row['pid']]['forced']) {
+                                               continue;
+                                       }
+
+                                       // there is alread a NOT-forced entry and the current one is also NOT-forced, thus keep the old
+                                       if (!$sysDomainData[$row['pid']]['forced'] && !$row['forced']) {
+                                               continue;
+                                       }
+                               }
+
+                               // as we passed all previous checks, we save this domain for the current pid
+                               $sysDomainData[$row['pid']] = array(
+                                       'pid' => $row['pid'],
+                                       'domainName' => rtrim($row['domainName'], '/'),
+                                       'forced' => $row['forced'],
+                               );
+                       }
+                       $runtimeCache->set($entryIdentifier, $sysDomainData);
+                       $GLOBALS['TYPO3_DB']->sql_free_result($result);
+               }
+               return $sysDomainData;
+       }
+
+       /**
+        * Obtains domain data for the target pid. Domain data is an array with
+        * 'pid', 'domainName' and 'forced' members (see sys_domain table for
+        * meaning of these fields.
+        *
+        * @param int $targetPid Target page id
+        * @return mixed Return domain data or NULL
+       */
+       public function getDomainDataForPid($targetPid) {
+               $result = NULL;
+
+               $sysDomainData = $this->getSysDomainCache();
+               $rootline = $this->sys_page->getRootLine($targetPid);
+               // walk the rootline downwards from the target page to the root, until a domain record is found
+               foreach ($rootline as $pageInRootline) {
+                       $pidInRootline = $pageInRootline['uid'];
+                       if (isset($sysDomainData[$pidInRootline])) {
+                               $result = $sysDomainData[$pidInRootline];
+                               break;
+                       }
+               }
+               return $result;
+       }
+
+       /**
+        * Obtains the domain name for the target pid. If there are several domains,
+        * the first is returned.
+        *
+        * @param int $targetPid Target page id
+        * @return mixed Return domain name or NULL if not found
+        */
+       public function getDomainNameForPid($targetPid) {
+               $domainData = $this->getDomainDataForPid($targetPid);
+               return $domainData ? $domainData['domainName'] : NULL;
+       }
 }
 
 ?>
\ No newline at end of file
index 4ccbc4d..b8ec91a 100644 (file)
@@ -203,7 +203,19 @@ class TypoScriptFrontendControllerTest extends \TYPO3\CMS\Core\Tests\UnitTestCas
                $this->fixture->_call('updateRootLinesWithTranslations');
                $this->assertSame($rootLine, $this->fixture->rootLine);
                $this->assertSame(array_reverse($rootLine), $this->fixture->tmpl->rootLine);
+       }
+
+       /*
+        * @todo Implement me!
+        */
+       public function getDomainDataForPageId() {
+       }
 
+       /**
+        * @test
+        * @todo Implement me!
+        */
+       public function getDomainNameForPageId() {
        }
 
 }