diff --git a/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php
index 1910ec66a25c6..40cf8f9ee4cae 100644
--- a/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php
+++ b/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php
@@ -47,8 +47,9 @@ public function __construct(ContainerBuilder $container, FileLocatorInterface $l
* @param Definition $prototype A definition to use as template
* @param string $namespace The namespace prefix of classes in the scanned directory
* @param string $resource The directory to look for classes, glob-patterns allowed
+ * @param string $exclude A globed path of files to exclude
*/
- public function registerClasses(Definition $prototype, $namespace, $resource)
+ public function registerClasses(Definition $prototype, $namespace, $resource, $exclude = null)
{
if ('\\' !== substr($namespace, -1)) {
throw new InvalidArgumentException(sprintf('Namespace prefix must end with a "\\": %s.', $namespace));
@@ -57,7 +58,7 @@ public function registerClasses(Definition $prototype, $namespace, $resource)
throw new InvalidArgumentException(sprintf('Namespace is not a valid PSR-4 prefix: %s.', $namespace));
}
- $classes = $this->findClasses($namespace, $resource);
+ $classes = $this->findClasses($namespace, $resource, $exclude);
// prepare for deep cloning
$prototype = serialize($prototype);
@@ -84,9 +85,24 @@ protected function setDefinition($id, Definition $definition)
}
}
- private function findClasses($namespace, $pattern)
+ private function findClasses($namespace, $pattern, $excludePattern)
{
$parameterBag = $this->container->getParameterBag();
+
+ $excludePaths = array();
+ $excludePrefix = null;
+ if ($excludePattern) {
+ $excludePattern = $parameterBag->unescapeValue($parameterBag->resolveValue($excludePattern));
+ foreach ($this->glob($excludePattern, true, $resource) as $path => $info) {
+ if (null === $excludePrefix) {
+ $excludePrefix = $resource->getPrefix();
+ }
+
+ // normalize Windows slashes
+ $excludePaths[str_replace('\\', '/', $path)] = true;
+ }
+ }
+
$pattern = $parameterBag->unescapeValue($parameterBag->resolveValue($pattern));
$classes = array();
$extRegexp = defined('HHVM_VERSION') ? '/\\.(?:php|hh)$/' : '/\\.php$/';
@@ -94,6 +110,14 @@ private function findClasses($namespace, $pattern)
foreach ($this->glob($pattern, true, $resource) as $path => $info) {
if (null === $prefixLen) {
$prefixLen = strlen($resource->getPrefix());
+
+ if ($excludePrefix && strpos($excludePrefix, $resource->getPrefix()) !== 0) {
+ throw new InvalidArgumentException(sprintf('Invalid "exclude" pattern when importing classes for "%s": make sure your "exclude" pattern (%s) is a subset of the "resource" pattern (%s)', $namespace, $excludePattern, $pattern));
+ }
+ }
+
+ if (isset($excludePaths[str_replace('\\', '/', $path)])) {
+ continue;
}
if (!preg_match($extRegexp, $path, $m) || !$info->isReadable()) {
diff --git a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php
index 3ccbdbc79155e..79c8fbf9677ab 100644
--- a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php
+++ b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php
@@ -143,7 +143,7 @@ private function parseDefinitions(\DOMDocument $xml, $file, $defaults)
foreach ($services as $service) {
if (null !== $definition = $this->parseDefinition($service, $file, $defaults)) {
if ('prototype' === $service->tagName) {
- $this->registerClasses($definition, (string) $service->getAttribute('namespace'), (string) $service->getAttribute('resource'));
+ $this->registerClasses($definition, (string) $service->getAttribute('namespace'), (string) $service->getAttribute('resource'), (string) $service->getAttribute('exclude'));
} else {
$this->setDefinition((string) $service->getAttribute('id'), $definition);
}
diff --git a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php
index 3214f4e023cb6..c3e697c1b5207 100644
--- a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php
+++ b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php
@@ -61,6 +61,7 @@ class YamlFileLoader extends FileLoader
private static $prototypeKeywords = array(
'resource' => 'resource',
+ 'exclude' => 'exclude',
'parent' => 'parent',
'shared' => 'shared',
'lazy' => 'lazy',
@@ -528,7 +529,8 @@ private function parseDefinition($id, $service, $file, array $defaults)
if (!is_string($service['resource'])) {
throw new InvalidArgumentException(sprintf('A "resource" attribute must be of type string for service "%s" in %s. Check your YAML syntax.', $id, $file));
}
- $this->registerClasses($definition, $id, $service['resource']);
+ $exclude = isset($service['exclude']) ? $service['exclude'] : null;
+ $this->registerClasses($definition, $id, $service['resource'], $exclude);
} else {
$this->setDefinition($id, $definition);
}
diff --git a/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd b/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd
index 43ea65a2d8577..2de786bdb7cba 100644
--- a/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd
+++ b/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd
@@ -161,6 +161,7 @@
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: