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;
15use Symfony\Component\DependencyInjection\Exception\EnvParameterException;
16
17/**
18 * This class is used to remove circular dependencies between individual passes.
19 *
20 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
21 */
22class Compiler
23{
24    private $passConfig;
25    private $log = [];
26    private $loggingFormatter;
27    private $serviceReferenceGraph;
28
29    public function __construct()
30    {
31        $this->passConfig = new PassConfig();
32        $this->serviceReferenceGraph = new ServiceReferenceGraph();
33    }
34
35    /**
36     * Returns the PassConfig.
37     *
38     * @return PassConfig The PassConfig instance
39     */
40    public function getPassConfig()
41    {
42        return $this->passConfig;
43    }
44
45    /**
46     * Returns the ServiceReferenceGraph.
47     *
48     * @return ServiceReferenceGraph The ServiceReferenceGraph instance
49     */
50    public function getServiceReferenceGraph()
51    {
52        return $this->serviceReferenceGraph;
53    }
54
55    /**
56     * Returns the logging formatter which can be used by compilation passes.
57     *
58     * @return LoggingFormatter
59     *
60     * @deprecated since version 3.3, to be removed in 4.0. Use the ContainerBuilder::log() method instead.
61     */
62    public function getLoggingFormatter()
63    {
64        if (null === $this->loggingFormatter) {
65            @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the ContainerBuilder::log() method instead.', __METHOD__), E_USER_DEPRECATED);
66
67            $this->loggingFormatter = new LoggingFormatter();
68        }
69
70        return $this->loggingFormatter;
71    }
72
73    /**
74     * Adds a pass to the PassConfig.
75     *
76     * @param CompilerPassInterface $pass A compiler pass
77     * @param string                $type The type of the pass
78     */
79    public function addPass(CompilerPassInterface $pass, $type = PassConfig::TYPE_BEFORE_OPTIMIZATION/*, int $priority = 0*/)
80    {
81        if (\func_num_args() >= 3) {
82            $priority = func_get_arg(2);
83        } else {
84            if (__CLASS__ !== static::class) {
85                $r = new \ReflectionMethod($this, __FUNCTION__);
86                if (__CLASS__ !== $r->getDeclaringClass()->getName()) {
87                    @trigger_error(sprintf('Method %s() will have a third `int $priority = 0` argument in version 4.0. Not defining it is deprecated since Symfony 3.2.', __METHOD__), E_USER_DEPRECATED);
88                }
89            }
90
91            $priority = 0;
92        }
93
94        $this->passConfig->addPass($pass, $type, $priority);
95    }
96
97    /**
98     * Adds a log message.
99     *
100     * @param string $string The log message
101     *
102     * @deprecated since version 3.3, to be removed in 4.0. Use the ContainerBuilder::log() method instead.
103     */
104    public function addLogMessage($string)
105    {
106        @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the ContainerBuilder::log() method instead.', __METHOD__), E_USER_DEPRECATED);
107
108        $this->log[] = $string;
109    }
110
111    /**
112     * @final
113     */
114    public function log(CompilerPassInterface $pass, $message)
115    {
116        if (false !== strpos($message, "\n")) {
117            $message = str_replace("\n", "\n".\get_class($pass).': ', trim($message));
118        }
119
120        $this->log[] = \get_class($pass).': '.$message;
121    }
122
123    /**
124     * Returns the log.
125     *
126     * @return array Log array
127     */
128    public function getLog()
129    {
130        return $this->log;
131    }
132
133    /**
134     * Run the Compiler and process all Passes.
135     */
136    public function compile(ContainerBuilder $container)
137    {
138        try {
139            foreach ($this->passConfig->getPasses() as $pass) {
140                $pass->process($container);
141            }
142        } catch (\Exception $e) {
143            $usedEnvs = [];
144            $prev = $e;
145
146            do {
147                $msg = $prev->getMessage();
148
149                if ($msg !== $resolvedMsg = $container->resolveEnvPlaceholders($msg, null, $usedEnvs)) {
150                    $r = new \ReflectionProperty($prev, 'message');
151                    $r->setAccessible(true);
152                    $r->setValue($prev, $resolvedMsg);
153                }
154            } while ($prev = $prev->getPrevious());
155
156            if ($usedEnvs) {
157                $e = new EnvParameterException($usedEnvs, $e);
158            }
159
160            throw $e;
161        } finally {
162            $this->getServiceReferenceGraph()->clear();
163        }
164    }
165}
166