1<?php
2
3/*
4 * This file is part of the Symfony package.
5 *
6 * (c) Fabien Potencier <fabien@symfony.com>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12namespace Symfony\Component\DependencyInjection\Compiler;
13
14use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
15
16/**
17 * Compiler Pass Configuration.
18 *
19 * This class has a default configuration embedded.
20 *
21 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
22 */
23class PassConfig
24{
25    public const TYPE_AFTER_REMOVING = 'afterRemoving';
26    public const TYPE_BEFORE_OPTIMIZATION = 'beforeOptimization';
27    public const TYPE_BEFORE_REMOVING = 'beforeRemoving';
28    public const TYPE_OPTIMIZE = 'optimization';
29    public const TYPE_REMOVE = 'removing';
30
31    private $mergePass;
32    private $afterRemovingPasses = [];
33    private $beforeOptimizationPasses = [];
34    private $beforeRemovingPasses = [];
35    private $optimizationPasses;
36    private $removingPasses;
37
38    public function __construct()
39    {
40        $this->mergePass = new MergeExtensionConfigurationPass();
41
42        $this->beforeOptimizationPasses = [
43            100 => [
44                new ResolveClassPass(),
45                new ResolveInstanceofConditionalsPass(),
46                new RegisterEnvVarProcessorsPass(),
47            ],
48            -1000 => [new ExtensionCompilerPass()],
49        ];
50
51        $this->optimizationPasses = [[
52            new ValidateEnvPlaceholdersPass(),
53            new ResolveChildDefinitionsPass(),
54            new RegisterServiceSubscribersPass(),
55            new ResolveParameterPlaceHoldersPass(false, false),
56            new ResolveFactoryClassPass(),
57            new ResolveNamedArgumentsPass(),
58            new AutowireRequiredMethodsPass(),
59            new ResolveBindingsPass(),
60            new ServiceLocatorTagPass(),
61            new DecoratorServicePass(),
62            new CheckDefinitionValidityPass(),
63            new AutowirePass(false),
64            new ResolveTaggedIteratorArgumentPass(),
65            new ResolveServiceSubscribersPass(),
66            new ResolveReferencesToAliasesPass(),
67            new ResolveInvalidReferencesPass(),
68            new AnalyzeServiceReferencesPass(true),
69            new CheckCircularReferencesPass(),
70            new CheckReferenceValidityPass(),
71            new CheckArgumentsValidityPass(false),
72        ]];
73
74        $this->beforeRemovingPasses = [
75            -100 => [
76                new ResolvePrivatesPass(),
77            ],
78        ];
79
80        $this->removingPasses = [[
81            new RemovePrivateAliasesPass(),
82            new ReplaceAliasByActualDefinitionPass(),
83            new RemoveAbstractDefinitionsPass(),
84            new RemoveUnusedDefinitionsPass(),
85            new AnalyzeServiceReferencesPass(),
86            new CheckExceptionOnInvalidReferenceBehaviorPass(),
87            new InlineServiceDefinitionsPass(new AnalyzeServiceReferencesPass()),
88            new AnalyzeServiceReferencesPass(),
89            new DefinitionErrorExceptionPass(),
90        ]];
91
92        $this->afterRemovingPasses = [[
93            new ResolveHotPathPass(),
94        ]];
95    }
96
97    /**
98     * Returns all passes in order to be processed.
99     *
100     * @return CompilerPassInterface[]
101     */
102    public function getPasses()
103    {
104        return array_merge(
105            [$this->mergePass],
106            $this->getBeforeOptimizationPasses(),
107            $this->getOptimizationPasses(),
108            $this->getBeforeRemovingPasses(),
109            $this->getRemovingPasses(),
110            $this->getAfterRemovingPasses()
111        );
112    }
113
114    /**
115     * Adds a pass.
116     *
117     * @param string $type     The pass type
118     * @param int    $priority Used to sort the passes
119     *
120     * @throws InvalidArgumentException when a pass type doesn't exist
121     */
122    public function addPass(CompilerPassInterface $pass, $type = self::TYPE_BEFORE_OPTIMIZATION, int $priority = 0)
123    {
124        $property = $type.'Passes';
125        if (!isset($this->$property)) {
126            throw new InvalidArgumentException(sprintf('Invalid type "%s".', $type));
127        }
128
129        $passes = &$this->$property;
130
131        if (!isset($passes[$priority])) {
132            $passes[$priority] = [];
133        }
134        $passes[$priority][] = $pass;
135    }
136
137    /**
138     * Gets all passes for the AfterRemoving pass.
139     *
140     * @return CompilerPassInterface[]
141     */
142    public function getAfterRemovingPasses()
143    {
144        return $this->sortPasses($this->afterRemovingPasses);
145    }
146
147    /**
148     * Gets all passes for the BeforeOptimization pass.
149     *
150     * @return CompilerPassInterface[]
151     */
152    public function getBeforeOptimizationPasses()
153    {
154        return $this->sortPasses($this->beforeOptimizationPasses);
155    }
156
157    /**
158     * Gets all passes for the BeforeRemoving pass.
159     *
160     * @return CompilerPassInterface[]
161     */
162    public function getBeforeRemovingPasses()
163    {
164        return $this->sortPasses($this->beforeRemovingPasses);
165    }
166
167    /**
168     * Gets all passes for the Optimization pass.
169     *
170     * @return CompilerPassInterface[]
171     */
172    public function getOptimizationPasses()
173    {
174        return $this->sortPasses($this->optimizationPasses);
175    }
176
177    /**
178     * Gets all passes for the Removing pass.
179     *
180     * @return CompilerPassInterface[]
181     */
182    public function getRemovingPasses()
183    {
184        return $this->sortPasses($this->removingPasses);
185    }
186
187    /**
188     * Gets the Merge pass.
189     *
190     * @return CompilerPassInterface
191     */
192    public function getMergePass()
193    {
194        return $this->mergePass;
195    }
196
197    public function setMergePass(CompilerPassInterface $pass)
198    {
199        $this->mergePass = $pass;
200    }
201
202    /**
203     * Sets the AfterRemoving passes.
204     *
205     * @param CompilerPassInterface[] $passes
206     */
207    public function setAfterRemovingPasses(array $passes)
208    {
209        $this->afterRemovingPasses = [$passes];
210    }
211
212    /**
213     * Sets the BeforeOptimization passes.
214     *
215     * @param CompilerPassInterface[] $passes
216     */
217    public function setBeforeOptimizationPasses(array $passes)
218    {
219        $this->beforeOptimizationPasses = [$passes];
220    }
221
222    /**
223     * Sets the BeforeRemoving passes.
224     *
225     * @param CompilerPassInterface[] $passes
226     */
227    public function setBeforeRemovingPasses(array $passes)
228    {
229        $this->beforeRemovingPasses = [$passes];
230    }
231
232    /**
233     * Sets the Optimization passes.
234     *
235     * @param CompilerPassInterface[] $passes
236     */
237    public function setOptimizationPasses(array $passes)
238    {
239        $this->optimizationPasses = [$passes];
240    }
241
242    /**
243     * Sets the Removing passes.
244     *
245     * @param CompilerPassInterface[] $passes
246     */
247    public function setRemovingPasses(array $passes)
248    {
249        $this->removingPasses = [$passes];
250    }
251
252    /**
253     * Sort passes by priority.
254     *
255     * @param array $passes CompilerPassInterface instances with their priority as key
256     *
257     * @return CompilerPassInterface[]
258     */
259    private function sortPasses(array $passes): array
260    {
261        if (0 === \count($passes)) {
262            return [];
263        }
264
265        krsort($passes);
266
267        // Flatten the array
268        return array_merge(...$passes);
269    }
270}
271