vendor/pimcore/pimcore/lib/Routing/RedirectHandler.php line 88

Open in your IDE?
  1. <?php
  2. /**
  3.  * Pimcore
  4.  *
  5.  * This source file is available under two different licenses:
  6.  * - GNU General Public License version 3 (GPLv3)
  7.  * - Pimcore Enterprise License (PEL)
  8.  * Full copyright and license information is available in
  9.  * LICENSE.md which is distributed with this source code.
  10.  *
  11.  * @copyright  Copyright (c) Pimcore GmbH (http://www.pimcore.org)
  12.  * @license    http://www.pimcore.org/license     GPLv3 and PEL
  13.  */
  14. namespace Pimcore\Routing;
  15. use Pimcore\Cache;
  16. use Pimcore\Config;
  17. use Pimcore\Http\Request\Resolver\SiteResolver;
  18. use Pimcore\Http\RequestHelper;
  19. use Pimcore\Model\Document;
  20. use Pimcore\Model\Redirect;
  21. use Pimcore\Model\Site;
  22. use Pimcore\Model\Tool\Lock;
  23. use Pimcore\Routing\Redirect\RedirectUrlPartResolver;
  24. use Pimcore\Tool;
  25. use Psr\Log\LoggerAwareInterface;
  26. use Psr\Log\LoggerAwareTrait;
  27. use Symfony\Component\HttpFoundation\RedirectResponse;
  28. use Symfony\Component\HttpFoundation\Request;
  29. use Symfony\Component\HttpFoundation\Response;
  30. class RedirectHandler implements LoggerAwareInterface
  31. {
  32.     use LoggerAwareTrait;
  33.     /**
  34.      * @var RequestHelper
  35.      */
  36.     private $requestHelper;
  37.     /**
  38.      * @var SiteResolver
  39.      */
  40.     private $siteResolver;
  41.     /**
  42.      * @var Redirect[]
  43.      */
  44.     private $redirects;
  45.     /**
  46.      * For BC, this is currently added as extra method call. The required annotation
  47.      * makes sure this is called via autowiring.
  48.      *
  49.      * TODO Pimcore 6 set as constructor dependency
  50.      *
  51.      * @required
  52.      *
  53.      * @param RequestHelper $requestHelper
  54.      */
  55.     public function setRequestHelper(RequestHelper $requestHelper)
  56.     {
  57.         $this->requestHelper $requestHelper;
  58.     }
  59.     /**
  60.      * For BC, this is currently added as extra method call. The required annotation
  61.      * makes sure this is called via autowiring.
  62.      *
  63.      * TODO Pimcore 6 set as constructor dependency
  64.      *
  65.      * @required
  66.      *
  67.      * @param SiteResolver $siteResolver
  68.      */
  69.     public function setSiteResolver(SiteResolver $siteResolver)
  70.     {
  71.         $this->siteResolver $siteResolver;
  72.     }
  73.     /**
  74.      * @param Request $request
  75.      * @param bool $override
  76.      *
  77.      * @return null|Response
  78.      */
  79.     public function checkForRedirect(Request $request$override false)
  80.     {
  81.         // not for admin requests
  82.         if ($this->requestHelper->isFrontendRequestByAdmin($request)) {
  83.             return null;
  84.         }
  85.         // get current site if available
  86.         $sourceSite null;
  87.         if ($this->siteResolver->isSiteRequest($request)) {
  88.             $sourceSite $this->siteResolver->getSite($request);
  89.         }
  90.         $config Config::getSystemConfig();
  91.         $partResolver = new RedirectUrlPartResolver($request);
  92.         foreach ($this->getFilteredRedirects($override) as $redirect) {
  93.             if (null !== $response $this->matchRedirect($redirect$request$partResolver$config$sourceSite)) {
  94.                 return $response;
  95.             }
  96.         }
  97.     }
  98.     private function matchRedirect(
  99.         Redirect $redirect,
  100.         Request $request,
  101.         RedirectUrlPartResolver $partResolver,
  102.         Config\Config $config,
  103.         Site $sourceSite null
  104.     ) {
  105.         if (empty($redirect->getType())) {
  106.             return null;
  107.         }
  108.         $matchPart $partResolver->getRequestUriPart($redirect->getType());
  109.         $matches = [];
  110.         $doesMatch false;
  111.         if ($redirect->isRegex()) {
  112.             $doesMatch = (bool)@preg_match($redirect->getSource(), $matchPart$matches);
  113.         } else {
  114.             $source str_replace('+'' '$redirect->getSource()); // see #2202
  115.             $doesMatch $source === $matchPart;
  116.         }
  117.         if (!$doesMatch) {
  118.             return null;
  119.         }
  120.         // check for a site
  121.         if ($redirect->getSourceSite() || $sourceSite) {
  122.             if (!$sourceSite || $sourceSite->getId() !== $redirect->getSourceSite()) {
  123.                 return null;
  124.             }
  125.         }
  126.         $target $redirect->getTarget();
  127.         if (is_numeric($target)) {
  128.             $d Document::getById($target);
  129.             if ($d instanceof Document\Page || $d instanceof Document\Link || $d instanceof Document\Hardlink) {
  130.                 $target $d->getFullPath();
  131.             } else {
  132.                 $this->logger->error('Target of redirect {redirect} not found (Document-ID: {document})', [
  133.                     'redirect' => $redirect->getId(),
  134.                     'document' => $target
  135.                 ]);
  136.                 return null;
  137.             }
  138.         }
  139.         $url $target;
  140.         if ($redirect->isRegex()) {
  141.             array_shift($matches);
  142.             // support for pcre backreferences
  143.             $url replace_pcre_backreferences($url$matches);
  144.         }
  145.         if ($redirect->getTargetSite() && !preg_match('@http(s)?://@i'$url)) {
  146.             if ($targetSite Site::getById($redirect->getTargetSite())) {
  147.                 // if the target site is specified and and the target-path is starting at root (not absolute to site)
  148.                 // the root-path will be replaced so that the page can be shown
  149.                 $url preg_replace('@^' $targetSite->getRootPath() . '/@''/'$url);
  150.                 $url $request->getScheme() . '://' $targetSite->getMainDomain() . $url;
  151.             } else {
  152.                 $this->logger->error('Site with ID {targetSite} not found', [
  153.                     'redirect' => $redirect->getId(),
  154.                     'targetSite' => $redirect->getTargetSite()
  155.                 ]);
  156.                 return null;
  157.             }
  158.         } elseif (!preg_match('@http(s)?://@i'$url) && $config->general->domain) {
  159.             // prepend the host and scheme to avoid infinite loops when using "domain" redirects
  160.             $url $request->getScheme() . '://' $config->general->domain $url;
  161.         }
  162.         // pass-through parameters if specified
  163.         $queryString $request->getQueryString();
  164.         if ($redirect->getPassThroughParameters() && !empty($queryString)) {
  165.             $glue '?';
  166.             if (strpos($url'?')) {
  167.                 $glue '&';
  168.             }
  169.             $url .= $glue;
  170.             $url .= $queryString;
  171.         }
  172.         $statusCode $redirect->getStatusCode() ?: Response::HTTP_MOVED_PERMANENTLY;
  173.         $response = new RedirectResponse($url$statusCode);
  174.         $response->headers->set('X-Pimcore-ID'$redirect->getId());
  175.         // log all redirects to the redirect log
  176.         \Pimcore\Log\Simple::log(
  177.             'redirect',
  178.             Tool::getAnonymizedClientIp() . " \t Custom-Redirect ID: " $redirect->getId() . ', Source: ' $_SERVER['REQUEST_URI'] . ' -> ' $url
  179.         );
  180.         return $response;
  181.     }
  182.     /**
  183.      * @return Redirect[]
  184.      */
  185.     private function getRedirects()
  186.     {
  187.         if (null !== $this->redirects && is_array($this->redirects)) {
  188.             return $this->redirects;
  189.         }
  190.         $cacheKey 'system_route_redirect';
  191.         if (($this->redirects Cache::load($cacheKey)) === false) {
  192.             // acquire lock to avoid concurrent redirect cache warm-up
  193.             Lock::acquire($cacheKey);
  194.             //check again if redirects are cached to avoid re-warming cache
  195.             if (($this->redirects Cache::load($cacheKey)) === false) {
  196.                 try {
  197.                     $list = new Redirect\Listing();
  198.                     $list->setCondition('active = 1');
  199.                     $list->setOrder('DESC');
  200.                     $list->setOrderKey('priority');
  201.                     $this->redirects $list->load();
  202.                     Cache::save($this->redirects$cacheKey, ['system''redirect''route'], null998true);
  203.                 } catch (\Exception $e) {
  204.                     $this->logger->error('Failed to load redirects');
  205.                 }
  206.             }
  207.             Lock::release($cacheKey);
  208.         }
  209.         if (!is_array($this->redirects)) {
  210.             $this->logger->warning('Failed to load redirects', [
  211.                 'redirects' => $this->redirects
  212.             ]);
  213.             $this->redirects = [];
  214.         }
  215.         return $this->redirects;
  216.     }
  217.     /**
  218.      * @param bool $override
  219.      *
  220.      * @return Redirect[]
  221.      */
  222.     private function getFilteredRedirects($override false)
  223.     {
  224.         $now time();
  225.         return array_filter($this->getRedirects(), function (Redirect $redirect) use ($override$now) {
  226.             // this is the case when maintenance did't deactivate the redirect yet but it is already expired
  227.             if (!empty($redirect->getExpiry()) && $redirect->getExpiry() < $now) {
  228.                 return false;
  229.             }
  230.             if ($override) {
  231.                 // if override is true the priority has to be 99 which means that overriding is ok
  232.                 return (int)$redirect->getPriority() === 99;
  233.             } else {
  234.                 return (int)$redirect->getPriority() !== 99;
  235.             }
  236.         });
  237.     }
  238. }