‪TYPO3CMS  ‪main
CanonicalGenerator.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
20 use Psr\EventDispatcher\EventDispatcherInterface;
21 use Psr\Http\Message\ServerRequestInterface;
30 
36 readonly class ‪CanonicalGenerator
37 {
38  public function ‪__construct(
39  private EventDispatcherInterface $eventDispatcher,
40  ) {}
41 
42  public function ‪generate(array $params): string
43  {
45  $request = $params['request'];
46  $pageRecord = $request->getAttribute('frontend.page.information')->getPageRecord();
47  $canonicalGenerationDisabledException = null;
48 
49  $href = '';
50  try {
51  $typoScriptConfigArray = $request->getAttribute('frontend.typoscript')->getConfigArray();
52  if ($typoScriptConfigArray['disableCanonical'] ?? false) {
53  throw new ‪CanonicalGenerationDisabledException('Generation of the canonical tag is disabled via TypoScript "disableCanonical"', 1706104146);
54  }
55  if ((int)$pageRecord['no_index'] === 1) {
56  throw new ‪CanonicalGenerationDisabledException('Generation of the canonical is disabled due to "no_index" being set active in the page properties', 1706104147);
57  }
58 
59  // 1) Check if page has canonical URL set
60  $href = $this->‪checkForCanonicalLink($request);
61  if ($href === '') {
62  // 2) Check if page show content from other page
63  $href = $this->‪checkContentFromPid($request);
64  }
65  if ($href === '') {
66  // 3) Fallback, create canonical URL
67  $href = $this->‪checkDefaultCanonical($request);
68  }
69  } catch (‪CanonicalGenerationDisabledException $canonicalGenerationDisabledException) {
70  } finally {
71  $event = $this->eventDispatcher->dispatch(
72  new ‪ModifyUrlForCanonicalTagEvent($request, new ‪Page($pageRecord), $href, $canonicalGenerationDisabledException)
73  );
74  $href = $event->getUrl();
75  }
76 
77  if ($href !== '') {
78  $canonical = '<link ' . GeneralUtility::implodeAttributes([
79  'rel' => 'canonical',
80  'href' => $href,
81  ], true) . '/>' . LF;
82  $request->getAttribute('frontend.controller')->additionalHeaderData[] = $canonical;
83  return $canonical;
84  }
85  return '';
86  }
87 
88  protected function ‪checkForCanonicalLink(ServerRequestInterface $request): string
89  {
90  $typoScriptFrontendController = $request->getAttribute('frontend.controller');
91  $pageRecord = $request->getAttribute('frontend.page.information')->getPageRecord();
92  $cObj = GeneralUtility::makeInstance(ContentObjectRenderer::class, $typoScriptFrontendController);
93  $cObj->setRequest($request);
94  $cObj->start($pageRecord, 'pages');
95  if (!empty($pageRecord['canonical_link'])) {
96  return $cObj->createUrl([
97  'parameter' => $pageRecord['canonical_link'],
98  'forceAbsoluteUrl' => true,
99  ]);
100  }
101  return '';
102  }
103 
104  protected function ‪checkContentFromPid(ServerRequestInterface $request): string
105  {
106  $pageInformation = $request->getAttribute('frontend.page.information');
107  $id = $pageInformation->getId();
108  $contentPid = $pageInformation->getContentFromPid();
109  if ($id !== $contentPid) {
110  $targetPid = $contentPid;
111  if ($targetPid > 0) {
112  $pageRepository = GeneralUtility::makeInstance(PageRepository::class);
113  $targetPageRecord = $pageRepository->getPage($contentPid, true);
114  if (!empty($targetPageRecord['canonical_link'])) {
115  $targetPid = $targetPageRecord['canonical_link'];
116  }
117  $cObj = GeneralUtility::makeInstance(ContentObjectRenderer::class, $request->getAttribute('frontend.controller'));
118  $cObj->setRequest($request);
119  $cObj->start($request->getAttribute('frontend.page.information')->getPageRecord(), 'pages');
120  return $cObj->createUrl([
121  'parameter' => $targetPid,
122  'forceAbsoluteUrl' => true,
123  ]);
124  }
125  }
126  return '';
127  }
128 
129  protected function ‪checkDefaultCanonical(ServerRequestInterface $request): string
130  {
131  $pageInformation = $request->getAttribute('frontend.page.information');
132  $id = $pageInformation->getId();
133  // We should only create a canonical link to the target, if the target is within a valid site root
134  $inSiteRoot = $this->‪isPageWithinSiteRoot($id);
135  if (!$inSiteRoot) {
136  return '';
137  }
138 
139  // Temporarily remove current mount point information as we want to have the
140  // URL of the target page and not of the page within the mount point if the
141  // current page is a mount point.
142  $pageInformation = clone $pageInformation;
143  $pageInformation->setMountPoint('');
144  $request = $request->withAttribute('frontend.page.information', $pageInformation);
145  $cObj = GeneralUtility::makeInstance(ContentObjectRenderer::class, $request->getAttribute('frontend.controller'));
146  $cObj->setRequest($request);
147  $cObj->start($pageInformation->getPageRecord(), 'pages');
148  return $cObj->createUrl([
149  'parameter' => $id . ',' . $request->getAttribute('routing')->getPageType(),
150  'forceAbsoluteUrl' => true,
151  'addQueryString' => true,
152  'addQueryString.' => [
153  'exclude' => implode(
154  ',',
156  $id,
157  (array)‪$GLOBALS['TYPO3_CONF_VARS']['FE']['additionalCanonicalizedUrlParameters']
158  )
159  ),
160  ],
161  ]);
162  }
163 
164  protected function ‪isPageWithinSiteRoot(int $id): bool
165  {
166  $rootline = GeneralUtility::makeInstance(RootlineUtility::class, $id)->get();
167  foreach ($rootline as $page) {
168  if ($page['is_siteroot']) {
169  return true;
170  }
171  }
172  return false;
173  }
174 }
‪TYPO3\CMS\Seo\Canonical\CanonicalGenerator\__construct
‪__construct(private EventDispatcherInterface $eventDispatcher,)
Definition: CanonicalGenerator.php:38
‪TYPO3\CMS\Core\Domain\Page
Definition: Page.php:24
‪TYPO3\CMS\Core\Utility\RootlineUtility
Definition: RootlineUtility.php:40
‪TYPO3\CMS\Seo\Canonical\CanonicalGenerator\checkContentFromPid
‪checkContentFromPid(ServerRequestInterface $request)
Definition: CanonicalGenerator.php:104
‪TYPO3\CMS\Seo\Canonical\CanonicalGenerator\checkForCanonicalLink
‪checkForCanonicalLink(ServerRequestInterface $request)
Definition: CanonicalGenerator.php:88
‪TYPO3\CMS\Seo\Canonical\CanonicalGenerator\checkDefaultCanonical
‪checkDefaultCanonical(ServerRequestInterface $request)
Definition: CanonicalGenerator.php:129
‪TYPO3\CMS\Frontend\Utility\CanonicalizationUtility\getParamsToExcludeForCanonicalizedUrl
‪static getParamsToExcludeForCanonicalizedUrl(int $pageId, array $additionalCanonicalizedUrlParameters=[])
Definition: CanonicalizationUtility.php:40
‪TYPO3\CMS\Seo\Canonical\CanonicalGenerator\isPageWithinSiteRoot
‪isPageWithinSiteRoot(int $id)
Definition: CanonicalGenerator.php:164
‪TYPO3\CMS\Seo\Canonical\CanonicalGenerator
Definition: CanonicalGenerator.php:37
‪TYPO3\CMS\Seo\Exception\CanonicalGenerationDisabledException
Definition: CanonicalGenerationDisabledException.php:25
‪TYPO3\CMS\Seo\Event\ModifyUrlForCanonicalTagEvent
Definition: ModifyUrlForCanonicalTagEvent.php:28
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Frontend\Utility\CanonicalizationUtility
Definition: CanonicalizationUtility.php:26
‪TYPO3\CMS\Seo\Canonical\CanonicalGenerator\generate
‪generate(array $params)
Definition: CanonicalGenerator.php:42
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer
Definition: ContentObjectRenderer.php:102
‪TYPO3\CMS\Seo\Canonical
Definition: CanonicalGenerator.php:18
‪TYPO3\CMS\Core\Domain\Repository\PageRepository
Definition: PageRepository.php:69
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52