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\ContainerBuilder;
15
16/**
17 * Removes unused service definitions from the container.
18 *
19 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
20 */
21class RemoveUnusedDefinitionsPass implements RepeatablePassInterface
22{
23    private $repeatedPass;
24
25    /**
26     * {@inheritdoc}
27     */
28    public function setRepeatedPass(RepeatedPass $repeatedPass)
29    {
30        $this->repeatedPass = $repeatedPass;
31    }
32
33    /**
34     * Processes the ContainerBuilder to remove unused definitions.
35     */
36    public function process(ContainerBuilder $container)
37    {
38        $graph = $container->getCompiler()->getServiceReferenceGraph();
39
40        $hasChanged = false;
41        foreach ($container->getDefinitions() as $id => $definition) {
42            if ($definition->isPublic() || $definition->isPrivate()) {
43                continue;
44            }
45
46            if ($graph->hasNode($id)) {
47                $edges = $graph->getNode($id)->getInEdges();
48                $referencingAliases = [];
49                $sourceIds = [];
50                foreach ($edges as $edge) {
51                    if ($edge->isWeak()) {
52                        continue;
53                    }
54                    $node = $edge->getSourceNode();
55                    $sourceIds[] = $node->getId();
56
57                    if ($node->isAlias()) {
58                        $referencingAliases[] = $node->getValue();
59                    }
60                }
61                $isReferenced = (\count(array_unique($sourceIds)) - \count($referencingAliases)) > 0;
62            } else {
63                $referencingAliases = [];
64                $isReferenced = false;
65            }
66
67            if (1 === \count($referencingAliases) && false === $isReferenced) {
68                $container->setDefinition((string) reset($referencingAliases), $definition);
69                $definition->setPublic(!$definition->isPrivate());
70                $definition->setPrivate(reset($referencingAliases)->isPrivate());
71                $container->removeDefinition($id);
72                $container->log($this, sprintf('Removed service "%s"; reason: replaces alias %s.', $id, reset($referencingAliases)));
73            } elseif (0 === \count($referencingAliases) && false === $isReferenced) {
74                $container->removeDefinition($id);
75                $container->resolveEnvPlaceholders(serialize($definition));
76                $container->log($this, sprintf('Removed service "%s"; reason: unused.', $id));
77                $hasChanged = true;
78            }
79        }
80
81        if ($hasChanged) {
82            $this->repeatedPass->setRepeat();
83        }
84    }
85}
86