* * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FOS\JsRoutingBundle\Extractor; use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouteCollection; use Symfony\Component\Routing\RouterInterface; use JMS\I18nRoutingBundle\Router\I18nLoader; /** * @author William DURAND */ class ExposedRoutesExtractor implements ExposedRoutesExtractorInterface { /** * @var RouterInterface */ protected $router; /** * Base cache directory * * @var string */ protected $cacheDir; /** * @var array */ protected $bundles; /** * @var string */ protected $pattern; /** * @var array */ protected $availableDomains; /** * Default constructor. * * @param RouterInterface $router The router. * @param array $routesToExpose Some route names to expose. * @param string $cacheDir * @param array $bundles list of loaded bundles to check when generating the prefix * * @throws \Exception */ public function __construct(RouterInterface $router, array $routesToExpose, $cacheDir, $bundles = array()) { $this->router = $router; $this->cacheDir = $cacheDir; $this->bundles = $bundles; $domainPatterns = $this->extractDomainPatterns($routesToExpose); $this->availableDomains = array_keys($domainPatterns); $this->pattern = $this->buildPattern($domainPatterns); } /** * {@inheritDoc} */ public function getRoutes() { $collection = $this->router->getRouteCollection(); $routes = new RouteCollection(); /** @var Route $route */ foreach ($collection->all() as $name => $route) { if ($route->hasOption('expose')) { $expose = $route->getOption('expose'); if ($expose !== false && $expose !== 'false') { $routes->add($name, $route); } continue; } preg_match('#^' . $this->pattern . '$#', $name, $matches); if (count($matches) === 0) { continue; } $domain = $this->getDomainByRouteMatches($matches, $name); if (is_null($domain)) { continue; } $route = clone $route; $route->setOption('expose', $domain); $routes->add($name, $route); } return $routes; } /** * {@inheritDoc} */ public function getBaseUrl() { return $this->router->getContext()->getBaseUrl() ?: ''; } /** * {@inheritDoc} */ public function getPrefix($locale) { if (isset($this->bundles['JMSI18nRoutingBundle'])) { return $locale . I18nLoader::ROUTING_PREFIX; } return ''; } /** * {@inheritDoc} */ public function getHost() { $requestContext = $this->router->getContext(); $host = $requestContext->getHost() . ('' === $this->getPort() ? $this->getPort() : ':' . $this->getPort()); return $host; } /** * {@inheritDoc} */ public function getPort() { $requestContext = $this->router->getContext(); $port=""; if ($this->usesNonStandardPort()) { $method = sprintf('get%sPort', ucfirst($requestContext->getScheme())); $port = $requestContext->$method(); } return $port; } /** * {@inheritDoc} */ public function getScheme() { return $this->router->getContext()->getScheme(); } /** * {@inheritDoc} */ public function getCachePath($locale) { $cachePath = $this->cacheDir . DIRECTORY_SEPARATOR . 'fosJsRouting'; if (!file_exists($cachePath)) { mkdir($cachePath); } if (isset($this->bundles['JMSI18nRoutingBundle'])) { $cachePath = $cachePath . DIRECTORY_SEPARATOR . 'data.' . $locale . '.json'; } else { $cachePath = $cachePath . DIRECTORY_SEPARATOR . 'data.json'; } return $cachePath; } /** * {@inheritDoc} */ public function getResources() { return $this->router->getRouteCollection()->getResources(); } /** * {@inheritDoc} */ public function isRouteExposed(Route $route, $name) { if (false === $route->hasOption('expose')) { return ('' !== $this->pattern && preg_match('#^' . $this->pattern . '$#', $name)); } $status = $route->getOption('expose'); return ($status !== false && $status !== 'false'); } protected function getDomainByRouteMatches($matches, $name) { $matches = array_filter($matches, function($match) { return !empty($match); }); $matches = array_flip(array_intersect_key($matches, array_flip($this->availableDomains))); return isset($matches[$name]) ? $matches[$name] : null; } protected function extractDomainPatterns($routesToExpose) { $domainPatterns = array(); foreach ($routesToExpose as $item) { if (is_string($item)) { $domainPatterns['default'][] = $item; continue; } if (is_array($item) && is_string($item['pattern'])) { if (!isset($item['domain'])) { $domainPatterns['default'][] = $item['pattern']; continue; } elseif (is_string($item['domain'])) { $domainPatterns[$item['domain']][] = $item['pattern']; continue; } } throw new \Exception('routes_to_expose definition is invalid'); } return $domainPatterns; } /** * Convert the routesToExpose array in a regular expression pattern * * @param $domainPatterns * @return string * @throws \Exception */ protected function buildPattern($domainPatterns) { $patterns = array(); foreach ($domainPatterns as $domain => $items) { $patterns[] = '(?P<' . $domain . '>' . implode('|', $items) . ')'; } return implode('|', $patterns); } /** * Check whether server is serving this request from a non-standard port * * @return bool */ private function usesNonStandardPort() { return $this->usesNonStandardHttpPort() || $this->usesNonStandardHttpsPort(); } /** * Check whether server is serving HTTP over a non-standard port * * @return bool */ private function usesNonStandardHttpPort() { return 'http' === $this->getScheme() && '80' != $this->router->getContext()->getHttpPort(); } /** * Check whether server is serving HTTPS over a non-standard port * * @return bool */ private function usesNonStandardHttpsPort() { return 'https' === $this->getScheme() && '443' != $this->router->getContext()->getHttpsPort(); } }