[BUGFIX] Make redirect sources with Query matchable
[Packages/TYPO3.CMS.git] / typo3 / sysext / redirects / Classes / Http / Middleware / RedirectHandler.php
1 <?php
2 declare(strict_types = 1);
3 namespace TYPO3\CMS\Redirects\Http\Middleware;
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
18 use Psr\Http\Message\ResponseInterface;
19 use Psr\Http\Message\ServerRequestInterface;
20 use Psr\Http\Message\UriInterface;
21 use Psr\Http\Server\MiddlewareInterface;
22 use Psr\Http\Server\RequestHandlerInterface;
23 use Psr\Log\LoggerAwareInterface;
24 use Psr\Log\LoggerAwareTrait;
25 use TYPO3\CMS\Core\Configuration\Features;
26 use TYPO3\CMS\Core\Database\ConnectionPool;
27 use TYPO3\CMS\Core\Http\RedirectResponse;
28 use TYPO3\CMS\Core\Utility\GeneralUtility;
29 use TYPO3\CMS\Redirects\Service\RedirectService;
30
31 /**
32 * Hooks into the frontend request, and checks if a redirect should apply,
33 * If so, a redirect response is triggered.
34 *
35 * @internal
36 */
37 class RedirectHandler implements MiddlewareInterface, LoggerAwareInterface
38 {
39 use LoggerAwareTrait;
40
41 /**
42 * First hook within the Frontend Request handling
43 *
44 * @param ServerRequestInterface $request
45 * @param RequestHandlerInterface $handler
46 * @return ResponseInterface
47 */
48 public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
49 {
50 $redirectService = GeneralUtility::makeInstance(RedirectService::class);
51 $port = $request->getUri()->getPort();
52 $matchedRedirect = $redirectService->matchRedirect(
53 $request->getUri()->getHost() . ($port ? ':' . $port : ''),
54 $request->getUri()->getPath(),
55 $request->getUri()->getQuery() ?? ''
56 );
57
58 // If the matched redirect is found, resolve it, and check further
59 if (is_array($matchedRedirect)) {
60 $url = $redirectService->getTargetUrl($matchedRedirect, $request->getQueryParams());
61 if ($url instanceof UriInterface) {
62 $this->logger->debug('Redirecting', ['record' => $matchedRedirect, 'uri' => $url]);
63 $response = $this->buildRedirectResponse($url, $matchedRedirect);
64 $this->incrementHitCount($matchedRedirect);
65
66 return $response;
67 }
68 }
69
70 return $handler->handle($request);
71 }
72
73 /**
74 * Creates a PSR-7 compatible Response object
75 *
76 * @param UriInterface $uri
77 * @param array $redirectRecord
78 * @return ResponseInterface
79 */
80 protected function buildRedirectResponse(UriInterface $uri, array $redirectRecord): ResponseInterface
81 {
82 return new RedirectResponse(
83 $uri,
84 (int)$redirectRecord['target_statuscode'],
85 ['X-Redirect-By' => 'TYPO3 Redirect ' . $redirectRecord['uid']]
86 );
87 }
88
89 /**
90 * Updates the sys_record's hit counter by one
91 *
92 * @param array $redirectRecord
93 */
94 protected function incrementHitCount(array $redirectRecord)
95 {
96 // Track the hit if not disabled
97 if (!GeneralUtility::makeInstance(Features::class)->isFeatureEnabled('redirects.hitCount') || $redirectRecord['disable_hitcount']) {
98 return;
99 }
100 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
101 ->getQueryBuilderForTable('sys_redirect');
102 $queryBuilder
103 ->update('sys_redirect')
104 ->where(
105 $queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($redirectRecord['uid'], \PDO::PARAM_INT))
106 )
107 ->set('hitcount', $queryBuilder->quoteIdentifier('hitcount') . '+1', false)
108 ->set('lasthiton', $GLOBALS['EXEC_TIME'])
109 ->execute();
110 }
111 }