vendor/jms/i18n-routing-bundle/JMS/I18nRoutingBundle/Router/I18nRouter.php line 179

Open in your IDE?
  1. <?php
  2. /*
  3.  * Copyright 2012 Johannes M. Schmitt <[email protected]>
  4.  *
  5.  * Licensed under the Apache License, Version 2.0 (the "License");
  6.  * you may not use this file except in compliance with the License.
  7.  * You may obtain a copy of the License at
  8.  *
  9.  * http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. namespace JMS\I18nRoutingBundle\Router;
  18. use JMS\I18nRoutingBundle\Exception\NotAcceptableLanguageException;
  19. use Symfony\Component\Routing\Matcher\RequestMatcherInterface;
  20. use Symfony\Component\Routing\RequestContext;
  21. use Symfony\Component\DependencyInjection\ContainerInterface;
  22. use Symfony\Bundle\FrameworkBundle\Routing\Router;
  23. use Symfony\Component\Routing\Exception\RouteNotFoundException;
  24. use Symfony\Component\Routing\Exception\ResourceNotFoundException;
  25. use Symfony\Component\HttpFoundation\Request;
  26. /**
  27.  * I18n Router implementation.
  28.  *
  29.  * @author Johannes M. Schmitt <[email protected]>
  30.  */
  31. class I18nRouter extends Router
  32. {
  33.     private $hostMap = array();
  34.     private $i18nLoaderId;
  35.     private $container;
  36.     private $defaultLocale;
  37.     private $redirectToHost true;
  38.     private $localeResolver;
  39.     /**
  40.      * Constructor.
  41.      *
  42.      * The only purpose of this is to make the container available in the sub-class
  43.      * since it is declared private in the parent class.
  44.      *
  45.      * The parameters are not listed explicitly here because they are different for
  46.      * Symfony 2.0 and 2.1. If we did list them, it would make this class incompatible
  47.      * with one of both versions.
  48.      */
  49.     public function __construct()
  50.     {
  51.         call_user_func_array(array('Symfony\Bundle\FrameworkBundle\Routing\Router''__construct'), func_get_args());
  52.         $this->container func_get_arg(0);
  53.     }
  54.     public function setLocaleResolver(LocaleResolverInterface $resolver)
  55.     {
  56.         $this->localeResolver $resolver;
  57.     }
  58.     /**
  59.      * Whether the user should be redirected to a different host if the
  60.      * matching route is not belonging to the current domain.
  61.      *
  62.      * @param Boolean $bool
  63.      */
  64.     public function setRedirectToHost($bool)
  65.     {
  66.         $this->redirectToHost = (Boolean) $bool;
  67.     }
  68.     /**
  69.      * Sets the host map to use.
  70.      *
  71.      * @param array $hostMap a map of locales to hosts
  72.      */
  73.     public function setHostMap(array $hostMap)
  74.     {
  75.         $this->hostMap $hostMap;
  76.     }
  77.     public function setI18nLoaderId($id)
  78.     {
  79.         $this->i18nLoaderId $id;
  80.     }
  81.     public function setDefaultLocale($locale)
  82.     {
  83.         $this->defaultLocale $locale;
  84.     }
  85.     /**
  86.      * {@inheritdoc}
  87.      */
  88.     public function generate($name$parameters = array(), $referenceType self::ABSOLUTE_PATH)
  89.     {
  90.         // determine the most suitable locale to use for route generation
  91.         $currentLocale $this->context->getParameter('_locale');
  92.         if (isset($parameters['_locale'])) {
  93.             $locale $parameters['_locale'];
  94.         } else if ($currentLocale) {
  95.             $locale $currentLocale;
  96.         } else {
  97.             $locale $this->defaultLocale;
  98.         }
  99.         // if the locale is changed, and we have a host map, then we need to
  100.         // generate an absolute URL
  101.         if ($currentLocale && $currentLocale !== $locale && $this->hostMap) {
  102.             $referenceType self::NETWORK_PATH === $referenceType self::NETWORK_PATH self::ABSOLUTE_URL;
  103.         }
  104.         $needsHost self::NETWORK_PATH === $referenceType || self::ABSOLUTE_URL === $referenceType;
  105.         $generator $this->getGenerator();
  106.         // if an absolute or network URL is requested, we set the correct host
  107.         if ($needsHost && $this->hostMap) {
  108.             $currentHost $this->context->getHost();
  109.             $this->context->setHost($this->hostMap[$locale]);
  110.         }
  111.         try {
  112.             $url $generator->generate($locale.I18nLoader::ROUTING_PREFIX.$name$parameters$referenceType);
  113.             if ($needsHost && $this->hostMap) {
  114.                 $this->context->setHost($currentHost);
  115.             }
  116.             return $url;
  117.         } catch (RouteNotFoundException $ex) {
  118.             if ($needsHost && $this->hostMap) {
  119.                 $this->context->setHost($currentHost);
  120.             }
  121.             // fallback to default behavior
  122.         }
  123.         // use the default behavior if no localized route exists
  124.         return $generator->generate($name$parameters$referenceType);
  125.     }
  126.     /**
  127.      * {@inheritdoc}
  128.      */
  129.     public function match($url)
  130.     {
  131.         return $this->matchI18n(parent::match($url), $url);
  132.     }
  133.     public function getRouteCollection()
  134.     {
  135.         $collection parent::getRouteCollection();
  136.         return $this->container->get($this->i18nLoaderId)->load($collection);
  137.     }
  138.     public function getOriginalRouteCollection()
  139.     {
  140.         return parent::getRouteCollection();
  141.     }
  142.     /**
  143.      * To make compatible with Symfony <2.4
  144.      */
  145.     public function matchRequest(Request $request)
  146.     {
  147.         $matcher $this->getMatcher();
  148.         $pathInfo $request->getPathInfo();
  149.         if (!$matcher instanceof RequestMatcherInterface) {
  150.             // fallback to the default UrlMatcherInterface
  151.             return $this->matchI18n($matcher->match($pathInfo), $pathInfo);
  152.         }
  153.         return $this->matchI18n($matcher->matchRequest($request), $pathInfo);
  154.     }
  155.     private function matchI18n(array $params$url)
  156.     {
  157.         if (false === $params) {
  158.             return false;
  159.         }
  160.         $request $this->getRequest();
  161.         if (isset($params['_locales'])) {
  162.             if (false !== $pos strpos($params['_route'], I18nLoader::ROUTING_PREFIX)) {
  163.                 $params['_route'] = substr($params['_route'], $pos strlen(I18nLoader::ROUTING_PREFIX));
  164.             }
  165.             if (!($currentLocale $this->context->getParameter('_locale'))
  166.                     && null !== $request) {
  167.                 $currentLocale $this->localeResolver->resolveLocale(
  168.                     $request$params['_locales']
  169.                 );
  170.                 // If the locale resolver was not able to determine a locale, then all efforts to
  171.                 // make an informed decision have failed. Just display something as a last resort.
  172.                 if (!$currentLocale) {
  173.                     $currentLocale reset($params['_locales']);
  174.                 }
  175.             }
  176.             if (!in_array($currentLocale$params['_locales'], true)) {
  177.                 // TODO: We might want to allow the user to be redirected to the route for the given locale if
  178.                 //       it exists regardless of whether it would be on another domain, or the same domain.
  179.                 //       Below we assume that we do not want to redirect always.
  180.                 // if the available locales are on a different host, throw a ResourceNotFoundException
  181.                 if ($this->hostMap) {
  182.                     // generate host maps
  183.                     $hostMap $this->hostMap;
  184.                     $availableHosts array_map(function($locale) use ($hostMap) {
  185.                         return $hostMap[$locale];
  186.                     }, $params['_locales']);
  187.                     $differentHost true;
  188.                     foreach ($availableHosts as $host) {
  189.                         if ($this->hostMap[$currentLocale] === $host) {
  190.                             $differentHost false;
  191.                             break;
  192.                         }
  193.                     }
  194.                     if ($differentHost) {
  195.                         throw new ResourceNotFoundException(sprintf('The route "%s" is not available on the current host "%s", but only on these hosts "%s".',
  196.                             $params['_route'], $this->hostMap[$currentLocale], implode(', '$availableHosts)));
  197.                     }
  198.                 }
  199.                 // no host map, or same host means that the given locale is not supported for this route
  200.                 throw new NotAcceptableLanguageException($currentLocale$params['_locales']);
  201.             }
  202.             unset($params['_locales']);
  203.             $params['_locale'] = $currentLocale;
  204.         } else if (isset($params['_locale']) && $pos strpos($params['_route'], I18nLoader::ROUTING_PREFIX)) {
  205.             $params['_route'] = substr($params['_route'], $pos strlen(I18nLoader::ROUTING_PREFIX));
  206.         }
  207.         // check if the matched route belongs to a different locale on another host
  208.         if (isset($params['_locale'])
  209.                 && isset($this->hostMap[$params['_locale']])
  210.                 && $this->context->getHost() !== $host $this->hostMap[$params['_locale']]) {
  211.             if (!$this->redirectToHost) {
  212.                 throw new ResourceNotFoundException(sprintf(
  213.                     'Resource corresponding to pattern "%s" not found for locale "%s".'$url$this->getContext()->getParameter('_locale')));
  214.             }
  215.             return array(
  216.                 '_controller' => 'JMS\I18nRoutingBundle\Controller\RedirectController::redirectAction',
  217.                 'path'        => $url,
  218.                 'host'        => $host,
  219.                 'permanent'   => true,
  220.                 'scheme'      => $this->context->getScheme(),
  221.                 'httpPort'    => $this->context->getHttpPort(),
  222.                 'httpsPort'   => $this->context->getHttpsPort(),
  223.                 '_route'      => $params['_route'],
  224.             );
  225.         }
  226.         // if we have no locale set on the route, we try to set one according to the localeResolver
  227.         // if we don't do this all _internal routes will have the default locale on first request
  228.         if (!isset($params['_locale'])
  229.                 && null !== $request
  230.                 && $locale $this->localeResolver->resolveLocale(
  231.                         $request,
  232.                         $this->container->getParameter('jms_i18n_routing.locales'))) {
  233.             $params['_locale'] = $locale;
  234.         }
  235.         return $params;
  236.     }
  237.     /**
  238.      * @return Request|null
  239.      */
  240.     private function getRequest()
  241.     {
  242.         $request null;
  243.         if ($this->container->has('request_stack')) {
  244.             $request $this->container->get('request_stack')->getCurrentRequest();
  245.         } elseif (method_exists($this->container'isScopeActive') && $this->container->isScopeActive('request')) {
  246.             $request $this->container->get('request');
  247.         }
  248.         return $request;
  249.     }
  250. }