Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitfb532bf

Browse files
committed
feature#22680 [DI] Fixing missing "exclude" functionality from PSR4 loader (weaverryan)
This PR was merged into the 3.3-dev branch.Discussion----------[DI] Fixing missing "exclude" functionality from PSR4 loader| Q | A| ------------- | ---| Branch? | master| Bug fix? | yes| New feature? | yes| BC breaks? | no| Deprecations? | no| Tests pass? | yes| Fixed tickets | none| License | MIT| Doc PR | TODOWhen the PSR4 loader was added in#21289,@nicolas-grekas said this:> given that glob() is powerful enough to include/exclude dirs, I removed the Test special exclusion (source:#21289 (comment))But, I don't believe that's true! [Glob is all about inclusion, not exclusion](https://en.wikipedia.org/wiki/Glob_(programming)#Syntax) - the maximum you can exclude is a single character. Thus, I've marked this as a bug.My motivation came from upgrading KnpU to the new 3.3 DI stuff. We have many directories (40) in `src/`, and listing them all one-by-one in `resource:` is crazy - the `resource` line would be ~350 characters long and quite unreadable. What I really want to do is include *everything* and then exclude few directories. I tried to do this with `glob`, but it's not possible.This PR allows for something like this:```ymlservices: _defaults: public: false # ... AppBundle\: resource: '../../src/AppBundle/*' exclude: '../../src/AppBundle/{AppBundle.php,Entity}'```This works *beautifully* in practice. And even if I forget to exclude a directory - since the services are private - **they're ultimately removed from the container anyways**. In fact, the *only* reason I need to exclude *anything* is because of the new "service argument resolver", which causes entities to not be properly removed from the compiled container.Thanks!Commits-------7d07f19 Allowing prototype/PSR4 service loading ability to exclude, because glob doesn't support this
2 parents76190ab +7d07f19 commitfb532bf

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-
publicfunctionregisterClasses(Definition$prototype,$namespace,$resource)
52+
publicfunctionregisterClasses(Definition$prototype,$namespace,$resource,$exclude =null)
5253
{
5354
if ('\\' !==substr($namespace, -1)) {
5455
thrownewInvalidArgumentException(sprintf('Namespace prefix must end with a "\\": %s.',$namespace));
@@ -57,7 +58,7 @@ public function registerClasses(Definition $prototype, $namespace, $resource)
5758
thrownewInvalidArgumentException(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-
privatefunctionfindClasses($namespace,$pattern)
88+
privatefunctionfindClasses($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+
thrownewInvalidArgumentException(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 ($servicesas$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
privatestatic$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
thrownewInvalidArgumentException(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:attributename="namespace"type="xsd:string"use="required" />
163163
<xsd:attributename="resource"type="xsd:string"use="required" />
164+
<xsd:attributename="exclude"type="xsd:string" />
164165
<xsd:attributename="shared"type="boolean" />
165166
<xsd:attributename="public"type="boolean" />
166167
<xsd:attributename="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+
namespaceSymfony\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+
namespaceSymfony\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
<containerxmlns="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-
<prototypenamespace="Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\"resource="../Prototype/*" />
4+
<prototypenamespace="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
useSymfony\Component\Config\Loader\LoaderResolver;
1717
useSymfony\Component\DependencyInjection\ContainerBuilder;
1818
useSymfony\Component\DependencyInjection\Definition;
19+
useSymfony\Component\DependencyInjection\Exception\InvalidArgumentException;
1920
useSymfony\Component\DependencyInjection\Loader\FileLoader;
2021
useSymfony\Component\DependencyInjection\Loader\IniFileLoader;
2122
useSymfony\Component\DependencyInjection\Loader\PhpFileLoader;
2223
useSymfony\Component\DependencyInjection\Loader\XmlFileLoader;
2324
useSymfony\Component\DependencyInjection\Loader\YamlFileLoader;
2425
useSymfony\Component\DependencyInjection\Reference;
26+
useSymfony\Component\DependencyInjection\Tests\Fixtures\Prototype\OtherDir\AnotherSub\DeeperBaz;
27+
useSymfony\Component\DependencyInjection\Tests\Fixtures\Prototype\OtherDir\Baz;
2528
useSymfony\Component\DependencyInjection\Tests\Fixtures\Prototype\Sub\Bar;
2629

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

96+
publicfunctiontestRegisterClassesWithExclude()
97+
{
98+
$container =newContainerBuilder();
99+
$container->setParameter('other_dir','OtherDir');
100+
$loader =newTestFileLoader($container,newFileLocator(self::$fixturesPath.'/Fixtures'));
101+
102+
$loader->registerClasses(
103+
newDefinition(),
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(newDefinition(),'Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\\','Prototype/Sub/*');
104127
}
128+
129+
/**
130+
* @dataProvider getIncompatibleExcludeTests
131+
*/
132+
publicfunctiontestRegisterClassesWithIncompatibleExclude($resourcePattern,$excludePattern)
133+
{
134+
$container =newContainerBuilder();
135+
$loader =newTestFileLoader($container,newFileLocator(self::$fixturesPath.'/Fixtures'));
136+
137+
try {
138+
$loader->registerClasses(
139+
newDefinition(),
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+
publicfunctiongetIncompatibleExcludeTests()
153+
{
154+
yieldarray('Prototype/*','yaml/*',false);
155+
yieldarray('Prototype/OtherDir/*','Prototype/*',false);
156+
}
105157
}
106158

107159
class TestFileLoaderextends FileLoader

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp