Chris@0: Chris@0: * Chris@0: * For the full copyright and license information, please view the LICENSE Chris@0: * file that was distributed with this source code. Chris@0: */ Chris@0: Chris@0: namespace Symfony\Component\Routing; Chris@0: Chris@0: use Symfony\Component\Config\Exception\FileLoaderLoadException; Chris@0: use Symfony\Component\Config\Loader\LoaderInterface; Chris@0: use Symfony\Component\Config\Resource\ResourceInterface; Chris@0: Chris@0: /** Chris@0: * Helps add and import routes into a RouteCollection. Chris@0: * Chris@0: * @author Ryan Weaver Chris@0: */ Chris@0: class RouteCollectionBuilder Chris@0: { Chris@0: /** Chris@0: * @var Route[]|RouteCollectionBuilder[] Chris@0: */ Chris@17: private $routes = []; Chris@0: Chris@0: private $loader; Chris@17: private $defaults = []; Chris@0: private $prefix; Chris@0: private $host; Chris@0: private $condition; Chris@17: private $requirements = []; Chris@17: private $options = []; Chris@0: private $schemes; Chris@0: private $methods; Chris@17: private $resources = []; Chris@0: Chris@0: public function __construct(LoaderInterface $loader = null) Chris@0: { Chris@0: $this->loader = $loader; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Import an external routing resource and returns the RouteCollectionBuilder. Chris@0: * Chris@17: * $routes->import('blog.yml', '/blog'); Chris@0: * Chris@0: * @param mixed $resource Chris@0: * @param string|null $prefix Chris@0: * @param string $type Chris@0: * Chris@0: * @return self Chris@0: * Chris@0: * @throws FileLoaderLoadException Chris@0: */ Chris@0: public function import($resource, $prefix = '/', $type = null) Chris@0: { Chris@14: /** @var RouteCollection[] $collection */ Chris@14: $collections = $this->load($resource, $type); Chris@0: Chris@0: // create a builder from the RouteCollection Chris@0: $builder = $this->createBuilder(); Chris@0: Chris@14: foreach ($collections as $collection) { Chris@14: if (null === $collection) { Chris@14: continue; Chris@14: } Chris@14: Chris@14: foreach ($collection->all() as $name => $route) { Chris@14: $builder->addRoute($route, $name); Chris@14: } Chris@14: Chris@14: foreach ($collection->getResources() as $resource) { Chris@14: $builder->addResource($resource); Chris@14: } Chris@0: } Chris@0: Chris@0: // mount into this builder Chris@0: $this->mount($prefix, $builder); Chris@0: Chris@0: return $builder; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Adds a route and returns it for future modification. Chris@0: * Chris@0: * @param string $path The route path Chris@0: * @param string $controller The route's controller Chris@0: * @param string|null $name The name to give this route Chris@0: * Chris@0: * @return Route Chris@0: */ Chris@0: public function add($path, $controller, $name = null) Chris@0: { Chris@0: $route = new Route($path); Chris@0: $route->setDefault('_controller', $controller); Chris@0: $this->addRoute($route, $name); Chris@0: Chris@0: return $route; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Returns a RouteCollectionBuilder that can be configured and then added with mount(). Chris@0: * Chris@0: * @return self Chris@0: */ Chris@0: public function createBuilder() Chris@0: { Chris@0: return new self($this->loader); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Add a RouteCollectionBuilder. Chris@0: * Chris@0: * @param string $prefix Chris@0: * @param RouteCollectionBuilder $builder Chris@0: */ Chris@16: public function mount($prefix, self $builder) Chris@0: { Chris@0: $builder->prefix = trim(trim($prefix), '/'); Chris@0: $this->routes[] = $builder; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Adds a Route object to the builder. Chris@0: * Chris@0: * @param Route $route Chris@0: * @param string|null $name Chris@0: * Chris@0: * @return $this Chris@0: */ Chris@0: public function addRoute(Route $route, $name = null) Chris@0: { Chris@0: if (null === $name) { Chris@0: // used as a flag to know which routes will need a name later Chris@0: $name = '_unnamed_route_'.spl_object_hash($route); Chris@0: } Chris@0: Chris@0: $this->routes[$name] = $route; Chris@0: Chris@0: return $this; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Sets the host on all embedded routes (unless already set). Chris@0: * Chris@0: * @param string $pattern Chris@0: * Chris@0: * @return $this Chris@0: */ Chris@0: public function setHost($pattern) Chris@0: { Chris@0: $this->host = $pattern; Chris@0: Chris@0: return $this; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Sets a condition on all embedded routes (unless already set). Chris@0: * Chris@0: * @param string $condition Chris@0: * Chris@0: * @return $this Chris@0: */ Chris@0: public function setCondition($condition) Chris@0: { Chris@0: $this->condition = $condition; Chris@0: Chris@0: return $this; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Sets a default value that will be added to all embedded routes (unless that Chris@0: * default value is already set). Chris@0: * Chris@0: * @param string $key Chris@0: * @param mixed $value Chris@0: * Chris@0: * @return $this Chris@0: */ Chris@0: public function setDefault($key, $value) Chris@0: { Chris@0: $this->defaults[$key] = $value; Chris@0: Chris@0: return $this; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Sets a requirement that will be added to all embedded routes (unless that Chris@0: * requirement is already set). Chris@0: * Chris@0: * @param string $key Chris@0: * @param mixed $regex Chris@0: * Chris@0: * @return $this Chris@0: */ Chris@0: public function setRequirement($key, $regex) Chris@0: { Chris@0: $this->requirements[$key] = $regex; Chris@0: Chris@0: return $this; Chris@0: } Chris@0: Chris@0: /** Chris@14: * Sets an option that will be added to all embedded routes (unless that Chris@0: * option is already set). Chris@0: * Chris@0: * @param string $key Chris@0: * @param mixed $value Chris@0: * Chris@0: * @return $this Chris@0: */ Chris@0: public function setOption($key, $value) Chris@0: { Chris@0: $this->options[$key] = $value; Chris@0: Chris@0: return $this; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Sets the schemes on all embedded routes (unless already set). Chris@0: * Chris@0: * @param array|string $schemes Chris@0: * Chris@0: * @return $this Chris@0: */ Chris@0: public function setSchemes($schemes) Chris@0: { Chris@0: $this->schemes = $schemes; Chris@0: Chris@0: return $this; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Sets the methods on all embedded routes (unless already set). Chris@0: * Chris@0: * @param array|string $methods Chris@0: * Chris@0: * @return $this Chris@0: */ Chris@0: public function setMethods($methods) Chris@0: { Chris@0: $this->methods = $methods; Chris@0: Chris@0: return $this; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Adds a resource for this collection. Chris@0: * Chris@0: * @return $this Chris@0: */ Chris@0: private function addResource(ResourceInterface $resource) Chris@0: { Chris@0: $this->resources[] = $resource; Chris@0: Chris@0: return $this; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Creates the final RouteCollection and returns it. Chris@0: * Chris@0: * @return RouteCollection Chris@0: */ Chris@0: public function build() Chris@0: { Chris@0: $routeCollection = new RouteCollection(); Chris@0: Chris@0: foreach ($this->routes as $name => $route) { Chris@0: if ($route instanceof Route) { Chris@0: $route->setDefaults(array_merge($this->defaults, $route->getDefaults())); Chris@0: $route->setOptions(array_merge($this->options, $route->getOptions())); Chris@0: Chris@0: foreach ($this->requirements as $key => $val) { Chris@0: if (!$route->hasRequirement($key)) { Chris@0: $route->setRequirement($key, $val); Chris@0: } Chris@0: } Chris@0: Chris@0: if (null !== $this->prefix) { Chris@0: $route->setPath('/'.$this->prefix.$route->getPath()); Chris@0: } Chris@0: Chris@0: if (!$route->getHost()) { Chris@0: $route->setHost($this->host); Chris@0: } Chris@0: Chris@0: if (!$route->getCondition()) { Chris@0: $route->setCondition($this->condition); Chris@0: } Chris@0: Chris@0: if (!$route->getSchemes()) { Chris@0: $route->setSchemes($this->schemes); Chris@0: } Chris@0: Chris@0: if (!$route->getMethods()) { Chris@0: $route->setMethods($this->methods); Chris@0: } Chris@0: Chris@0: // auto-generate the route name if it's been marked Chris@0: if ('_unnamed_route_' === substr($name, 0, 15)) { Chris@0: $name = $this->generateRouteName($route); Chris@0: } Chris@0: Chris@0: $routeCollection->add($name, $route); Chris@0: } else { Chris@0: /* @var self $route */ Chris@0: $subCollection = $route->build(); Chris@0: $subCollection->addPrefix($this->prefix); Chris@0: Chris@0: $routeCollection->addCollection($subCollection); Chris@0: } Chris@14: } Chris@0: Chris@14: foreach ($this->resources as $resource) { Chris@14: $routeCollection->addResource($resource); Chris@0: } Chris@0: Chris@0: return $routeCollection; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Generates a route name based on details of this route. Chris@0: * Chris@0: * @return string Chris@0: */ Chris@0: private function generateRouteName(Route $route) Chris@0: { Chris@0: $methods = implode('_', $route->getMethods()).'_'; Chris@0: Chris@0: $routeName = $methods.$route->getPath(); Chris@17: $routeName = str_replace(['/', ':', '|', '-'], '_', $routeName); Chris@0: $routeName = preg_replace('/[^a-z0-9A-Z_.]+/', '', $routeName); Chris@0: Chris@0: // Collapse consecutive underscores down into a single underscore. Chris@0: $routeName = preg_replace('/_+/', '_', $routeName); Chris@0: Chris@0: return $routeName; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Finds a loader able to load an imported resource and loads it. Chris@0: * Chris@0: * @param mixed $resource A resource Chris@0: * @param string|null $type The resource type or null if unknown Chris@0: * Chris@14: * @return RouteCollection[] Chris@0: * Chris@0: * @throws FileLoaderLoadException If no loader is found Chris@0: */ Chris@0: private function load($resource, $type = null) Chris@0: { Chris@0: if (null === $this->loader) { Chris@0: throw new \BadMethodCallException('Cannot import other routing resources: you must pass a LoaderInterface when constructing RouteCollectionBuilder.'); Chris@0: } Chris@0: Chris@0: if ($this->loader->supports($resource, $type)) { Chris@14: $collections = $this->loader->load($resource, $type); Chris@14: Chris@17: return \is_array($collections) ? $collections : [$collections]; Chris@0: } Chris@0: Chris@0: if (null === $resolver = $this->loader->getResolver()) { Chris@14: throw new FileLoaderLoadException($resource, null, null, null, $type); Chris@0: } Chris@0: Chris@0: if (false === $loader = $resolver->resolve($resource, $type)) { Chris@14: throw new FileLoaderLoadException($resource, null, null, null, $type); Chris@0: } Chris@0: Chris@14: $collections = $loader->load($resource, $type); Chris@14: Chris@17: return \is_array($collections) ? $collections : [$collections]; Chris@0: } Chris@0: }