3161ccd421ddd1962f9fccf8049655e6d877e237
[yaffs-website] / web / modules / contrib / redirect / src / EventSubscriber / RedirectRequestSubscriber.php
1 <?php
2
3 namespace Drupal\redirect\EventSubscriber;
4
5 use Drupal\Core\Cache\CacheableMetadata;
6 use Drupal\Core\Config\ConfigFactoryInterface;
7 use Drupal\Core\Entity\EntityManagerInterface;
8 use Drupal\Core\Extension\ModuleHandlerInterface;
9 use Drupal\Core\Language\LanguageManagerInterface;
10 use Drupal\Core\Logger\RfcLogLevel;
11 use Drupal\Core\Path\AliasManager;
12 use Drupal\Core\PathProcessor\InboundPathProcessorInterface;
13 use Drupal\Core\Routing\TrustedRedirectResponse;
14 use Drupal\Core\Url;
15 use Drupal\redirect\Exception\RedirectLoopException;
16 use Drupal\redirect\RedirectChecker;
17 use Drupal\redirect\RedirectRepository;
18 use Symfony\Component\HttpFoundation\Response;
19 use Symfony\Component\HttpKernel\HttpKernelInterface;
20 use Symfony\Component\HttpKernel\KernelEvents;
21 use Symfony\Component\HttpKernel\Event\GetResponseEvent;
22 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
23 use Symfony\Component\Routing\RequestContext;
24
25 /**
26  * Redirect subscriber for controller requests.
27  */
28 class RedirectRequestSubscriber implements EventSubscriberInterface {
29
30   /** @var  \Drupal\redirect\RedirectRepository */
31   protected $redirectRepository;
32
33   /**
34    * @var \Drupal\Core\Language\LanguageManagerInterface
35    */
36   protected $languageManager;
37
38   /**
39    * @var \Drupal\Core\Config\Config
40    */
41   protected $config;
42
43   /**
44    * @var \Drupal\Core\Path\AliasManager
45    */
46   protected $aliasManager;
47
48   /**
49    * @var \Drupal\Core\Extension\ModuleHandlerInterface
50    */
51   protected $moduleHandler;
52
53   /**
54    * @var \Drupal\Core\Entity\EntityManagerInterface
55    */
56   protected $entityManager;
57
58   /**
59    * @var \Drupal\redirect\RedirectChecker
60    */
61   protected $checker;
62
63   /**
64    * @var \Symfony\Component\Routing\RequestContext
65    */
66   protected $context;
67
68   /**
69    * A path processor manager for resolving the system path.
70    *
71    * @var \Drupal\Core\PathProcessor\InboundPathProcessorInterface
72    */
73   protected $pathProcessor;
74
75   /**
76    * Constructs a \Drupal\redirect\EventSubscriber\RedirectRequestSubscriber object.
77    *
78    * @param \Drupal\redirect\RedirectRepository $redirect_repository
79    *   The redirect entity repository.
80    * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
81    *   The language manager service.
82    * @param \Drupal\Core\Config\ConfigFactoryInterface $config
83    *   The config.
84    * @param \Drupal\Core\Path\AliasManager $alias_manager
85    *   The alias manager service.
86    * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
87    *   The module handler service.
88    * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
89    *   The entity manager service.
90    * @param \Drupal\redirect\RedirectChecker $checker
91    *   The redirect checker service.
92    * @param \Symfony\Component\Routing\RequestContext
93    *   Request context.
94    */
95   public function __construct(RedirectRepository $redirect_repository, LanguageManagerInterface $language_manager, ConfigFactoryInterface $config, AliasManager $alias_manager, ModuleHandlerInterface $module_handler, EntityManagerInterface $entity_manager, RedirectChecker $checker, RequestContext $context, InboundPathProcessorInterface $path_processor) {
96     $this->redirectRepository = $redirect_repository;
97     $this->languageManager = $language_manager;
98     $this->config = $config->get('redirect.settings');
99     $this->aliasManager = $alias_manager;
100     $this->moduleHandler = $module_handler;
101     $this->entityManager = $entity_manager;
102     $this->checker = $checker;
103     $this->context = $context;
104     $this->pathProcessor = $path_processor;
105   }
106
107   /**
108    * Handles the redirect if any found.
109    *
110    * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event
111    *   The event to process.
112    */
113   public function onKernelRequestCheckRedirect(GetResponseEvent $event) {
114     // Get a clone of the request. During inbound processing the request
115     // can be altered. Allowing this here can lead to unexpected behavior.
116     // For example the path_processor.files inbound processor provided by
117     // the system module alters both the path and the request; only the
118     // changes to the request will be propagated, while the change to the
119     // path will be lost.
120     $request = clone $event->getRequest();
121
122     if (!$this->checker->canRedirect($request)) {
123       return;
124     }
125
126     // Get URL info and process it to be used for hash generation.
127     parse_str($request->getQueryString(), $request_query);
128
129     // Do the inbound processing so that for example language prefixes are
130     // removed.
131     $path = $this->pathProcessor->processInbound($request->getPathInfo(), $request);
132     $path = trim($path, '/');
133
134     $this->context->fromRequest($request);
135
136     try {
137       $redirect = $this->redirectRepository->findMatchingRedirect($path, $request_query, $this->languageManager->getCurrentLanguage()->getId());
138     }
139     catch (RedirectLoopException $e) {
140       \Drupal::logger('redirect')->warning($e->getMessage());
141       $response = new Response();
142       $response->setStatusCode(503);
143       $response->setContent('Service unavailable');
144       $event->setResponse($response);
145       return;
146     }
147
148     if (!empty($redirect)) {
149
150       // Handle internal path.
151       $url = $redirect->getRedirectUrl();
152       if ($this->config->get('passthrough_querystring')) {
153         $url->setOption('query', (array) $url->getOption('query') + $request_query);
154       }
155       $headers = [
156         'X-Redirect-ID' => $redirect->id(),
157       ];
158       $response = new TrustedRedirectResponse($url->setAbsolute()->toString(), $redirect->getStatusCode(), $headers);
159       $response->addCacheableDependency($redirect);
160       $event->setResponse($response);
161     }
162   }
163
164   /**
165    * Prior to set the response it check if we can redirect.
166    *
167    * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event
168    *   The event object.
169    * @param \Drupal\Core\Url $url
170    *   The Url where we want to redirect.
171    */
172   protected function setResponse(GetResponseEvent $event, Url $url) {
173     $request = $event->getRequest();
174     $this->context->fromRequest($request);
175
176     parse_str($request->getQueryString(), $query);
177     $url->setOption('query', $query);
178     $url->setAbsolute(TRUE);
179
180     // We can only check access for routed URLs.
181     if (!$url->isRouted() || $this->checker->canRedirect($request, $url->getRouteName())) {
182       // Add the 'rendered' cache tag, so that we can invalidate all responses
183       // when settings are changed.
184       $response = new TrustedRedirectResponse($url->toString(), 301);
185       $response->addCacheableDependency(CacheableMetadata::createFromRenderArray([])->addCacheTags(['rendered']));
186       $event->setResponse($response);
187     }
188   }
189
190   /**
191    * {@inheritdoc}
192    */
193   public static function getSubscribedEvents() {
194     // This needs to run before RouterListener::onKernelRequest(), which has
195     // a priority of 32. Otherwise, that aborts the request if no matching
196     // route is found.
197     $events[KernelEvents::REQUEST][] = array('onKernelRequestCheckRedirect', 33);
198     return $events;
199   }
200
201 }