vendor/ezsystems/ezplatform-kernel/eZ/Publish/Core/MVC/Symfony/SiteAccess/Router.php line 16

Open in your IDE?
  1. <?php
  2. /**
  3.  * @copyright Copyright (C) Ibexa AS. All rights reserved.
  4.  * @license For full copyright and license information view LICENSE file distributed with this source code.
  5.  */
  6. namespace eZ\Publish\Core\MVC\Symfony\SiteAccess;
  7. use eZ\Publish\Core\MVC\Symfony\SiteAccess;
  8. use eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest;
  9. use eZ\Publish\Core\MVC\Exception\InvalidSiteAccessException;
  10. use Psr\Log\LoggerInterface;
  11. use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\CompoundInterface;
  12. use InvalidArgumentException;
  13. class Router implements SiteAccessRouterInterfaceSiteAccessAware
  14. {
  15.     public const HEADER_SA_MATCHING_TYPE 'header';
  16.     public const ENV_SA_MATCHING_TYPE 'env';
  17.     /**
  18.      * Name of the default siteaccess.
  19.      *
  20.      * @var string
  21.      */
  22.     protected $defaultSiteAccess;
  23.     /**
  24.      * The configuration for siteaccess matching.
  25.      * Consists in an hash indexed by matcher type class.
  26.      * Value is a hash where index is what to match against and value is the corresponding siteaccess name.
  27.      *
  28.      * Example:
  29.      * <code>
  30.      * array(
  31.      *     // Using built-in URI matcher. Key is the prefix that matches the siteaccess, in the value
  32.      *     "Map\\URI" => array(
  33.      *         "ezdemo_site" => "ezdemo_site",
  34.      *         "ezdemo_site_admin" => "ezdemo_site_admin",
  35.      *     ),
  36.      *     // Using built-in HOST matcher. Key is the hostname, value is the siteaccess name
  37.      *     "Map\\Host" => array(
  38.      *         "ezpublish.dev" => "ezdemo_site",
  39.      *         "ezpublish.admin.dev" => "ezdemo_site_admin",
  40.      *     ),
  41.      *     // Using a custom matcher (class must begin with a '\', as a full qualified class name).
  42.      *     // The custom matcher must implement eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher interface.
  43.      *     "\\My\\Custom\\Matcher" => array(
  44.      *         "something_to_match_against" => "siteaccess_name"
  45.      *     )
  46.      * )
  47.      * </code>
  48.      *
  49.      * @var array
  50.      */
  51.     protected $siteAccessesConfiguration;
  52.     /** @var \eZ\Publish\Core\MVC\Symfony\SiteAccess\SiteAccessProviderInterface */
  53.     protected $siteAccessProvider;
  54.     /** @var \eZ\Publish\Core\MVC\Symfony\SiteAccess */
  55.     protected $siteAccess;
  56.     /** @var string */
  57.     protected $siteAccessClass;
  58.     /** @var \Psr\Log\LoggerInterface */
  59.     protected $logger;
  60.     /** @var \eZ\Publish\Core\MVC\Symfony\SiteAccess\MatcherBuilderInterface */
  61.     protected $matcherBuilder;
  62.     /** @var \eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest */
  63.     protected $request;
  64.     /** @var bool */
  65.     protected $debug;
  66.     /**
  67.      * @param \eZ\Publish\Core\MVC\Symfony\SiteAccess\MatcherBuilderInterface $matcherBuilder
  68.      * @param \Psr\Log\LoggerInterface $logger
  69.      * @param string $defaultSiteAccess
  70.      * @param array $siteAccessesConfiguration
  71.      * @param \eZ\Publish\Core\MVC\Symfony\SiteAccess\SiteAccessProviderInterface $siteAccessProvider
  72.      * @param string|null $siteAccessClass
  73.      * @param bool $debug
  74.      */
  75.     public function __construct(
  76.         MatcherBuilderInterface $matcherBuilder,
  77.         LoggerInterface $logger,
  78.         $defaultSiteAccess,
  79.         array $siteAccessesConfiguration,
  80.         SiteAccessProviderInterface $siteAccessProvider,
  81.         $siteAccessClass null,
  82.         $debug false
  83.     ) {
  84.         $this->matcherBuilder $matcherBuilder;
  85.         $this->logger $logger;
  86.         $this->defaultSiteAccess $defaultSiteAccess;
  87.         $this->siteAccessesConfiguration $siteAccessesConfiguration;
  88.         $this->siteAccessProvider $siteAccessProvider;
  89.         $this->siteAccessClass $siteAccessClass ?: 'eZ\\Publish\\Core\\MVC\\Symfony\\SiteAccess';
  90.         $this->request = new SimplifiedRequest();
  91.         $this->debug $debug;
  92.     }
  93.     /**
  94.      * @return \eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest
  95.      */
  96.     public function getRequest()
  97.     {
  98.         return $this->request;
  99.     }
  100.     /**
  101.      * Performs SiteAccess matching given the $request.
  102.      *
  103.      * @param \eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest $request
  104.      *
  105.      * @throws \eZ\Publish\Core\MVC\Exception\InvalidSiteAccessException
  106.      *
  107.      * @return \eZ\Publish\Core\MVC\Symfony\SiteAccess
  108.      */
  109.     public function match(SimplifiedRequest $request)
  110.     {
  111.         $this->request $request;
  112.         if (isset($this->siteAccess)) {
  113.             return $this->siteAccess;
  114.         }
  115.         // Request header always have precedence
  116.         // Note: request headers are always in lower cased.
  117.         if (!empty($request->headers['x-siteaccess'])) {
  118.             $siteaccessName $request->headers['x-siteaccess'][0];
  119.             if (!$this->siteAccessProvider->isDefined($siteaccessName)) {
  120.                 throw new InvalidSiteAccessException(
  121.                     $siteaccessName,
  122.                     $this->siteAccessProvider,
  123.                     'X-Siteaccess request header',
  124.                     $this->debug
  125.                 );
  126.             }
  127.             $this->siteAccess $this->siteAccessProvider->getSiteAccess($siteaccessName);
  128.             $this->siteAccess->matchingType self::HEADER_SA_MATCHING_TYPE;
  129.             return $this->siteAccess;
  130.         }
  131.         // Then check environment variable
  132.         $siteaccessEnvName getenv('EZPUBLISH_SITEACCESS');
  133.         if ($siteaccessEnvName !== false) {
  134.             if (!$this->siteAccessProvider->isDefined($siteaccessEnvName)) {
  135.                 throw new InvalidSiteAccessException(
  136.                     $siteaccessEnvName,
  137.                     $this->siteAccessProvider,
  138.                     'EZPUBLISH_SITEACCESS environment variable',
  139.                     $this->debug
  140.                 );
  141.             }
  142.             $this->siteAccess $this->siteAccessProvider->getSiteAccess($siteaccessEnvName);
  143.             $this->siteAccess->matchingType self::ENV_SA_MATCHING_TYPE;
  144.             return $this->siteAccess;
  145.         }
  146.         return $this->doMatch($request);
  147.     }
  148.     /**
  149.      * Returns the SiteAccess object matched against $request and the siteaccess configuration.
  150.      * If nothing could be matched, the default siteaccess is returned, with "default" as matching type.
  151.      *
  152.      * @param \eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest $request
  153.      *
  154.      * @return \eZ\Publish\Core\MVC\Symfony\SiteAccess
  155.      */
  156.     private function doMatch(SimplifiedRequest $request)
  157.     {
  158.         foreach ($this->siteAccessesConfiguration as $matchingClass => $matchingConfiguration) {
  159.             $matcher $this->matcherBuilder->buildMatcher($matchingClass$matchingConfiguration$request);
  160.             if ($matcher instanceof CompoundInterface) {
  161.                 $matcher->setMatcherBuilder($this->matcherBuilder);
  162.             }
  163.             $siteAccessName $matcher->match();
  164.             if ($siteAccessName !== false && $this->siteAccessProvider->isDefined($siteAccessName)) {
  165.                 $this->siteAccess $this->siteAccessProvider->getSiteAccess($siteAccessName);
  166.                 $this->siteAccess->matchingType $matcher->getName();
  167.                 $this->siteAccess->matcher $matcher;
  168.                 return $this->siteAccess;
  169.             }
  170.         }
  171.         $this->logger->notice('Siteaccess not matched against configuration, returning default siteaccess.');
  172.         $this->siteAccess = new $this->siteAccessClass($this->defaultSiteAccess);
  173.         $this->siteAccess->matchingType SiteAccess::DEFAULT_MATCHING_TYPE;
  174.         return $this->siteAccess;
  175.     }
  176.     /**
  177.      * Matches a SiteAccess by name.
  178.      * Returns corresponding SiteAccess object, according to configuration, with corresponding matcher.
  179.      * Returns null if no matcher can be found (e.g. non versatile).
  180.      *
  181.      * @param string $siteAccessName
  182.      *
  183.      * @throws \InvalidArgumentException If $siteAccessName is invalid (i.e. not present in configured list).
  184.      *
  185.      * @return \eZ\Publish\Core\MVC\Symfony\SiteAccess|null
  186.      */
  187.     public function matchByName($siteAccessName)
  188.     {
  189.         if (!$this->siteAccessProvider->isDefined($siteAccessName)) {
  190.             throw new InvalidArgumentException("Invalid SiteAccess name provided for reverse matching: $siteAccessName");
  191.         }
  192.         $request = clone $this->request;
  193.         // Be sure to have a clean pathinfo, without SiteAccess part in it.
  194.         if ($this->siteAccess && $this->siteAccess->matcher instanceof URILexer) {
  195.             $request->setPathinfo($this->siteAccess->matcher->analyseURI($request->pathinfo));
  196.         }
  197.         $siteAccessClass $this->siteAccessClass;
  198.         foreach ($this->siteAccessesConfiguration as $matchingClass => $matchingConfiguration) {
  199.             $matcher $this->matcherBuilder->buildMatcher($matchingClass$matchingConfiguration$request);
  200.             if (!$matcher instanceof VersatileMatcher) {
  201.                 continue;
  202.             }
  203.             if ($matcher instanceof CompoundInterface) {
  204.                 $matcher->setMatcherBuilder($this->matcherBuilder);
  205.             }
  206.             $reverseMatcher $matcher->reverseMatch($siteAccessName);
  207.             if (!$reverseMatcher instanceof Matcher) {
  208.                 continue;
  209.             }
  210.             /** @var \eZ\Publish\Core\MVC\Symfony\SiteAccess $siteAccess */
  211.             $siteAccess = new $siteAccessClass($siteAccessName);
  212.             $siteAccess->matcher $reverseMatcher;
  213.             $siteAccess->matchingType $reverseMatcher->getName();
  214.             return $siteAccess;
  215.         }
  216.         // No VersatileMatcher configured for $siteAccessName.
  217.         $this->logger->notice("Siteaccess '$siteAccessName' could not be reverse-matched against configuration. No VersatileMatcher found. Returning default SiteAccess.");
  218.         return new $siteAccessClass($this->defaultSiteAccess'default');
  219.     }
  220.     /**
  221.      * @return \eZ\Publish\Core\MVC\Symfony\SiteAccess|null
  222.      */
  223.     public function getSiteAccess()
  224.     {
  225.         return $this->siteAccess;
  226.     }
  227.     /**
  228.      * @param \eZ\Publish\Core\MVC\Symfony\SiteAccess $siteAccess
  229.      */
  230.     public function setSiteAccess(SiteAccess $siteAccess null)
  231.     {
  232.         $this->siteAccess $siteAccess;
  233.     }
  234. }