Skip to content

Commit 81f4f63

Browse files
committed
feature symfony#18022 [DependencyInjection] Sort the CompilerPass by priority (Ener-Getick)
This PR was squashed before being merged into the 3.2-dev branch (closes symfony#18022). Discussion ---------- [DependencyInjection] Sort the CompilerPass by priority | Q | A | ------------- | --- | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | symfony#10778 | License | MIT | Doc PR | This PR replaces the CompilerPass types by a new priority argument. Sometimes we want to be sure that a CompilerPass will be executed after another but we can't do that because we don't know when the other pass will be added. This PR fixes this by allowing people to simply choose when their compiler passes will be executed. Things to debate: - the constants value - should we create a new function to get/set passes for a specific priority ? Commits ------- d17c1a9 [DependencyInjection] Sort the CompilerPass by priority
2 parents fce0299 + d17c1a9 commit 81f4f63

File tree

6 files changed

+139
-47
lines changed

6 files changed

+139
-47
lines changed

src/Symfony/Component/DependencyInjection/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
CHANGELOG
22
=========
33

4+
3.2.0
5+
-----
6+
7+
* allowed to prioritize compiler passes by introducing a third argument to `PassConfig::addPass()`, to `Compiler::addPass` and to `ContainerBuilder::addCompilerPass()`
8+
49
3.0.0
510
-----
611

src/Symfony/Component/DependencyInjection/Compiler/Compiler.php

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,20 @@ public function getLoggingFormatter()
6565
/**
6666
* Adds a pass to the PassConfig.
6767
*
68-
* @param CompilerPassInterface $pass A compiler pass
69-
* @param string $type The type of the pass
68+
* @param CompilerPassInterface $pass A compiler pass
69+
* @param string $type The type of the pass
70+
* @param int $priority Used to sort the passes
7071
*/
71-
public function addPass(CompilerPassInterface $pass, $type = PassConfig::TYPE_BEFORE_OPTIMIZATION)
72+
public function addPass(CompilerPassInterface $pass, $type = PassConfig::TYPE_BEFORE_OPTIMIZATION/**, $priority = 0*/)
7273
{
73-
$this->passConfig->addPass($pass, $type);
74+
// For BC
75+
if (func_num_args() >= 3) {
76+
$priority = func_get_arg(2);
77+
} else {
78+
$priority = 0;
79+
}
80+
81+
$this->passConfig->addPass($pass, $type, $priority);
7482
}
7583

7684
/**

src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php

Lines changed: 67 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public function __construct()
3939
{
4040
$this->mergePass = new MergeExtensionConfigurationPass();
4141

42-
$this->optimizationPasses = array(
42+
$this->optimizationPasses = array(array(
4343
new ExtensionCompilerPass(),
4444
new ResolveDefinitionTemplatesPass(),
4545
new DecoratorServicePass(),
@@ -51,9 +51,9 @@ public function __construct()
5151
new AnalyzeServiceReferencesPass(true),
5252
new CheckCircularReferencesPass(),
5353
new CheckReferenceValidityPass(),
54-
);
54+
));
5555

56-
$this->removingPasses = array(
56+
$this->removingPasses = array(array(
5757
new RemovePrivateAliasesPass(),
5858
new ReplaceAliasByActualDefinitionPass(),
5959
new RemoveAbstractDefinitionsPass(),
@@ -64,98 +64,111 @@ public function __construct()
6464
new RemoveUnusedDefinitionsPass(),
6565
)),
6666
new CheckExceptionOnInvalidReferenceBehaviorPass(),
67-
);
67+
));
6868
}
6969

7070
/**
7171
* Returns all passes in order to be processed.
7272
*
73-
* @return array An array of all passes to process
73+
* @return CompilerPassInterface[]
7474
*/
7575
public function getPasses()
7676
{
7777
return array_merge(
7878
array($this->mergePass),
79-
$this->beforeOptimizationPasses,
80-
$this->optimizationPasses,
81-
$this->beforeRemovingPasses,
82-
$this->removingPasses,
83-
$this->afterRemovingPasses
79+
$this->getBeforeOptimizationPasses(),
80+
$this->getOptimizationPasses(),
81+
$this->getBeforeRemovingPasses(),
82+
$this->getRemovingPasses(),
83+
$this->getAfterRemovingPasses()
8484
);
8585
}
8686

8787
/**
8888
* Adds a pass.
8989
*
90-
* @param CompilerPassInterface $pass A Compiler pass
91-
* @param string $type The pass type
90+
* @param CompilerPassInterface $pass A Compiler pass
91+
* @param string $type The pass type
92+
* @param int $priority Used to sort the passes
9293
*
9394
* @throws InvalidArgumentException when a pass type doesn't exist
9495
*/
95-
public function addPass(CompilerPassInterface $pass, $type = self::TYPE_BEFORE_OPTIMIZATION)
96+
public function addPass(CompilerPassInterface $pass, $type = self::TYPE_BEFORE_OPTIMIZATION/*, $priority = 0*/)
9697
{
98+
// For BC
99+
if (func_num_args() >= 3) {
100+
$priority = func_get_arg(2);
101+
} else {
102+
$priority = 0;
103+
}
104+
97105
$property = $type.'Passes';
98106
if (!isset($this->$property)) {
99107
throw new InvalidArgumentException(sprintf('Invalid type "%s".', $type));
100108
}
101109

102-
$this->{$property}[] = $pass;
110+
$passes = &$this->$property;
111+
112+
if (!isset($passes[$priority])) {
113+
$passes[$priority] = array();
114+
}
115+
$passes[$priority][] = $pass;
103116
}
104117

105118
/**
106119
* Gets all passes for the AfterRemoving pass.
107120
*
108-
* @return array An array of passes
121+
* @return CompilerPassInterface[]
109122
*/
110123
public function getAfterRemovingPasses()
111124
{
112-
return $this->afterRemovingPasses;
125+
return $this->sortPasses($this->afterRemovingPasses);
113126
}
114127

115128
/**
116129
* Gets all passes for the BeforeOptimization pass.
117130
*
118-
* @return array An array of passes
131+
* @return CompilerPassInterface[]
119132
*/
120133
public function getBeforeOptimizationPasses()
121134
{
122-
return $this->beforeOptimizationPasses;
135+
return $this->sortPasses($this->beforeOptimizationPasses);
123136
}
124137

125138
/**
126139
* Gets all passes for the BeforeRemoving pass.
127140
*
128-
* @return array An array of passes
141+
* @return CompilerPassInterface[]
129142
*/
130143
public function getBeforeRemovingPasses()
131144
{
132-
return $this->beforeRemovingPasses;
145+
return $this->sortPasses($this->beforeRemovingPasses);
133146
}
134147

135148
/**
136149
* Gets all passes for the Optimization pass.
137150
*
138-
* @return array An array of passes
151+
* @return CompilerPassInterface[]
139152
*/
140153
public function getOptimizationPasses()
141154
{
142-
return $this->optimizationPasses;
155+
return $this->sortPasses($this->optimizationPasses);
143156
}
144157

145158
/**
146159
* Gets all passes for the Removing pass.
147160
*
148-
* @return array An array of passes
161+
* @return CompilerPassInterface[]
149162
*/
150163
public function getRemovingPasses()
151164
{
152-
return $this->removingPasses;
165+
return $this->sortPasses($this->removingPasses);
153166
}
154167

155168
/**
156169
* Gets all passes for the Merge pass.
157170
*
158-
* @return array An array of passes
171+
* @return CompilerPassInterface
159172
*/
160173
public function getMergePass()
161174
{
@@ -175,50 +188,69 @@ public function setMergePass(CompilerPassInterface $pass)
175188
/**
176189
* Sets the AfterRemoving passes.
177190
*
178-
* @param array $passes An array of passes
191+
* @param CompilerPassInterface[] $passes
179192
*/
180193
public function setAfterRemovingPasses(array $passes)
181194
{
182-
$this->afterRemovingPasses = $passes;
195+
$this->afterRemovingPasses = array($passes);
183196
}
184197

185198
/**
186199
* Sets the BeforeOptimization passes.
187200
*
188-
* @param array $passes An array of passes
201+
* @param CompilerPassInterface[] $passes
189202
*/
190203
public function setBeforeOptimizationPasses(array $passes)
191204
{
192-
$this->beforeOptimizationPasses = $passes;
205+
$this->beforeOptimizationPasses = array($passes);
193206
}
194207

195208
/**
196209
* Sets the BeforeRemoving passes.
197210
*
198-
* @param array $passes An array of passes
211+
* @param CompilerPassInterface[] $passes
199212
*/
200213
public function setBeforeRemovingPasses(array $passes)
201214
{
202-
$this->beforeRemovingPasses = $passes;
215+
$this->beforeRemovingPasses = array($passes);
203216
}
204217

205218
/**
206219
* Sets the Optimization passes.
207220
*
208-
* @param array $passes An array of passes
221+
* @param CompilerPassInterface[] $passes
209222
*/
210223
public function setOptimizationPasses(array $passes)
211224
{
212-
$this->optimizationPasses = $passes;
225+
$this->optimizationPasses = array($passes);
213226
}
214227

215228
/**
216229
* Sets the Removing passes.
217230
*
218-
* @param array $passes An array of passes
231+
* @param CompilerPassInterface[] $passes
219232
*/
220233
public function setRemovingPasses(array $passes)
221234
{
222-
$this->removingPasses = $passes;
235+
$this->removingPasses = array($passes);
236+
}
237+
238+
/**
239+
* Sort passes by priority.
240+
*
241+
* @param array $passes CompilerPassInterface instances with their priority as key.
242+
*
243+
* @return CompilerPassInterface[]
244+
*/
245+
private function sortPasses(array $passes)
246+
{
247+
if (0 === count($passes)) {
248+
return array();
249+
}
250+
251+
krsort($passes);
252+
253+
// Flatten the array
254+
return call_user_func_array('array_merge', $passes);
223255
}
224256
}

src/Symfony/Component/DependencyInjection/ContainerBuilder.php

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -298,14 +298,22 @@ public function loadFromExtension($extension, array $values = array())
298298
/**
299299
* Adds a compiler pass.
300300
*
301-
* @param CompilerPassInterface $pass A compiler pass
302-
* @param string $type The type of compiler pass
301+
* @param CompilerPassInterface $pass A compiler pass
302+
* @param string $type The type of compiler pass
303+
* @param int $priority Used to sort the passes
303304
*
304305
* @return ContainerBuilder The current instance
305306
*/
306-
public function addCompilerPass(CompilerPassInterface $pass, $type = PassConfig::TYPE_BEFORE_OPTIMIZATION)
307+
public function addCompilerPass(CompilerPassInterface $pass, $type = PassConfig::TYPE_BEFORE_OPTIMIZATION/**, $priority = 0*/)
307308
{
308-
$this->getCompiler()->addPass($pass, $type);
309+
// For BC
310+
if (func_num_args() >= 3) {
311+
$priority = func_get_arg(2);
312+
} else {
313+
$priority = 0;
314+
}
315+
316+
$this->getCompiler()->addPass($pass, $type, $priority);
309317

310318
$this->addObjectResource($pass);
311319

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\DependencyInjection\Tests\Compiler;
13+
14+
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
15+
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
16+
17+
/**
18+
* @author Guilhem N <[email protected]>
19+
*/
20+
class PassConfigTest extends \PHPUnit_Framework_TestCase
21+
{
22+
public function testPassOrdering()
23+
{
24+
$config = new PassConfig();
25+
26+
$pass1 = $this->getMock(CompilerPassInterface::class);
27+
$config->addPass($pass1, PassConfig::TYPE_BEFORE_OPTIMIZATION, 10);
28+
29+
$pass2 = $this->getMock(CompilerPassInterface::class);
30+
$config->addPass($pass2, PassConfig::TYPE_BEFORE_OPTIMIZATION, 30);
31+
32+
$this->assertSame(array($pass2, $pass1), $config->getBeforeOptimizationPasses());
33+
}
34+
}

src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use Symfony\Bridge\PhpUnit\ErrorAssert;
1818
use Symfony\Component\Config\Resource\ResourceInterface;
1919
use Symfony\Component\DependencyInjection\Alias;
20+
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
2021
use Symfony\Component\DependencyInjection\ContainerBuilder;
2122
use Symfony\Component\DependencyInjection\ContainerInterface;
2223
use Symfony\Component\DependencyInjection\Definition;
@@ -284,10 +285,14 @@ public function testAddGetCompilerPass()
284285
{
285286
$builder = new ContainerBuilder();
286287
$builder->setResourceTracking(false);
287-
$builderCompilerPasses = $builder->getCompiler()->getPassConfig()->getPasses();
288-
$builder->addCompilerPass($this->getMock('Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface'));
289-
290-
$this->assertCount(count($builder->getCompiler()->getPassConfig()->getPasses()) - 1, $builderCompilerPasses);
288+
$defaultPasses = $builder->getCompiler()->getPassConfig()->getPasses();
289+
$builder->addCompilerPass($pass1 = $this->getMock('Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface'), PassConfig::TYPE_BEFORE_OPTIMIZATION, -5);
290+
$builder->addCompilerPass($pass2 = $this->getMock('Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface'), PassConfig::TYPE_BEFORE_OPTIMIZATION, 10);
291+
292+
$passes = $builder->getCompiler()->getPassConfig()->getPasses();
293+
$this->assertCount(count($passes) - 2, $defaultPasses);
294+
// Pass 1 is executed later
295+
$this->assertTrue(array_search($pass1, $passes, true) > array_search($pass2, $passes, true));
291296
}
292297

293298
public function testCreateService()

0 commit comments

Comments
 (0)