Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 7d07f19

Browse files
committed
Allowing prototype/PSR4 service loading ability to exclude, because glob doesn't support this
1 parent 489fd07 commit 7d07f19

File tree

9 files changed

+100
-6
lines changed

9 files changed

+100
-6
lines changed

src/Symfony/Component/DependencyInjection/Loader/FileLoader.php

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,9 @@ public function __construct(ContainerBuilder $container, FileLocatorInterface $l
4747
* @param Definition $prototype A definition to use as template
4848
* @param string $namespace The namespace prefix of classes in the scanned directory
4949
* @param string $resource The directory to look for classes, glob-patterns allowed
50+
* @param string $exclude A globed path of files to exclude
5051
*/
51-
public function registerClasses(Definition $prototype, $namespace, $resource)
52+
public function registerClasses(Definition $prototype, $namespace, $resource, $exclude = null)
5253
{
5354
if ('\\' !== substr($namespace, -1)) {
5455
throw new InvalidArgumentException(sprintf('Namespace prefix must end with a "\\": %s.', $namespace));
@@ -57,7 +58,7 @@ public function registerClasses(Definition $prototype, $namespace, $resource)
5758
throw new InvalidArgumentException(sprintf('Namespace is not a valid PSR-4 prefix: %s.', $namespace));
5859
}
5960

60-
$classes = $this->findClasses($namespace, $resource);
61+
$classes = $this->findClasses($namespace, $resource, $exclude);
6162
// prepare for deep cloning
6263
$prototype = serialize($prototype);
6364

@@ -84,16 +85,39 @@ protected function setDefinition($id, Definition $definition)
8485
}
8586
}
8687

87-
private function findClasses($namespace, $pattern)
88+
private function findClasses($namespace, $pattern, $excludePattern)
8889
{
8990
$parameterBag = $this->container->getParameterBag();
91+
92+
$excludePaths = array();
93+
$excludePrefix = null;
94+
if ($excludePattern) {
95+
$excludePattern = $parameterBag->unescapeValue($parameterBag->resolveValue($excludePattern));
96+
foreach ($this->glob($excludePattern, true, $resource) as $path => $info) {
97+
if (null === $excludePrefix) {
98+
$excludePrefix = $resource->getPrefix();
99+
}
100+
101+
// normalize Windows slashes
102+
$excludePaths[str_replace('\\', '/', $path)] = true;
103+
}
104+
}
105+
90106
$pattern = $parameterBag->unescapeValue($parameterBag->resolveValue($pattern));
91107
$classes = array();
92108
$extRegexp = defined('HHVM_VERSION') ? '/\\.(?:php|hh)$/' : '/\\.php$/';
93109
$prefixLen = null;
94110
foreach ($this->glob($pattern, true, $resource) as $path => $info) {
95111
if (null === $prefixLen) {
96112
$prefixLen = strlen($resource->getPrefix());
113+
114+
if ($excludePrefix && strpos($excludePrefix, $resource->getPrefix()) !== 0) {
115+
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));
116+
}
117+
}
118+
119+
if (isset($excludePaths[str_replace('\\', '/', $path)])) {
120+
continue;
97121
}
98122

99123
if (!preg_match($extRegexp, $path, $m) || !$info->isReadable()) {

src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ private function parseDefinitions(\DOMDocument $xml, $file, $defaults)
143143
foreach ($services as $service) {
144144
if (null !== $definition = $this->parseDefinition($service, $file, $defaults)) {
145145
if ('prototype' === $service->tagName) {
146-
$this->registerClasses($definition, (string) $service->getAttribute('namespace'), (string) $service->getAttribute('resource'));
146+
$this->registerClasses($definition, (string) $service->getAttribute('namespace'), (string) $service->getAttribute('resource'), (string) $service->getAttribute('exclude'));
147147
} else {
148148
$this->setDefinition((string) $service->getAttribute('id'), $definition);
149149
}

src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ class YamlFileLoader extends FileLoader
6161

6262
private static $prototypeKeywords = array(
6363
'resource' => 'resource',
64+
'exclude' => 'exclude',
6465
'parent' => 'parent',
6566
'shared' => 'shared',
6667
'lazy' => 'lazy',
@@ -528,7 +529,8 @@ private function parseDefinition($id, $service, $file, array $defaults)
528529
if (!is_string($service['resource'])) {
529530
throw new InvalidArgumentException(sprintf('A "resource" attribute must be of type string for service "%s" in %s. Check your YAML syntax.', $id, $file));
530531
}
531-
$this->registerClasses($definition, $id, $service['resource']);
532+
$exclude = isset($service['exclude']) ? $service['exclude'] : null;
533+
$this->registerClasses($definition, $id, $service['resource'], $exclude);
532534
} else {
533535
$this->setDefinition($id, $definition);
534536
}

src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@
161161
</xsd:choice>
162162
<xsd:attribute name="namespace" type="xsd:string" use="required" />
163163
<xsd:attribute name="resource" type="xsd:string" use="required" />
164+
<xsd:attribute name="exclude" type="xsd:string" />
164165
<xsd:attribute name="shared" type="boolean" />
165166
<xsd:attribute name="public" type="boolean" />
166167
<xsd:attribute name="lazy" type="boolean" />
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
3+
namespace Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\OtherDir\AnotherSub;
4+
5+
class DeeperBaz
6+
{
7+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
3+
namespace Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\OtherDir;
4+
5+
class Baz
6+
{
7+
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
33
<services>
4-
<prototype namespace="Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\" resource="../Prototype/*" />
4+
<prototype namespace="Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\" resource="../Prototype/*" exclude="../Prototype/{OtherDir}" />
55
</services>
66
</container>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
services:
22
Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\:
33
resource: ../Prototype
4+
exclude: '../Prototype/{OtherDir}'

src/Symfony/Component/DependencyInjection/Tests/Loader/FileLoaderTest.php

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,15 @@
1616
use Symfony\Component\Config\Loader\LoaderResolver;
1717
use Symfony\Component\DependencyInjection\ContainerBuilder;
1818
use Symfony\Component\DependencyInjection\Definition;
19+
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
1920
use Symfony\Component\DependencyInjection\Loader\FileLoader;
2021
use Symfony\Component\DependencyInjection\Loader\IniFileLoader;
2122
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
2223
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
2324
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
2425
use Symfony\Component\DependencyInjection\Reference;
26+
use Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\OtherDir\AnotherSub\DeeperBaz;
27+
use Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\OtherDir\Baz;
2528
use Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\Sub\Bar;
2629

2730
class FileLoaderTest extends TestCase
@@ -90,6 +93,26 @@ public function testRegisterClasses()
9093
);
9194
}
9295

96+
public function testRegisterClassesWithExclude()
97+
{
98+
$container = new ContainerBuilder();
99+
$container->setParameter('other_dir', 'OtherDir');
100+
$loader = new TestFileLoader($container, new FileLocator(self::$fixturesPath.'/Fixtures'));
101+
102+
$loader->registerClasses(
103+
new Definition(),
104+
'Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\\',
105+
'Prototype/*',
106+
// load everything, except OtherDir/AnotherSub & Foo.php
107+
'Prototype/{%other_dir%/AnotherSub,Foo.php}'
108+
);
109+
110+
$this->assertTrue($container->has(Bar::class));
111+
$this->assertTrue($container->has(Baz::class));
112+
$this->assertFalse($container->has(Foo::class));
113+
$this->assertFalse($container->has(DeeperBaz::class));
114+
}
115+
93116
/**
94117
* @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
95118
* @expectedExceptionMessageRegExp /Expected to find class "Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\Prototype\\Bar" in file ".+" while importing services from resource "Prototype\/Sub\/\*", but it was not found\! Check the namespace prefix used with the resource/
@@ -102,6 +125,35 @@ public function testRegisterClassesWithBadPrefix()
102125
// the Sub is missing from namespace prefix
103126
$loader->registerClasses(new Definition(), 'Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\\', 'Prototype/Sub/*');
104127
}
128+
129+
/**
130+
* @dataProvider getIncompatibleExcludeTests
131+
*/
132+
public function testRegisterClassesWithIncompatibleExclude($resourcePattern, $excludePattern)
133+
{
134+
$container = new ContainerBuilder();
135+
$loader = new TestFileLoader($container, new FileLocator(self::$fixturesPath.'/Fixtures'));
136+
137+
try {
138+
$loader->registerClasses(
139+
new Definition(),
140+
'Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\\',
141+
$resourcePattern,
142+
$excludePattern
143+
);
144+
} catch (InvalidArgumentException $e) {
145+
$this->assertEquals(
146+
sprintf('Invalid "exclude" pattern when importing classes for "Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\": make sure your "exclude" pattern (%s) is a subset of the "resource" pattern (%s)', $excludePattern, $resourcePattern),
147+
$e->getMessage()
148+
);
149+
}
150+
}
151+
152+
public function getIncompatibleExcludeTests()
153+
{
154+
yield array('Prototype/*', 'yaml/*', false);
155+
yield array('Prototype/OtherDir/*', 'Prototype/*', false);
156+
}
105157
}
106158

107159
class TestFileLoader extends FileLoader

0 commit comments

Comments
 (0)