diff --git a/library/Zend/Mvc/Router/Http/Chain.php b/library/Zend/Mvc/Router/Http/Chain.php index d65f81d838a..ff5274a5883 100644 --- a/library/Zend/Mvc/Router/Http/Chain.php +++ b/library/Zend/Mvc/Router/Http/Chain.php @@ -9,6 +9,7 @@ namespace Zend\Mvc\Router\Http; +use ArrayObject; use Traversable; use Zend\Mvc\Router\Exception; use Zend\Mvc\Router\PriorityList; @@ -40,13 +41,14 @@ class Chain extends TreeRouteStack implements RouteInterface * * @param array $routes * @param RoutePluginManager $routePlugins - * @throws Exception\InvalidArgumentException + * @param ArrayObject $prototypes */ - public function __construct(array $routes, RoutePluginManager $routePlugins) + public function __construct(array $routes, RoutePluginManager $routePlugins, ArrayObject $prototypes) { $this->chainRoutes = array_reverse($routes); $this->routePluginManager = $routePlugins; $this->routes = new PriorityList(); + $this->prototypes = $prototypes; } /** @@ -69,6 +71,10 @@ public static function factory($options = array()) throw new Exception\InvalidArgumentException('Missing "route" in options array'); } + if (!isset($options['prototypes'])) { + throw new Exception\InvalidArgumentException('Missing "prototypes" in options array'); + } + if ($options['routes'] instanceof Traversable) { $options['routes'] = ArrayUtils::iteratorToArray($options['child_routes']); } @@ -77,7 +83,11 @@ public static function factory($options = array()) throw new Exception\InvalidArgumentException('Missing "route_plugins" in options array'); } - return new static($options['routes'], $options['route_plugins']); + return new static( + $options['routes'], + $options['route_plugins'], + $options['prototypes'] + ); } /** diff --git a/library/Zend/Mvc/Router/Http/Part.php b/library/Zend/Mvc/Router/Http/Part.php index 1f66a83f582..28e12f5d3a7 100644 --- a/library/Zend/Mvc/Router/Http/Part.php +++ b/library/Zend/Mvc/Router/Http/Part.php @@ -9,6 +9,7 @@ namespace Zend\Mvc\Router\Http; +use ArrayObject; use Traversable; use Zend\Mvc\Router\Exception; use Zend\Mvc\Router\PriorityList; @@ -48,12 +49,13 @@ class Part extends TreeRouteStack implements RouteInterface * Create a new part route. * * @param mixed $route - * @param bool $mayTerminate + * @param bool $mayTerminate * @param RoutePluginManager $routePlugins * @param array|null $childRoutes + * @param ArrayObject $prototypes * @throws Exception\InvalidArgumentException */ - public function __construct($route, $mayTerminate, RoutePluginManager $routePlugins, array $childRoutes = null) + public function __construct($route, $mayTerminate, RoutePluginManager $routePlugins, array $childRoutes = null, ArrayObject $prototypes = null) { $this->routePluginManager = $routePlugins; @@ -68,6 +70,7 @@ public function __construct($route, $mayTerminate, RoutePluginManager $routePlug $this->route = $route; $this->mayTerminate = $mayTerminate; $this->childRoutes = $childRoutes; + $this->prototypes = $prototypes; $this->routes = new PriorityList(); } @@ -95,6 +98,10 @@ public static function factory($options = array()) throw new Exception\InvalidArgumentException('Missing "route_plugins" in options array'); } + if (!isset($options['prototypes'])) { + $options['prototypes'] = null; + } + if (!isset($options['may_terminate'])) { $options['may_terminate'] = false; } @@ -106,7 +113,13 @@ public static function factory($options = array()) $options['child_routes'] = ArrayUtils::iteratorToArray($options['child_routes']); } - return new static($options['route'], $options['may_terminate'], $options['route_plugins'], $options['child_routes']); + return new static( + $options['route'], + $options['may_terminate'], + $options['route_plugins'], + $options['child_routes'], + $options['prototypes'] + ); } /** diff --git a/library/Zend/Mvc/Router/Http/TreeRouteStack.php b/library/Zend/Mvc/Router/Http/TreeRouteStack.php index eae054705bd..ab11d240997 100644 --- a/library/Zend/Mvc/Router/Http/TreeRouteStack.php +++ b/library/Zend/Mvc/Router/Http/TreeRouteStack.php @@ -9,6 +9,7 @@ namespace Zend\Mvc\Router\Http; +use ArrayObject; use Traversable; use Zend\Mvc\Router\Exception; use Zend\Mvc\Router\SimpleRouteStack; @@ -35,6 +36,41 @@ class TreeRouteStack extends SimpleRouteStack */ protected $requestUri; + /** + * Prototype routes. + * + * We use an ArrayObject in this case so we can easily pass it down the tree + * by reference. + * + * @var ArrayObject + */ + protected $prototypes; + + /** + * factory(): defined by RouteInterface interface. + * + * @see \Zend\Mvc\Router\RouteInterface::factory() + * @param array|Traversable $options + * @return SimpleRouteStack + * @throws Exception\InvalidArgumentException + */ + public static function factory($options = array()) + { + if ($options instanceof Traversable) { + $options = ArrayUtils::iteratorToArray($options); + } elseif (!is_array($options)) { + throw new Exception\InvalidArgumentException(__METHOD__ . ' expects an array or Traversable set of options'); + } + + $instance = parent::factory($options); + + if (isset($options['prototypes'])) { + $instance->addPrototypes($options['prototypes']); + } + + return $instance; + } + /** * init(): defined by SimpleRouteStack. * @@ -42,6 +78,8 @@ class TreeRouteStack extends SimpleRouteStack */ protected function init() { + $this->prototypes = new ArrayObject; + $routes = $this->routePluginManager; foreach (array( 'hostname' => __NAMESPACE__ . '\Hostname', @@ -82,7 +120,7 @@ public function addRoute($name, $route, $priority = null) * routeFromArray(): defined by SimpleRouteStack. * * @see SimpleRouteStack::routeFromArray() - * @param array|Traversable $specs + * @param string|array|Traversable $specs * @return RouteInterface * @throws Exception\InvalidArgumentException When route definition is not an array nor traversable * @throws Exception\InvalidArgumentException When chain routes are not an array nor traversable @@ -90,7 +128,13 @@ public function addRoute($name, $route, $priority = null) */ protected function routeFromArray($specs) { - if ($specs instanceof Traversable) { + if (is_string($specs)) { + if (null === ($route = $this->getPrototype($specs))) { + throw new Exception\RuntimeException(sprintf('Could not find prototype with name %s', $specs)); + } + + return $route; + } elseif ($specs instanceof Traversable) { $specs = ArrayUtils::iteratorToArray($specs); } elseif (!is_array($specs)) { throw new Exception\InvalidArgumentException('Route definition must be an array or Traversable object'); @@ -106,7 +150,8 @@ protected function routeFromArray($specs) $options = array( 'routes' => $chainRoutes, - 'route_plugins' => $this->routePluginManager + 'route_plugins' => $this->routePluginManager, + 'prototypes' => $this->prototypes, ); $route = $this->routePluginManager->get('chain', $options); @@ -124,6 +169,7 @@ protected function routeFromArray($specs) 'may_terminate' => (isset($specs['may_terminate']) && $specs['may_terminate']), 'child_routes' => $specs['child_routes'], 'route_plugins' => $this->routePluginManager, + 'prototypes' => $this->prototypes, ); $priority = (isset($route->priority) ? $route->priority : null); @@ -135,6 +181,59 @@ protected function routeFromArray($specs) return $route; } + /** + * Add multiple prototypes at once. + * + * @param Traversable $routes + * @return TreeRouteStack + * @throws Exception\InvalidArgumentException + */ + public function addPrototypes($routes) + { + if (!is_array($routes) && !$routes instanceof Traversable) { + throw new Exception\InvalidArgumentException('addPrototypes expects an array or Traversable set of routes'); + } + + foreach ($routes as $name => $route) { + $this->addPrototype($name, $route); + } + + return $this; + } + + /** + * Add a prototype. + * + * @param string $name + * @param mixed $route + * @return TreeRouteStack + */ + public function addPrototype($name, $route) + { + if (!$route instanceof RouteInterface) { + $route = $this->routeFromArray($route); + } + + $this->prototypes[$name] = $route; + + return $this; + } + + /** + * Get a prototype. + * + * @param string $name + * @return RouterInterface|null + */ + public function getPrototype($name) + { + if (isset($this->prototypes[$name])) { + return $this->prototypes[$name]; + } + + return null; + } + /** * match(): defined by \Zend\Mvc\Router\RouteInterface *