Skip to content

Commit b420790

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

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+
// realpath to make sure Windows slashes are consistent
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

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:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy