vendor/suncat/mobile-detect-bundle/SunCat/MobileDetectBundle/EventListener/RequestResponseListener.php line 173

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of the MobileDetectBundle.
  4.  *
  5.  * (c) Nikolay Ivlev <nikolay.kotovsky@gmail.com>
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. namespace SunCat\MobileDetectBundle\EventListener;
  11. use SunCat\MobileDetectBundle\DeviceDetector\MobileDetector;
  12. use SunCat\MobileDetectBundle\Helper\DeviceView;
  13. use SunCat\MobileDetectBundle\Helper\RedirectResponseWithCookie;
  14. use Symfony\Component\HttpFoundation\RedirectResponse;
  15. use Symfony\Component\HttpFoundation\Request;
  16. use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
  17. use Symfony\Component\HttpKernel\Event\GetResponseEvent;
  18. use Symfony\Component\HttpKernel\HttpKernelInterface;
  19. use Symfony\Component\Routing\Route;
  20. use Symfony\Component\Routing\RouterInterface;
  21. /**
  22.  * Request and response listener
  23.  *
  24.  * @author suncat2000 <nikolay.kotovsky@gmail.com>
  25.  * @author HenriVesala <henri.vesala@gmail.com>
  26.  */
  27. class RequestResponseListener
  28. {
  29.     const REDIRECT                  'redirect';
  30.     const NO_REDIRECT               'no_redirect';
  31.     const REDIRECT_WITHOUT_PATH     'redirect_without_path';
  32.     const MOBILE    'mobile';
  33.     const TABLET    'tablet';
  34.     const FULL      'full';
  35.     /**
  36.      * @var MobileDetector
  37.      */
  38.     protected $mobileDetector;
  39.     /**
  40.      * @var DeviceView
  41.      */
  42.     protected $deviceView;
  43.     /**
  44.      * @var array
  45.      */
  46.     protected $redirectConf;
  47.     /**
  48.      * @var bool
  49.      */
  50.     protected $isFullPath;
  51.     /**
  52.      * @var bool
  53.      */
  54.     protected $needModifyResponse false;
  55.     /**
  56.      * @var \Closure
  57.      */
  58.     protected $modifyResponseClosure;
  59.     /**
  60.      * RequestResponseListener constructor.
  61.      *
  62.      * @param MobileDetector  $mobileDetector
  63.      * @param DeviceView      $deviceView
  64.      * @param RouterInterface $router
  65.      * @param array           $redirectConf
  66.      * @param bool            $fullPath
  67.      */
  68.     public function __construct(
  69.         MobileDetector $mobileDetector,
  70.         DeviceView $deviceView,
  71.         RouterInterface $router,
  72.         array $redirectConf,
  73.         $fullPath true
  74.     ) {
  75.         $this->mobileDetector $mobileDetector;
  76.         $this->deviceView $deviceView;
  77.         $this->router $router;
  78.         // Configs mobile & tablet
  79.         $this->redirectConf $redirectConf;
  80.         $this->isFullPath $fullPath;
  81.     }
  82.     /**
  83.      * Handles the Request
  84.      *
  85.      * @param GetResponseEvent $event
  86.      *
  87.      * @return null
  88.      */
  89.     public function handleRequest(GetResponseEvent $event)
  90.     {
  91.         // only handle master request, do not handle sub request like esi includes
  92.         // If the device view is "not the mobile view" (e.g. we're not in the request context)
  93.         if ($event->getRequestType() !== HttpKernelInterface::MASTER_REQUEST || $this->deviceView->isNotMobileView()) {
  94.             return;
  95.         }
  96.         $request $event->getRequest();
  97.         $this->mobileDetector->setUserAgent($request->headers->get('user-agent'));
  98.         // Sets the flag for the response handled by the GET switch param and the type of the view.
  99.         if ($this->deviceView->hasSwitchParam()) {
  100.             $event->setResponse($this->getRedirectResponseBySwitchParam($request));
  101.             return;
  102.         }
  103.         // If neither the SwitchParam nor the cookie are set, detect the view...
  104.         $cookieIsSet $this->deviceView->getRequestedViewType() !== null;
  105.         if (!$cookieIsSet) {
  106.             if ($this->redirectConf['detect_tablet_as_mobile'] === false && $this->mobileDetector->isTablet()) {
  107.                 $this->deviceView->setTabletView();
  108.             } elseif ($this->mobileDetector->isMobile()) {
  109.                 $this->deviceView->setMobileView();
  110.             } else {
  111.                 $this->deviceView->setFullView();
  112.             }
  113.         }
  114.         // Check if we must redirect to the target view and do so if needed
  115.         if ($this->mustRedirect($request$this->deviceView->getViewType())) {
  116.             if (($response $this->getRedirectResponse($request$this->deviceView->getViewType()))) {
  117.                 $event->setResponse($response);
  118.             }
  119.             return;
  120.         }
  121.         // No need to redirect
  122.         // We don't need to modify _every_ response: once the cookie is set,
  123.         // save bandwith and CPU cycles by just letting it expire someday.
  124.         if ($cookieIsSet) {
  125.             return;
  126.         }
  127.         // Sets the flag for the response handler and prepares the modification closure
  128.         $this->needModifyResponse true;
  129.         $this->prepareResponseModification($this->deviceView->getViewType());
  130.     }
  131.     /**
  132.      * Will this request listener modify the response? This flag will be set during the "handleRequest" phase.
  133.      * Made public for testability.
  134.      *
  135.      * @return boolean True if the response needs to be modified.
  136.      */
  137.     public function needsResponseModification()
  138.     {
  139.         return $this->needModifyResponse;
  140.     }
  141.     /**
  142.      * Handles the Response
  143.      *
  144.      * @param FilterResponseEvent $event
  145.      *
  146.      * @return null
  147.      */
  148.     public function handleResponse(FilterResponseEvent $event)
  149.     {
  150.         if ($this->needModifyResponse && $this->modifyResponseClosure instanceof \Closure) {
  151.             $modifyClosure $this->modifyResponseClosure;
  152.             $event->setResponse($modifyClosure($this->deviceView$event));
  153.             return;
  154.         }
  155.     }
  156.     /**
  157.      * Do we have to redirect?
  158.      *
  159.      * @param Request $request
  160.      * @param string  $view    For which view should be check?
  161.      *
  162.      * @return boolean
  163.      */
  164.     protected function mustRedirect(Request $request$view)
  165.     {
  166.         if (!isset($this->redirectConf[$view]) ||
  167.             !$this->redirectConf[$view]['is_enabled'] ||
  168.             ($this->getRoutingOption($request->get('_route'), $view) === self::NO_REDIRECT)
  169.         ) {
  170.             return false;
  171.         }
  172.         $isHost = ($this->getCurrentHost($request) === $this->redirectConf[$view]['host']);
  173.         if (!$isHost) {
  174.             return true;
  175.         }
  176.         return false;
  177.     }
  178.     /**
  179.      * Prepares the response modification which will take place after the controller logic has been executed.
  180.      *
  181.      * @param string $view The view for which to prepare the response modification.
  182.      *
  183.      * @return boolean
  184.      */
  185.     protected function prepareResponseModification($view)
  186.     {
  187.         $this->modifyResponseClosure = function (DeviceView $deviceViewFilterResponseEvent $event) use ($view) {
  188.             return $deviceView->modifyResponse($view$event->getResponse());
  189.         };
  190.     }
  191.     /**
  192.      * Gets the RedirectResponse by switch param.
  193.      *
  194.      * @param Request $request
  195.      *
  196.      * @return RedirectResponseWithCookie
  197.      */
  198.     protected function getRedirectResponseBySwitchParam(Request $request)
  199.     {
  200.         if ($this->mustRedirect($request$this->deviceView->getViewType())) {
  201.             // Avoid unnecessary redirects: if we need to redirect to another view,
  202.             // do it in one response while setting the cookie.
  203.             $redirectUrl $this->getRedirectUrl($request$this->deviceView->getViewType());
  204.         } else {
  205.             if (true === $this->isFullPath) {
  206.                 $redirectUrl $request->getUriForPath($request->getPathInfo());
  207.                 $queryParams $request->query->all();
  208.                 if (array_key_exists($this->deviceView->getSwitchParam(), $queryParams)) {
  209.                     unset($queryParams[$this->deviceView->getSwitchParam()]);
  210.                 }
  211.                 if (sizeof($queryParams) > 0) {
  212.                     $redirectUrl .= '?'.Request::normalizeQueryString(http_build_query($queryParamsnull'&'));
  213.                 }
  214.             } else {
  215.                 $redirectUrl $this->getCurrentHost($request);
  216.             }
  217.         }
  218.         return $this->deviceView->getRedirectResponseBySwitchParam($redirectUrl);
  219.     }
  220.     /**
  221.      * Gets the RedirectResponse for the specified view.
  222.      *
  223.      * @param Request $request
  224.      * @param string  $view    The view for which we want the RedirectResponse.
  225.      *
  226.      * @return RedirectResponse|null
  227.      */
  228.     protected function getRedirectResponse(Request $request$view)
  229.     {
  230.         if (($host $this->getRedirectUrl($request$view))) {
  231.             return $this->deviceView->getRedirectResponse(
  232.                 $view,
  233.                 $host,
  234.                 $this->redirectConf[$view]['status_code']
  235.             );
  236.         }
  237.         return null;
  238.     }
  239.     /**
  240.      * Gets the redirect url.
  241.      *
  242.      * @param Request $request
  243.      * @param string  $platform
  244.      *
  245.      * @return string|null
  246.      */
  247.     protected function getRedirectUrl(Request $request$platform)
  248.     {
  249.         if (($routingOption $this->getRoutingOption($request->get('_route'), $platform))) {
  250.             if (self::REDIRECT === $routingOption) {
  251.                 // Make sure to hint at the device override, otherwise infinite loop
  252.                 // redirection may occur if different device views are hosted on
  253.                 // different domains (since the cookie can't be shared across domains)
  254.                 $queryParams $request->query->all();
  255.                 $queryParams[$this->deviceView->getSwitchParam()] = $platform;
  256.                 return rtrim($this->redirectConf[$platform]['host'], '/').$request->getPathInfo().'?'.Request::normalizeQueryString(http_build_query($queryParamsnull'&'));
  257.             } elseif (self::REDIRECT_WITHOUT_PATH === $routingOption) {
  258.                 // Make sure to hint at the device override, otherwise infinite loop
  259.                 // redirections may occur if different device views are hosted on
  260.                 // different domains (since the cookie can't be shared across domains)
  261.                 return $this->redirectConf[$platform]['host'].'?'.$this->deviceView->getSwitchParam().'='.$platform;
  262.             } else {
  263.                 return null;
  264.             }
  265.         } else {
  266.             return null;
  267.         }
  268.     }
  269.     /**
  270.      * Gets named option from current route.
  271.      *
  272.      * @param string $routeName
  273.      * @param string $optionName
  274.      *
  275.      * @return string|null
  276.      */
  277.     protected function getRoutingOption($routeName$optionName)
  278.     {
  279.         $option null;
  280.         $route $this->router->getRouteCollection()->get($routeName);
  281.         if ($route instanceof Route) {
  282.             $option $route->getOption($optionName);
  283.         }
  284.         if (!$option && isset($this->redirectConf[$optionName])) {
  285.             $option $this->redirectConf[$optionName]['action'];
  286.         }
  287.         if (in_array($option, array(self::REDIRECTself::REDIRECT_WITHOUT_PATHself::NO_REDIRECT))) {
  288.             return $option;
  289.         }
  290.         return null;
  291.     }
  292.     /**
  293.      * Gets the current host.
  294.      *
  295.      * @param Request $request
  296.      *
  297.      * @return string
  298.      */
  299.     protected function getCurrentHost(Request $request)
  300.     {
  301.         return $request->getScheme().'://'.$request->getHost();
  302.     }
  303. }