PluginProbe
Packeta / trunk
Packeta vtrunk
2.3.2 2.3.1 trunk 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.3.0 1.3.1 1.3.2 1.4 1.4.1 1.4.2 1.4.3 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 All 56 releases
packeta / deps / nette / di / src / DI / Compiler.php

Compiler.php in Packeta trunk, at deps/nette/di/src/DI/Compiler.php

252 lines 9.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * This file is part of the Nette Framework (https://nette.org)
5 * Copyright (c) 2004 David Grudl (https://davidgrudl.com)
6 */
7 declare (strict_types=1);
8 namespace Packetery\Nette\DI;
9
10 use Packetery\Nette;
11 use Packetery\Nette\Schema;
12 /**
13 * DI container compiler.
14 */
15 class Compiler
16 {
17 use \Packetery\Nette\SmartObject;
18 private const Services = 'services', Parameters = 'parameters', DI = 'di';
19 /** @var CompilerExtension[] */
20 private $extensions = [];
21 /** @var ContainerBuilder */
22 private $builder;
23 /** @var array */
24 private $config = [];
25 /** @var array [section => array[]] */
26 private $configs = [];
27 /** @var string */
28 private $sources = '';
29 /** @var DependencyChecker */
30 private $dependencies;
31 /** @var string */
32 private $className = 'Container';
33 public function __construct(?ContainerBuilder $builder = null)
34 {
35 $this->builder = $builder ?: new ContainerBuilder();
36 $this->dependencies = new DependencyChecker();
37 $this->addExtension(self::Services, new Extensions\ServicesExtension());
38 $this->addExtension(self::Parameters, new Extensions\ParametersExtension($this->configs));
39 }
40 /**
41 * Add custom configurator extension.
42 * @return static
43 */
44 public function addExtension(?string $name, CompilerExtension $extension)
45 {
46 if ($name === null) {
47 $name = '_' . \count($this->extensions);
48 } elseif (isset($this->extensions[$name])) {
49 throw new \Packetery\Nette\InvalidArgumentException(\sprintf("Name '%s' is already used or reserved.", $name));
50 }
51 $lname = \strtolower($name);
52 foreach (\array_keys($this->extensions) as $nm) {
53 if ($lname === \strtolower((string) $nm)) {
54 throw new \Packetery\Nette\InvalidArgumentException(\sprintf("Name of extension '%s' has the same name as '%s' in a case-insensitive manner.", $name, $nm));
55 }
56 }
57 $this->extensions[$name] = $extension->setCompiler($this, $name);
58 return $this;
59 }
60 public function getExtensions(?string $type = null) : array
61 {
62 return $type ? \array_filter($this->extensions, function ($item) use($type) : bool {
63 return $item instanceof $type;
64 }) : $this->extensions;
65 }
66 public function getContainerBuilder() : ContainerBuilder
67 {
68 return $this->builder;
69 }
70 /** @return static */
71 public function setClassName(string $className)
72 {
73 $this->className = $className;
74 return $this;
75 }
76 /**
77 * Adds new configuration.
78 * @return static
79 */
80 public function addConfig(array $config)
81 {
82 foreach ($config as $section => $data) {
83 $this->configs[$section][] = $data;
84 }
85 $this->sources .= "// source: array\n";
86 return $this;
87 }
88 /**
89 * Adds new configuration from file.
90 * @return static
91 */
92 public function loadConfig(string $file, ?Config\Loader $loader = null)
93 {
94 $sources = $this->sources . "// source: {$file}\n";
95 $loader = $loader ?: new Config\Loader();
96 foreach ($loader->load($file, \false) as $data) {
97 $this->addConfig($data);
98 }
99 $this->dependencies->add($loader->getDependencies());
100 $this->sources = $sources;
101 return $this;
102 }
103 /**
104 * Returns configuration.
105 * @deprecated
106 */
107 public function getConfig() : array
108 {
109 return $this->config;
110 }
111 /**
112 * Sets the names of dynamic parameters.
113 * @return static
114 */
115 public function setDynamicParameterNames(array $names)
116 {
117 \assert($this->extensions[self::Parameters] instanceof Extensions\ParametersExtension);
118 $this->extensions[self::Parameters]->dynamicParams = $names;
119 return $this;
120 }
121 /**
122 * Adds dependencies to the list.
123 * @param array $deps of ReflectionClass|\ReflectionFunctionAbstract|string
124 * @return static
125 */
126 public function addDependencies(array $deps)
127 {
128 $this->dependencies->add(\array_filter($deps));
129 return $this;
130 }
131 /**
132 * Exports dependencies.
133 */
134 public function exportDependencies() : array
135 {
136 return $this->dependencies->export();
137 }
138 /** @return static */
139 public function addExportedTag(string $tag)
140 {
141 if (isset($this->extensions[self::DI])) {
142 \assert($this->extensions[self::DI] instanceof Extensions\DIExtension);
143 $this->extensions[self::DI]->exportedTags[$tag] = \true;
144 }
145 return $this;
146 }
147 /** @return static */
148 public function addExportedType(string $type)
149 {
150 if (isset($this->extensions[self::DI])) {
151 \assert($this->extensions[self::DI] instanceof Extensions\DIExtension);
152 $this->extensions[self::DI]->exportedTypes[$type] = \true;
153 }
154 return $this;
155 }
156 public function compile() : string
157 {
158 $this->processExtensions();
159 $this->processBeforeCompile();
160 return $this->generateCode();
161 }
162 /** @internal */
163 public function processExtensions() : void
164 {
165 $first = $this->getExtensions(Extensions\ParametersExtension::class) + $this->getExtensions(Extensions\ExtensionsExtension::class);
166 foreach ($first as $name => $extension) {
167 $config = $this->processSchema($extension->getConfigSchema(), $this->configs[$name] ?? [], $name);
168 $extension->setConfig($this->config[$name] = $config);
169 $extension->loadConfiguration();
170 }
171 $last = $this->getExtensions(Extensions\InjectExtension::class);
172 $this->extensions = \array_merge(\array_diff_key($this->extensions, $last), $last);
173 if ($decorator = $this->getExtensions(Extensions\DecoratorExtension::class)) {
174 \Packetery\Nette\Utils\Arrays::insertBefore($this->extensions, \key($decorator), $this->getExtensions(Extensions\SearchExtension::class));
175 }
176 $extensions = \array_diff_key($this->extensions, $first, [self::Services => 1]);
177 foreach ($extensions as $name => $extension) {
178 $config = $this->processSchema($extension->getConfigSchema(), $this->configs[$name] ?? [], $name);
179 $extension->setConfig($this->config[$name] = $config);
180 }
181 foreach ($extensions as $extension) {
182 $extension->loadConfiguration();
183 }
184 foreach ($this->getExtensions(Extensions\ServicesExtension::class) as $name => $extension) {
185 $config = $this->processSchema($extension->getConfigSchema(), $this->configs[$name] ?? [], $name);
186 $extension->setConfig($this->config[$name] = $config);
187 $extension->loadConfiguration();
188 }
189 if ($extra = \array_diff_key($this->extensions, $extensions, $first, [self::Services => 1])) {
190 throw new \Packetery\Nette\DeprecatedException(\sprintf("Extensions '%s' were added while container was being compiled.", \implode("', '", \array_keys($extra))));
191 } elseif ($extra = \key(\array_diff_key($this->configs, $this->extensions))) {
192 $hint = \Packetery\Nette\Utils\Helpers::getSuggestion(\array_keys($this->extensions), $extra);
193 throw new InvalidConfigurationException(\sprintf("Found section '%s' in configuration, but corresponding extension is missing", $extra) . ($hint ? ", did you mean '{$hint}'?" : '.'));
194 }
195 }
196 private function processBeforeCompile() : void
197 {
198 $this->builder->resolve();
199 foreach ($this->extensions as $extension) {
200 $extension->beforeCompile();
201 $this->dependencies->add([(new \ReflectionClass($extension))->getFileName()]);
202 }
203 $this->builder->complete();
204 }
205 /**
206 * Merges and validates configurations against scheme.
207 * @return array|object
208 */
209 private function processSchema(Schema\Schema $schema, array $configs, $name = null)
210 {
211 $processor = new Schema\Processor();
212 $processor->onNewContext[] = function (Schema\Context $context) use($name) {
213 $context->path = $name ? [$name] : [];
214 $context->dynamics =& $this->extensions[self::Parameters]->dynamicValidators;
215 };
216 try {
217 $res = $processor->processMultiple($schema, $configs);
218 } catch (Schema\ValidationException $e) {
219 throw new \Packetery\Nette\DI\InvalidConfigurationException($e->getMessage());
220 }
221 foreach ($processor->getWarnings() as $warning) {
222 \trigger_error($warning, \E_USER_DEPRECATED);
223 }
224 return $res;
225 }
226 /** @internal */
227 public function generateCode() : string
228 {
229 $generator = $this->createPhpGenerator();
230 $class = $generator->generate($this->className);
231 $this->dependencies->add($this->builder->getDependencies());
232 foreach ($this->extensions as $extension) {
233 $extension->afterCompile($class);
234 $generator->addInitialization($class, $extension);
235 }
236 return $this->sources . "\n" . $generator->toString($class);
237 }
238 /**
239 * Loads list of service definitions from configuration.
240 */
241 public function loadDefinitionsFromConfig(array $configList) : void
242 {
243 $extension = $this->extensions[self::Services];
244 \assert($extension instanceof Extensions\ServicesExtension);
245 $extension->loadDefinitions($this->processSchema($extension->getConfigSchema(), [$configList]));
246 }
247 protected function createPhpGenerator() : PhpGenerator
248 {
249 return new PhpGenerator($this->builder);
250 }
251 }
252