merged branch Tobion/loaders (PR #6172)
This PR was merged into the master branch. Commits -------237bbd0
fixed and refactored YamlFileLoader in the same sense as the XmlFileLoader392d785
removed covers annotation from loader tests and unneeded use statements45987eb
added tests for the previous XmlFileLoader fixesb20d6a7
ensure id, pattern and resource are specified8361b5a
refactor the XMlFileLoader83fc5ff
fix namespace handling in xml loader; it could not handle prefixes1a60a3c
make resource and key attributes required in xsd02e01b9
improve exception messages in xml loader51fbffe
remove unneeded cast358e9c4
fix some phpdoc Discussion ---------- [Routing] improve loaders BC break: no Main points: - fixed Xml loader that could not handle namespace prefixes but is valid xml - fixed Yaml loader where some nonsesense configs were not validated correctly like an imported resource with a pattern key. Added tests for all. Some refactoring + a few tweaks like better exception messages and more consistency between Xml loader and yaml loader. See also commits. --------------------------------------------------------------------------- by Tobion at 2012-12-07T18:16:08Z @fabpot this is ready --------------------------------------------------------------------------- by Tobion at 2012-12-11T17:30:10Z @fabpot rebased. Please don't squash to one big commit where one does not see what changed why. --------------------------------------------------------------------------- by fabpot at 2012-12-11T17:32:40Z I only squash when most commits are CS fixes and feedback. --------------------------------------------------------------------------- by Tobion at 2012-12-11T17:37:49Z Well, you squashed #6022 so it's not possible to revert a specific deprecation.
This commit is contained in:
commit
18c520a5e8
@ -12,6 +12,7 @@
|
|||||||
namespace Symfony\Component\Routing\Loader;
|
namespace Symfony\Component\Routing\Loader;
|
||||||
|
|
||||||
use Symfony\Component\Config\Loader\Loader;
|
use Symfony\Component\Config\Loader\Loader;
|
||||||
|
use Symfony\Component\Routing\RouteCollection;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ClosureLoader loads routes from a PHP closure.
|
* ClosureLoader loads routes from a PHP closure.
|
||||||
@ -30,6 +31,8 @@ class ClosureLoader extends Loader
|
|||||||
* @param \Closure $closure A Closure
|
* @param \Closure $closure A Closure
|
||||||
* @param string|null $type The resource type
|
* @param string|null $type The resource type
|
||||||
*
|
*
|
||||||
|
* @return RouteCollection A RouteCollection instance
|
||||||
|
*
|
||||||
* @api
|
* @api
|
||||||
*/
|
*/
|
||||||
public function load($closure, $type = null)
|
public function load($closure, $type = null)
|
||||||
|
@ -11,8 +11,9 @@
|
|||||||
|
|
||||||
namespace Symfony\Component\Routing\Loader;
|
namespace Symfony\Component\Routing\Loader;
|
||||||
|
|
||||||
use Symfony\Component\Config\Resource\FileResource;
|
|
||||||
use Symfony\Component\Config\Loader\FileLoader;
|
use Symfony\Component\Config\Loader\FileLoader;
|
||||||
|
use Symfony\Component\Config\Resource\FileResource;
|
||||||
|
use Symfony\Component\Routing\RouteCollection;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PhpFileLoader loads routes from a PHP file.
|
* PhpFileLoader loads routes from a PHP file.
|
||||||
@ -31,6 +32,8 @@ class PhpFileLoader extends FileLoader
|
|||||||
* @param string $file A PHP file path
|
* @param string $file A PHP file path
|
||||||
* @param string|null $type The resource type
|
* @param string|null $type The resource type
|
||||||
*
|
*
|
||||||
|
* @return RouteCollection A RouteCollection instance
|
||||||
|
*
|
||||||
* @api
|
* @api
|
||||||
*/
|
*/
|
||||||
public function load($file, $type = null)
|
public function load($file, $type = null)
|
||||||
|
@ -20,11 +20,15 @@ use Symfony\Component\Config\Loader\FileLoader;
|
|||||||
* XmlFileLoader loads XML routing files.
|
* XmlFileLoader loads XML routing files.
|
||||||
*
|
*
|
||||||
* @author Fabien Potencier <fabien@symfony.com>
|
* @author Fabien Potencier <fabien@symfony.com>
|
||||||
|
* @author Tobias Schultze <http://tobion.de>
|
||||||
*
|
*
|
||||||
* @api
|
* @api
|
||||||
*/
|
*/
|
||||||
class XmlFileLoader extends FileLoader
|
class XmlFileLoader extends FileLoader
|
||||||
{
|
{
|
||||||
|
const NAMESPACE_URI = 'http://symfony.com/schema/routing';
|
||||||
|
const SCHEME_PATH = '/schema/routing/routing-1.0.xsd';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads an XML file.
|
* Loads an XML file.
|
||||||
*
|
*
|
||||||
@ -33,7 +37,8 @@ class XmlFileLoader extends FileLoader
|
|||||||
*
|
*
|
||||||
* @return RouteCollection A RouteCollection instance
|
* @return RouteCollection A RouteCollection instance
|
||||||
*
|
*
|
||||||
* @throws \InvalidArgumentException When a tag can't be parsed
|
* @throws \InvalidArgumentException When the file cannot be loaded or when the XML cannot be
|
||||||
|
* parsed because it does not validate against the scheme.
|
||||||
*
|
*
|
||||||
* @api
|
* @api
|
||||||
*/
|
*/
|
||||||
@ -61,65 +66,28 @@ class XmlFileLoader extends FileLoader
|
|||||||
/**
|
/**
|
||||||
* Parses a node from a loaded XML file.
|
* Parses a node from a loaded XML file.
|
||||||
*
|
*
|
||||||
* @param RouteCollection $collection the collection to associate with the node
|
* @param RouteCollection $collection Collection to associate with the node
|
||||||
* @param \DOMElement $node the node to parse
|
* @param \DOMElement $node Element to parse
|
||||||
* @param string $path the path of the XML file being processed
|
* @param string $path Full path of the XML file being processed
|
||||||
* @param string $file
|
* @param string $file Loaded file name
|
||||||
*
|
*
|
||||||
* @throws \InvalidArgumentException When a tag can't be parsed
|
* @throws \InvalidArgumentException When the XML is invalid
|
||||||
*/
|
*/
|
||||||
protected function parseNode(RouteCollection $collection, \DOMElement $node, $path, $file)
|
protected function parseNode(RouteCollection $collection, \DOMElement $node, $path, $file)
|
||||||
{
|
{
|
||||||
switch ($node->tagName) {
|
if (self::NAMESPACE_URI !== $node->namespaceURI) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ($node->localName) {
|
||||||
case 'route':
|
case 'route':
|
||||||
$this->parseRoute($collection, $node, $path);
|
$this->parseRoute($collection, $node, $path);
|
||||||
break;
|
break;
|
||||||
case 'import':
|
case 'import':
|
||||||
$resource = $node->getAttribute('resource');
|
$this->parseImport($collection, $node, $path, $file);
|
||||||
$type = $node->getAttribute('type');
|
|
||||||
$prefix = $node->getAttribute('prefix');
|
|
||||||
$hostnamePattern = $node->hasAttribute('hostname-pattern') ? $node->getAttribute('hostname-pattern') : null;
|
|
||||||
|
|
||||||
$defaults = array();
|
|
||||||
$requirements = array();
|
|
||||||
$options = array();
|
|
||||||
|
|
||||||
foreach ($node->childNodes as $n) {
|
|
||||||
if (!$n instanceof \DOMElement) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch ($n->tagName) {
|
|
||||||
case 'default':
|
|
||||||
$defaults[$n->getAttribute('key')] = trim($n->nodeValue);
|
|
||||||
break;
|
|
||||||
case 'requirement':
|
|
||||||
$requirements[$n->getAttribute('key')] = trim($n->nodeValue);
|
|
||||||
break;
|
|
||||||
case 'option':
|
|
||||||
$options[$n->getAttribute('key')] = trim($n->nodeValue);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new \InvalidArgumentException(sprintf('Unable to parse tag "%s"', $n->tagName));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->setCurrentDir(dirname($path));
|
|
||||||
|
|
||||||
$subCollection = $this->import($resource, ('' !== $type ? $type : null), false, $file);
|
|
||||||
/* @var $subCollection RouteCollection */
|
|
||||||
$subCollection->addPrefix($prefix);
|
|
||||||
if (null !== $hostnamePattern) {
|
|
||||||
$subCollection->setHostnamePattern($hostnamePattern);
|
|
||||||
}
|
|
||||||
$subCollection->addDefaults($defaults);
|
|
||||||
$subCollection->addRequirements($requirements);
|
|
||||||
$subCollection->addOptions($options);
|
|
||||||
|
|
||||||
$collection->addCollection($subCollection);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new \InvalidArgumentException(sprintf('Unable to parse tag "%s"', $node->tagName));
|
throw new \InvalidArgumentException(sprintf('Unknown tag "%s" used in file "%s". Expected "route" or "import".', $node->localName, $path));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,41 +104,59 @@ class XmlFileLoader extends FileLoader
|
|||||||
/**
|
/**
|
||||||
* Parses a route and adds it to the RouteCollection.
|
* Parses a route and adds it to the RouteCollection.
|
||||||
*
|
*
|
||||||
* @param RouteCollection $collection A RouteCollection instance
|
* @param RouteCollection $collection RouteCollection instance
|
||||||
* @param \DOMElement $definition Route definition
|
* @param \DOMElement $node Element to parse that represents a Route
|
||||||
* @param string $file An XML file path
|
* @param string $path Full path of the XML file being processed
|
||||||
*
|
*
|
||||||
* @throws \InvalidArgumentException When the definition cannot be parsed
|
* @throws \InvalidArgumentException When the XML is invalid
|
||||||
*/
|
*/
|
||||||
protected function parseRoute(RouteCollection $collection, \DOMElement $definition, $file)
|
protected function parseRoute(RouteCollection $collection, \DOMElement $node, $path)
|
||||||
{
|
{
|
||||||
$defaults = array();
|
if ('' === ($id = $node->getAttribute('id')) || !$node->hasAttribute('pattern')) {
|
||||||
$requirements = array();
|
throw new \InvalidArgumentException(sprintf('The <route> element in file "%s" must have an "id" and a "pattern" attribute.', $path));
|
||||||
$options = array();
|
|
||||||
|
|
||||||
foreach ($definition->childNodes as $node) {
|
|
||||||
if (!$node instanceof \DOMElement) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch ($node->tagName) {
|
|
||||||
case 'default':
|
|
||||||
$defaults[$node->getAttribute('key')] = trim((string) $node->nodeValue);
|
|
||||||
break;
|
|
||||||
case 'option':
|
|
||||||
$options[$node->getAttribute('key')] = trim((string) $node->nodeValue);
|
|
||||||
break;
|
|
||||||
case 'requirement':
|
|
||||||
$requirements[$node->getAttribute('key')] = trim((string) $node->nodeValue);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new \InvalidArgumentException(sprintf('Unable to parse tag "%s"', $node->tagName));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$route = new Route($definition->getAttribute('pattern'), $defaults, $requirements, $options, $definition->getAttribute('hostname-pattern'));
|
list($defaults, $requirements, $options) = $this->parseConfigs($node, $path);
|
||||||
|
|
||||||
$collection->add($definition->getAttribute('id'), $route);
|
$route = new Route($node->getAttribute('pattern'), $defaults, $requirements, $options, $node->getAttribute('hostname-pattern'));
|
||||||
|
$collection->add($id, $route);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses an import and adds the routes in the resource to the RouteCollection.
|
||||||
|
*
|
||||||
|
* @param RouteCollection $collection RouteCollection instance
|
||||||
|
* @param \DOMElement $node Element to parse that represents a Route
|
||||||
|
* @param string $path Full path of the XML file being processed
|
||||||
|
* @param string $file Loaded file name
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException When the XML is invalid
|
||||||
|
*/
|
||||||
|
protected function parseImport(RouteCollection $collection, \DOMElement $node, $path, $file)
|
||||||
|
{
|
||||||
|
if ('' === $resource = $node->getAttribute('resource')) {
|
||||||
|
throw new \InvalidArgumentException(sprintf('The <import> element in file "%s" must have a "resource" attribute.', $path));
|
||||||
|
}
|
||||||
|
|
||||||
|
$type = $node->getAttribute('type');
|
||||||
|
$prefix = $node->getAttribute('prefix');
|
||||||
|
$hostnamePattern = $node->hasAttribute('hostname-pattern') ? $node->getAttribute('hostname-pattern') : null;
|
||||||
|
|
||||||
|
list($defaults, $requirements, $options) = $this->parseConfigs($node, $path);
|
||||||
|
|
||||||
|
$this->setCurrentDir(dirname($path));
|
||||||
|
|
||||||
|
$subCollection = $this->import($resource, ('' !== $type ? $type : null), false, $file);
|
||||||
|
/* @var $subCollection RouteCollection */
|
||||||
|
$subCollection->addPrefix($prefix);
|
||||||
|
if (null !== $hostnamePattern) {
|
||||||
|
$subCollection->setHostnamePattern($hostnamePattern);
|
||||||
|
}
|
||||||
|
$subCollection->addDefaults($defaults);
|
||||||
|
$subCollection->addRequirements($requirements);
|
||||||
|
$subCollection->addOptions($options);
|
||||||
|
|
||||||
|
$collection->addCollection($subCollection);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -180,7 +166,9 @@ class XmlFileLoader extends FileLoader
|
|||||||
*
|
*
|
||||||
* @return \DOMDocument
|
* @return \DOMDocument
|
||||||
*
|
*
|
||||||
* @throws \InvalidArgumentException When loading of XML file returns error
|
* @throws \InvalidArgumentException When loading of XML file fails because of syntax errors
|
||||||
|
* or when the XML structure is not as expected by the scheme -
|
||||||
|
* see validate()
|
||||||
*/
|
*/
|
||||||
protected function loadFile($file)
|
protected function loadFile($file)
|
||||||
{
|
{
|
||||||
@ -220,12 +208,10 @@ class XmlFileLoader extends FileLoader
|
|||||||
*/
|
*/
|
||||||
protected function validate(\DOMDocument $dom)
|
protected function validate(\DOMDocument $dom)
|
||||||
{
|
{
|
||||||
$location = __DIR__.'/schema/routing/routing-1.0.xsd';
|
|
||||||
|
|
||||||
$current = libxml_use_internal_errors(true);
|
$current = libxml_use_internal_errors(true);
|
||||||
libxml_clear_errors();
|
libxml_clear_errors();
|
||||||
|
|
||||||
if (!$dom->schemaValidate($location)) {
|
if (!$dom->schemaValidate(__DIR__ . static::SCHEME_PATH)) {
|
||||||
throw new \InvalidArgumentException(implode("\n", $this->getXmlErrors($current)));
|
throw new \InvalidArgumentException(implode("\n", $this->getXmlErrors($current)));
|
||||||
}
|
}
|
||||||
libxml_use_internal_errors($current);
|
libxml_use_internal_errors($current);
|
||||||
@ -255,4 +241,39 @@ class XmlFileLoader extends FileLoader
|
|||||||
|
|
||||||
return $errors;
|
return $errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the config elements (default, requirement, option).
|
||||||
|
*
|
||||||
|
* @param \DOMElement $node Element to parse that contains the configs
|
||||||
|
* @param string $path Full path of the XML file being processed
|
||||||
|
*
|
||||||
|
* @return array An array with the defaults as first item, requirements as second and options as third.
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException When the XML is invalid
|
||||||
|
*/
|
||||||
|
private function parseConfigs(\DOMElement $node, $path)
|
||||||
|
{
|
||||||
|
$defaults = array();
|
||||||
|
$requirements = array();
|
||||||
|
$options = array();
|
||||||
|
|
||||||
|
foreach ($node->getElementsByTagNameNS(self::NAMESPACE_URI, '*') as $n) {
|
||||||
|
switch ($n->localName) {
|
||||||
|
case 'default':
|
||||||
|
$defaults[$n->getAttribute('key')] = trim($n->textContent);
|
||||||
|
break;
|
||||||
|
case 'requirement':
|
||||||
|
$requirements[$n->getAttribute('key')] = trim($n->textContent);
|
||||||
|
break;
|
||||||
|
case 'option':
|
||||||
|
$options[$n->getAttribute('key')] = trim($n->textContent);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new \InvalidArgumentException(sprintf('Unknown tag "%s" used in file "%s". Expected "default", "requirement" or "option".', $n->localName, $path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return array($defaults, $requirements, $options);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,13 +21,14 @@ use Symfony\Component\Config\Loader\FileLoader;
|
|||||||
* YamlFileLoader loads Yaml routing files.
|
* YamlFileLoader loads Yaml routing files.
|
||||||
*
|
*
|
||||||
* @author Fabien Potencier <fabien@symfony.com>
|
* @author Fabien Potencier <fabien@symfony.com>
|
||||||
|
* @author Tobias Schultze <http://tobion.de>
|
||||||
*
|
*
|
||||||
* @api
|
* @api
|
||||||
*/
|
*/
|
||||||
class YamlFileLoader extends FileLoader
|
class YamlFileLoader extends FileLoader
|
||||||
{
|
{
|
||||||
private static $availableKeys = array(
|
private static $availableKeys = array(
|
||||||
'type', 'resource', 'prefix', 'pattern', 'options', 'defaults', 'requirements', 'hostname_pattern',
|
'resource', 'type', 'prefix', 'pattern', 'hostname_pattern', 'defaults', 'requirements', 'options',
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -38,7 +39,7 @@ class YamlFileLoader extends FileLoader
|
|||||||
*
|
*
|
||||||
* @return RouteCollection A RouteCollection instance
|
* @return RouteCollection A RouteCollection instance
|
||||||
*
|
*
|
||||||
* @throws \InvalidArgumentException When route can't be parsed
|
* @throws \InvalidArgumentException When a route can't be parsed because YAML is invalid
|
||||||
*
|
*
|
||||||
* @api
|
* @api
|
||||||
*/
|
*/
|
||||||
@ -53,38 +54,19 @@ class YamlFileLoader extends FileLoader
|
|||||||
|
|
||||||
// empty file
|
// empty file
|
||||||
if (null === $config) {
|
if (null === $config) {
|
||||||
$config = array();
|
return $collection;
|
||||||
}
|
}
|
||||||
|
|
||||||
// not an array
|
// not an array
|
||||||
if (!is_array($config)) {
|
if (!is_array($config)) {
|
||||||
throw new \InvalidArgumentException(sprintf('The file "%s" must contain a YAML array.', $file));
|
throw new \InvalidArgumentException(sprintf('The file "%s" must contain a YAML array.', $path));
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($config as $name => $config) {
|
foreach ($config as $name => $config) {
|
||||||
$config = $this->normalizeRouteConfig($config);
|
$this->validate($config, $name, $path);
|
||||||
|
|
||||||
if (isset($config['resource'])) {
|
if (isset($config['resource'])) {
|
||||||
$type = isset($config['type']) ? $config['type'] : null;
|
$this->parseImport($collection, $config, $path, $file);
|
||||||
$prefix = isset($config['prefix']) ? $config['prefix'] : '';
|
|
||||||
$defaults = isset($config['defaults']) ? $config['defaults'] : array();
|
|
||||||
$requirements = isset($config['requirements']) ? $config['requirements'] : array();
|
|
||||||
$options = isset($config['options']) ? $config['options'] : array();
|
|
||||||
$hostnamePattern = isset($config['hostname_pattern']) ? $config['hostname_pattern'] : null;
|
|
||||||
|
|
||||||
$this->setCurrentDir(dirname($path));
|
|
||||||
|
|
||||||
$subCollection = $this->import($config['resource'], $type, false, $file);
|
|
||||||
/* @var $subCollection RouteCollection */
|
|
||||||
$subCollection->addPrefix($prefix);
|
|
||||||
if (null !== $hostnamePattern) {
|
|
||||||
$subCollection->setHostnamePattern($hostnamePattern);
|
|
||||||
}
|
|
||||||
$subCollection->addDefaults($defaults);
|
|
||||||
$subCollection->addRequirements($requirements);
|
|
||||||
$subCollection->addOptions($options);
|
|
||||||
|
|
||||||
$collection->addCollection($subCollection);
|
|
||||||
} else {
|
} else {
|
||||||
$this->parseRoute($collection, $name, $config, $path);
|
$this->parseRoute($collection, $name, $config, $path);
|
||||||
}
|
}
|
||||||
@ -109,46 +91,90 @@ class YamlFileLoader extends FileLoader
|
|||||||
* @param RouteCollection $collection A RouteCollection instance
|
* @param RouteCollection $collection A RouteCollection instance
|
||||||
* @param string $name Route name
|
* @param string $name Route name
|
||||||
* @param array $config Route definition
|
* @param array $config Route definition
|
||||||
* @param string $file A Yaml file path
|
* @param string $path Full path of the YAML file being processed
|
||||||
*
|
|
||||||
* @throws \InvalidArgumentException When config pattern is not defined for the given route
|
|
||||||
*/
|
*/
|
||||||
protected function parseRoute(RouteCollection $collection, $name, $config, $file)
|
protected function parseRoute(RouteCollection $collection, $name, array $config, $path)
|
||||||
{
|
{
|
||||||
$defaults = isset($config['defaults']) ? $config['defaults'] : array();
|
$defaults = isset($config['defaults']) ? $config['defaults'] : array();
|
||||||
$requirements = isset($config['requirements']) ? $config['requirements'] : array();
|
$requirements = isset($config['requirements']) ? $config['requirements'] : array();
|
||||||
$options = isset($config['options']) ? $config['options'] : array();
|
$options = isset($config['options']) ? $config['options'] : array();
|
||||||
$hostnamePattern = isset($config['hostname_pattern']) ? $config['hostname_pattern'] : null;
|
$hostnamePattern = isset($config['hostname_pattern']) ? $config['hostname_pattern'] : null;
|
||||||
|
|
||||||
if (!isset($config['pattern'])) {
|
|
||||||
throw new \InvalidArgumentException(sprintf('You must define a "pattern" for the "%s" route.', $name));
|
|
||||||
}
|
|
||||||
|
|
||||||
$route = new Route($config['pattern'], $defaults, $requirements, $options, $hostnamePattern);
|
$route = new Route($config['pattern'], $defaults, $requirements, $options, $hostnamePattern);
|
||||||
|
|
||||||
$collection->add($name, $route);
|
$collection->add($name, $route);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Normalize route configuration.
|
* Parses an import and adds the routes in the resource to the RouteCollection.
|
||||||
*
|
*
|
||||||
* @param array $config A resource config
|
* @param RouteCollection $collection A RouteCollection instance
|
||||||
*
|
* @param array $config Route definition
|
||||||
* @return array
|
* @param string $path Full path of the YAML file being processed
|
||||||
*
|
* @param string $file Loaded file name
|
||||||
* @throws \InvalidArgumentException if one of the provided config keys is not supported
|
|
||||||
*/
|
*/
|
||||||
private function normalizeRouteConfig(array $config)
|
protected function parseImport(RouteCollection $collection, array $config, $path, $file)
|
||||||
{
|
{
|
||||||
foreach ($config as $key => $value) {
|
$type = isset($config['type']) ? $config['type'] : null;
|
||||||
if (!in_array($key, self::$availableKeys)) {
|
$prefix = isset($config['prefix']) ? $config['prefix'] : '';
|
||||||
throw new \InvalidArgumentException(sprintf(
|
$defaults = isset($config['defaults']) ? $config['defaults'] : array();
|
||||||
'Yaml routing loader does not support given key: "%s". Expected one of the (%s).',
|
$requirements = isset($config['requirements']) ? $config['requirements'] : array();
|
||||||
$key, implode(', ', self::$availableKeys)
|
$options = isset($config['options']) ? $config['options'] : array();
|
||||||
));
|
$hostnamePattern = isset($config['hostname_pattern']) ? $config['hostname_pattern'] : null;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $config;
|
$this->setCurrentDir(dirname($path));
|
||||||
|
|
||||||
|
$subCollection = $this->import($config['resource'], $type, false, $file);
|
||||||
|
/* @var $subCollection RouteCollection */
|
||||||
|
$subCollection->addPrefix($prefix);
|
||||||
|
if (null !== $hostnamePattern) {
|
||||||
|
$subCollection->setHostnamePattern($hostnamePattern);
|
||||||
|
}
|
||||||
|
$subCollection->addDefaults($defaults);
|
||||||
|
$subCollection->addRequirements($requirements);
|
||||||
|
$subCollection->addOptions($options);
|
||||||
|
|
||||||
|
$collection->addCollection($subCollection);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates the route configuration.
|
||||||
|
*
|
||||||
|
* @param array $config A resource config
|
||||||
|
* @param string $name The config key
|
||||||
|
* @param string $path The loaded file path
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException If one of the provided config keys is not supported,
|
||||||
|
* something is missing or the combination is nonesense
|
||||||
|
*/
|
||||||
|
protected function validate($config, $name, $path)
|
||||||
|
{
|
||||||
|
if (!is_array($config)) {
|
||||||
|
throw new \InvalidArgumentException(sprintf('The definition of "%s" in "%s" must be a YAML array.', $name, $path));
|
||||||
|
}
|
||||||
|
if ($extraKeys = array_diff(array_keys($config), self::$availableKeys)) {
|
||||||
|
throw new \InvalidArgumentException(sprintf(
|
||||||
|
'The routing file "%s" contains unsupport keys for "%s": "%s". Expected one of: "%s".',
|
||||||
|
$path, $name, implode('", "', $extraKeys), implode('", "', self::$availableKeys)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if (isset($config['resource']) && isset($config['pattern'])) {
|
||||||
|
throw new \InvalidArgumentException(sprintf(
|
||||||
|
'The routing file "%s" must not specify both the "resource" key and the "pattern" key for "%s". Choose between an import and a route definition.',
|
||||||
|
$path, $name
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if (!isset($config['resource']) && isset($config['type'])) {
|
||||||
|
throw new \InvalidArgumentException(sprintf(
|
||||||
|
'The "type" key for the route definition "%s" in "%s" is unsupported. It is only available for imports in combination with the "resource" key.',
|
||||||
|
$name, $path
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if (!isset($config['resource']) && !isset($config['pattern'])) {
|
||||||
|
throw new \InvalidArgumentException(sprintf(
|
||||||
|
'You must define a "pattern" for the route "%s" in file "%s".',
|
||||||
|
$name, $path
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,14 +36,14 @@
|
|||||||
<xsd:group ref="configs" minOccurs="0" maxOccurs="unbounded" />
|
<xsd:group ref="configs" minOccurs="0" maxOccurs="unbounded" />
|
||||||
|
|
||||||
<xsd:attribute name="id" type="xsd:string" use="required" />
|
<xsd:attribute name="id" type="xsd:string" use="required" />
|
||||||
<xsd:attribute name="pattern" type="xsd:string" default="/" />
|
<xsd:attribute name="pattern" type="xsd:string" use="required" />
|
||||||
<xsd:attribute name="hostname-pattern" type="xsd:string" />
|
<xsd:attribute name="hostname-pattern" type="xsd:string" />
|
||||||
</xsd:complexType>
|
</xsd:complexType>
|
||||||
|
|
||||||
<xsd:complexType name="import">
|
<xsd:complexType name="import">
|
||||||
<xsd:group ref="configs" minOccurs="0" maxOccurs="unbounded" />
|
<xsd:group ref="configs" minOccurs="0" maxOccurs="unbounded" />
|
||||||
|
|
||||||
<xsd:attribute name="resource" type="xsd:string" />
|
<xsd:attribute name="resource" type="xsd:string" use="required" />
|
||||||
<xsd:attribute name="type" type="xsd:string" />
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
<xsd:attribute name="prefix" type="xsd:string" />
|
<xsd:attribute name="prefix" type="xsd:string" />
|
||||||
<xsd:attribute name="hostname-pattern" type="xsd:string" />
|
<xsd:attribute name="hostname-pattern" type="xsd:string" />
|
||||||
@ -52,7 +52,7 @@
|
|||||||
<xsd:complexType name="element">
|
<xsd:complexType name="element">
|
||||||
<xsd:simpleContent>
|
<xsd:simpleContent>
|
||||||
<xsd:extension base="xsd:string">
|
<xsd:extension base="xsd:string">
|
||||||
<xsd:attribute name="key" type="xsd:string" />
|
<xsd:attribute name="key" type="xsd:string" use="required" />
|
||||||
</xsd:extension>
|
</xsd:extension>
|
||||||
</xsd:simpleContent>
|
</xsd:simpleContent>
|
||||||
</xsd:complexType>
|
</xsd:complexType>
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
|
||||||
|
<routes xmlns="http://symfony.com/schema/routing"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">
|
||||||
|
|
||||||
|
<route pattern="/test"></route>
|
||||||
|
</routes>
|
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
|
||||||
|
<routes xmlns="http://symfony.com/schema/routing"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">
|
||||||
|
|
||||||
|
<route id="myroute"></route>
|
||||||
|
</routes>
|
@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
|
||||||
|
<r:routes xmlns:r="http://symfony.com/schema/routing"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">
|
||||||
|
|
||||||
|
<r:route id="blog_show" pattern="/blog/{slug}" hostname-pattern="{_locale}.example.com">
|
||||||
|
<r:default key="_controller">MyBundle:Blog:show</r:default>
|
||||||
|
<requirement xmlns="http://symfony.com/schema/routing" key="slug">\w+</requirement>
|
||||||
|
<r2:requirement xmlns:r2="http://symfony.com/schema/routing" key="_locale">en|fr|de</r2:requirement>
|
||||||
|
<r:option key="compiler_class">RouteCompiler</r:option>
|
||||||
|
</r:route>
|
||||||
|
</r:routes>
|
@ -0,0 +1,3 @@
|
|||||||
|
blog_show:
|
||||||
|
resource: validpattern.yml
|
||||||
|
pattern: /test
|
@ -0,0 +1,3 @@
|
|||||||
|
blog_show:
|
||||||
|
pattern: /blog/{slug}
|
||||||
|
type: custom
|
@ -0,0 +1 @@
|
|||||||
|
route: string
|
@ -11,8 +11,6 @@
|
|||||||
|
|
||||||
namespace Symfony\Component\Routing\Tests\Loader;
|
namespace Symfony\Component\Routing\Tests\Loader;
|
||||||
|
|
||||||
use Symfony\Component\Routing\Loader\AnnotationClassLoader;
|
|
||||||
|
|
||||||
class AnnotationClassLoaderTest extends AbstractAnnotationLoaderTest
|
class AnnotationClassLoaderTest extends AbstractAnnotationLoaderTest
|
||||||
{
|
{
|
||||||
protected $loader;
|
protected $loader;
|
||||||
@ -42,7 +40,6 @@ class AnnotationClassLoaderTest extends AbstractAnnotationLoaderTest
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @covers Symfony\Component\Routing\Loader\AnnotationClassLoader::supports
|
|
||||||
* @dataProvider provideTestSupportsChecksResource
|
* @dataProvider provideTestSupportsChecksResource
|
||||||
*/
|
*/
|
||||||
public function testSupportsChecksResource($resource, $expectedSupports)
|
public function testSupportsChecksResource($resource, $expectedSupports)
|
||||||
@ -63,9 +60,6 @@ class AnnotationClassLoaderTest extends AbstractAnnotationLoaderTest
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @covers Symfony\Component\Routing\Loader\AnnotationClassLoader::supports
|
|
||||||
*/
|
|
||||||
public function testSupportsChecksTypeIfSpecified()
|
public function testSupportsChecksTypeIfSpecified()
|
||||||
{
|
{
|
||||||
$this->assertTrue($this->loader->supports('class', 'annotation'), '->supports() checks the resource type if specified');
|
$this->assertTrue($this->loader->supports('class', 'annotation'), '->supports() checks the resource type if specified');
|
||||||
|
@ -40,9 +40,6 @@ class AnnotationDirectoryLoaderTest extends AbstractAnnotationLoaderTest
|
|||||||
$this->loader->load(__DIR__.'/../Fixtures/AnnotatedClasses');
|
$this->loader->load(__DIR__.'/../Fixtures/AnnotatedClasses');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @covers Symfony\Component\Routing\Loader\AnnotationDirectoryLoader::supports
|
|
||||||
*/
|
|
||||||
public function testSupports()
|
public function testSupports()
|
||||||
{
|
{
|
||||||
$fixturesDir = __DIR__.'/../Fixtures';
|
$fixturesDir = __DIR__.'/../Fixtures';
|
||||||
|
@ -34,9 +34,6 @@ class AnnotationFileLoaderTest extends AbstractAnnotationLoaderTest
|
|||||||
$this->loader->load(__DIR__.'/../Fixtures/AnnotatedClasses/FooClass.php');
|
$this->loader->load(__DIR__.'/../Fixtures/AnnotatedClasses/FooClass.php');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @covers Symfony\Component\Routing\Loader\AnnotationFileLoader::supports
|
|
||||||
*/
|
|
||||||
public function testSupports()
|
public function testSupports()
|
||||||
{
|
{
|
||||||
$fixture = __DIR__.'/../Fixtures/annotated.php';
|
$fixture = __DIR__.'/../Fixtures/annotated.php';
|
||||||
|
@ -24,9 +24,6 @@ class ClosureLoaderTest extends \PHPUnit_Framework_TestCase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @covers Symfony\Component\Routing\Loader\ClosureLoader::supports
|
|
||||||
*/
|
|
||||||
public function testSupports()
|
public function testSupports()
|
||||||
{
|
{
|
||||||
$loader = new ClosureLoader();
|
$loader = new ClosureLoader();
|
||||||
@ -40,9 +37,6 @@ class ClosureLoaderTest extends \PHPUnit_Framework_TestCase
|
|||||||
$this->assertFalse($loader->supports($closure, 'foo'), '->supports() checks the resource type if specified');
|
$this->assertFalse($loader->supports($closure, 'foo'), '->supports() checks the resource type if specified');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @covers Symfony\Component\Routing\Loader\ClosureLoader::load
|
|
||||||
*/
|
|
||||||
public function testLoad()
|
public function testLoad()
|
||||||
{
|
{
|
||||||
$loader = new ClosureLoader();
|
$loader = new ClosureLoader();
|
||||||
|
@ -13,7 +13,6 @@ namespace Symfony\Component\Routing\Tests\Loader;
|
|||||||
|
|
||||||
use Symfony\Component\Config\FileLocator;
|
use Symfony\Component\Config\FileLocator;
|
||||||
use Symfony\Component\Routing\Loader\PhpFileLoader;
|
use Symfony\Component\Routing\Loader\PhpFileLoader;
|
||||||
use Symfony\Component\Routing\Route;
|
|
||||||
|
|
||||||
class PhpFileLoaderTest extends \PHPUnit_Framework_TestCase
|
class PhpFileLoaderTest extends \PHPUnit_Framework_TestCase
|
||||||
{
|
{
|
||||||
@ -24,9 +23,6 @@ class PhpFileLoaderTest extends \PHPUnit_Framework_TestCase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @covers Symfony\Component\Routing\Loader\PhpFileLoader::supports
|
|
||||||
*/
|
|
||||||
public function testSupports()
|
public function testSupports()
|
||||||
{
|
{
|
||||||
$loader = new PhpFileLoader($this->getMock('Symfony\Component\Config\FileLocator'));
|
$loader = new PhpFileLoader($this->getMock('Symfony\Component\Config\FileLocator'));
|
||||||
|
@ -13,7 +13,6 @@ namespace Symfony\Component\Routing\Tests\Loader;
|
|||||||
|
|
||||||
use Symfony\Component\Config\FileLocator;
|
use Symfony\Component\Config\FileLocator;
|
||||||
use Symfony\Component\Routing\Loader\XmlFileLoader;
|
use Symfony\Component\Routing\Loader\XmlFileLoader;
|
||||||
use Symfony\Component\Routing\Route;
|
|
||||||
use Symfony\Component\Routing\Tests\Fixtures\CustomXmlFileLoader;
|
use Symfony\Component\Routing\Tests\Fixtures\CustomXmlFileLoader;
|
||||||
|
|
||||||
class XmlFileLoaderTest extends \PHPUnit_Framework_TestCase
|
class XmlFileLoaderTest extends \PHPUnit_Framework_TestCase
|
||||||
@ -25,9 +24,6 @@ class XmlFileLoaderTest extends \PHPUnit_Framework_TestCase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @covers Symfony\Component\Routing\Loader\XmlFileLoader::supports
|
|
||||||
*/
|
|
||||||
public function testSupports()
|
public function testSupports()
|
||||||
{
|
{
|
||||||
$loader = new XmlFileLoader($this->getMock('Symfony\Component\Config\FileLocator'));
|
$loader = new XmlFileLoader($this->getMock('Symfony\Component\Config\FileLocator'));
|
||||||
@ -56,6 +52,21 @@ class XmlFileLoaderTest extends \PHPUnit_Framework_TestCase
|
|||||||
$this->assertEquals('RouteCompiler', $route->getOption('compiler_class'));
|
$this->assertEquals('RouteCompiler', $route->getOption('compiler_class'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testLoadWithNamespacePrefix()
|
||||||
|
{
|
||||||
|
$loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures')));
|
||||||
|
$routeCollection = $loader->load('namespaceprefix.xml');
|
||||||
|
|
||||||
|
$this->assertCount(1, $routeCollection, 'One route is loaded');
|
||||||
|
$route = $routeCollection->get('blog_show');
|
||||||
|
$this->assertEquals('/blog/{slug}', $route->getPattern());
|
||||||
|
$this->assertEquals('MyBundle:Blog:show', $route->getDefault('_controller'));
|
||||||
|
$this->assertEquals('\w+', $route->getRequirement('slug'));
|
||||||
|
$this->assertEquals('en|fr|de', $route->getRequirement('_locale'));
|
||||||
|
$this->assertEquals('{_locale}.example.com', $route->getHostnamePattern());
|
||||||
|
$this->assertEquals('RouteCompiler', $route->getOption('compiler_class'));
|
||||||
|
}
|
||||||
|
|
||||||
public function testLoadWithImport()
|
public function testLoadWithImport()
|
||||||
{
|
{
|
||||||
$loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures')));
|
$loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures')));
|
||||||
@ -94,7 +105,7 @@ class XmlFileLoaderTest extends \PHPUnit_Framework_TestCase
|
|||||||
|
|
||||||
public function getPathsToInvalidFiles()
|
public function getPathsToInvalidFiles()
|
||||||
{
|
{
|
||||||
return array(array('nonvalidnode.xml'), array('nonvalidroute.xml'), array('nonvalid.xml'));
|
return array(array('nonvalidnode.xml'), array('nonvalidroute.xml'), array('nonvalid.xml'), array('missing_id.xml'), array('missing_path.xml'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -13,7 +13,6 @@ namespace Symfony\Component\Routing\Tests\Loader;
|
|||||||
|
|
||||||
use Symfony\Component\Config\FileLocator;
|
use Symfony\Component\Config\FileLocator;
|
||||||
use Symfony\Component\Routing\Loader\YamlFileLoader;
|
use Symfony\Component\Routing\Loader\YamlFileLoader;
|
||||||
use Symfony\Component\Routing\Route;
|
|
||||||
use Symfony\Component\Config\Resource\FileResource;
|
use Symfony\Component\Config\Resource\FileResource;
|
||||||
|
|
||||||
class YamlFileLoaderTest extends \PHPUnit_Framework_TestCase
|
class YamlFileLoaderTest extends \PHPUnit_Framework_TestCase
|
||||||
@ -29,9 +28,6 @@ class YamlFileLoaderTest extends \PHPUnit_Framework_TestCase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @covers Symfony\Component\Routing\Loader\YamlFileLoader::supports
|
|
||||||
*/
|
|
||||||
public function testSupports()
|
public function testSupports()
|
||||||
{
|
{
|
||||||
$loader = new YamlFileLoader($this->getMock('Symfony\Component\Config\FileLocator'));
|
$loader = new YamlFileLoader($this->getMock('Symfony\Component\Config\FileLocator'));
|
||||||
@ -54,29 +50,17 @@ class YamlFileLoaderTest extends \PHPUnit_Framework_TestCase
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @expectedException \InvalidArgumentException
|
* @expectedException \InvalidArgumentException
|
||||||
|
* @dataProvider getPathsToInvalidFiles
|
||||||
*/
|
*/
|
||||||
public function testLoadThrowsExceptionIfNotAnArray()
|
public function testLoadThrowsExceptionWithInvalidFile($filePath)
|
||||||
{
|
{
|
||||||
$loader = new YamlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures')));
|
$loader = new YamlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures')));
|
||||||
$loader->load('nonvalid.yml');
|
$loader->load($filePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function getPathsToInvalidFiles()
|
||||||
* @expectedException \InvalidArgumentException
|
|
||||||
*/
|
|
||||||
public function testLoadThrowsExceptionIfArrayHasUnsupportedKeys()
|
|
||||||
{
|
{
|
||||||
$loader = new YamlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures')));
|
return array(array('nonvalid.yml'), array('nonvalid2.yml'), array('incomplete.yml'), array('nonvalidkeys.yml'), array('nonesense_resource_plus_path.yml'), array('nonesense_type_without_resource.yml'));
|
||||||
$loader->load('nonvalidkeys.yml');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @expectedException \InvalidArgumentException
|
|
||||||
*/
|
|
||||||
public function testLoadThrowsExceptionWhenIncomplete()
|
|
||||||
{
|
|
||||||
$loader = new YamlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures')));
|
|
||||||
$loader->load('incomplete.yml');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testLoadSpecialRouteName()
|
public function testLoadSpecialRouteName()
|
||||||
@ -121,13 +105,4 @@ class YamlFileLoaderTest extends \PHPUnit_Framework_TestCase
|
|||||||
$this->assertEquals('bar', $routes['blog_show']->getOption('foo'));
|
$this->assertEquals('bar', $routes['blog_show']->getOption('foo'));
|
||||||
$this->assertEquals('{locale}.example.com', $routes['blog_show']->getHostnamePattern());
|
$this->assertEquals('{locale}.example.com', $routes['blog_show']->getHostnamePattern());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @expectedException \InvalidArgumentException
|
|
||||||
*/
|
|
||||||
public function testParseRouteThrowsExceptionWithMissingPattern()
|
|
||||||
{
|
|
||||||
$loader = new YamlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures')));
|
|
||||||
$loader->load('incomplete.yml');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user