• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nette / di / 6739035766

02 Nov 2023 10:44PM UTC coverage: 52.037% (-41.8%) from 93.846%
6739035766

push

github

dg
x

1009 of 1939 relevant lines covered (52.04%)

0.52 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

71.17
/src/DI/Compiler.php
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

8
declare(strict_types=1);
9

10
namespace Nette\DI;
11

12
use Nette;
13
use Nette\Schema;
14

15

16
/**
17
 * DI container compiler.
18
 */
19
class Compiler
20
{
21
        use Nette\SmartObject;
22

23
        private const
24
                Services = 'services',
25
                Parameters = 'parameters',
26
                DI = 'di';
27

28
        /** @var CompilerExtension[] */
29
        private $extensions = [];
30

31
        /** @var ContainerBuilder */
32
        private $builder;
33

34
        /** @var array */
35
        private $config = [];
36

37
        /** @var array [section => array[]] */
38
        private $configs = [];
39

40
        /** @var string */
41
        private $sources = '';
42

43
        /** @var DependencyChecker */
44
        private $dependencies;
45

46
        /** @var string */
47
        private $className = 'Container';
48

49

50
        public function __construct(?ContainerBuilder $builder = null)
1✔
51
        {
52
                $this->builder = $builder ?: new ContainerBuilder;
1✔
53
                $this->dependencies = new DependencyChecker;
1✔
54
                $this->addExtension(self::Services, new Extensions\ServicesExtension);
1✔
55
                $this->addExtension(self::Parameters, new Extensions\ParametersExtension($this->configs));
1✔
56
        }
1✔
57

58

59
        /**
60
         * Add custom configurator extension.
61
         * @return static
62
         */
63
        public function addExtension(?string $name, CompilerExtension $extension)
1✔
64
        {
65
                if ($name === null) {
1✔
66
                        $name = '_' . count($this->extensions);
×
67
                } elseif (isset($this->extensions[$name])) {
1✔
68
                        throw new Nette\InvalidArgumentException(sprintf("Name '%s' is already used or reserved.", $name));
×
69
                }
70

71
                $lname = strtolower($name);
1✔
72
                foreach (array_keys($this->extensions) as $nm) {
1✔
73
                        if ($lname === strtolower((string) $nm)) {
1✔
74
                                throw new Nette\InvalidArgumentException(sprintf(
×
75
                                        "Name of extension '%s' has the same name as '%s' in a case-insensitive manner.",
×
76
                                        $name,
77
                                        $nm
78
                                ));
79
                        }
80
                }
81

82
                $this->extensions[$name] = $extension->setCompiler($this, $name);
1✔
83
                return $this;
1✔
84
        }
85

86

87
        public function getExtensions(?string $type = null): array
1✔
88
        {
89
                return $type
1✔
90
                        ? array_filter($this->extensions, function ($item) use ($type): bool { return $item instanceof $type; })
1✔
91
                        : $this->extensions;
1✔
92
        }
93

94

95
        public function getContainerBuilder(): ContainerBuilder
96
        {
97
                return $this->builder;
1✔
98
        }
99

100

101
        /** @return static */
102
        public function setClassName(string $className)
1✔
103
        {
104
                $this->className = $className;
1✔
105
                return $this;
1✔
106
        }
107

108

109
        /**
110
         * Adds new configuration.
111
         * @return static
112
         */
113
        public function addConfig(array $config)
1✔
114
        {
115
                foreach ($config as $section => $data) {
1✔
116
                        $this->configs[$section][] = $data;
1✔
117
                }
118

119
                $this->sources .= "// source: array\n";
1✔
120
                return $this;
1✔
121
        }
122

123

124
        /**
125
         * Adds new configuration from file.
126
         * @return static
127
         */
128
        public function loadConfig(string $file, ?Config\Loader $loader = null)
129
        {
130
                $sources = $this->sources . "// source: $file\n";
×
131
                $loader = $loader ?: new Config\Loader;
×
132
                foreach ($loader->load($file, false) as $data) {
×
133
                        $this->addConfig($data);
×
134
                }
135

136
                $this->dependencies->add($loader->getDependencies());
×
137
                $this->sources = $sources;
×
138
                return $this;
×
139
        }
140

141

142
        /**
143
         * Returns configuration.
144
         * @deprecated
145
         */
146
        public function getConfig(): array
147
        {
148
                return $this->config;
×
149
        }
150

151

152
        /**
153
         * Sets the names of dynamic parameters.
154
         * @return static
155
         */
156
        public function setDynamicParameterNames(array $names)
1✔
157
        {
158
                assert($this->extensions[self::Parameters] instanceof Extensions\ParametersExtension);
159
                $this->extensions[self::Parameters]->dynamicParams = $names;
1✔
160
                return $this;
1✔
161
        }
162

163

164
        /**
165
         * Adds dependencies to the list.
166
         * @param  array  $deps  of ReflectionClass|\ReflectionFunctionAbstract|string
167
         * @return static
168
         */
169
        public function addDependencies(array $deps)
170
        {
171
                $this->dependencies->add(array_filter($deps));
×
172
                return $this;
×
173
        }
174

175

176
        /**
177
         * Exports dependencies.
178
         */
179
        public function exportDependencies(): array
180
        {
181
                return $this->dependencies->export();
×
182
        }
183

184

185
        /** @return static */
186
        public function addExportedTag(string $tag)
187
        {
188
                if (isset($this->extensions[self::DI])) {
×
189
                        assert($this->extensions[self::DI] instanceof Extensions\DIExtension);
190
                        $this->extensions[self::DI]->exportedTags[$tag] = true;
×
191
                }
192

193
                return $this;
×
194
        }
195

196

197
        /** @return static */
198
        public function addExportedType(string $type)
199
        {
200
                if (isset($this->extensions[self::DI])) {
×
201
                        assert($this->extensions[self::DI] instanceof Extensions\DIExtension);
202
                        $this->extensions[self::DI]->exportedTypes[$type] = true;
×
203
                }
204

205
                return $this;
×
206
        }
207

208

209
        public function compile(): string
210
        {
211
                $this->processExtensions();
1✔
212
                $this->processBeforeCompile();
1✔
213
                return $this->generateCode();
1✔
214
        }
215

216

217
        /** @internal */
218
        public function processExtensions(): void
219
        {
220
                $first = $this->getExtensions(Extensions\ParametersExtension::class) + $this->getExtensions(Extensions\ExtensionsExtension::class);
1✔
221
                foreach ($first as $name => $extension) {
1✔
222
                        $config = $this->processSchema($extension->getConfigSchema(), $this->configs[$name] ?? [], $name);
1✔
223
                        $extension->setConfig($this->config[$name] = $config);
1✔
224
                        $extension->loadConfiguration();
1✔
225
                }
226

227
                $last = $this->getExtensions(Extensions\InjectExtension::class);
1✔
228
                $this->extensions = array_merge(array_diff_key($this->extensions, $last), $last);
1✔
229

230
                if ($decorator = $this->getExtensions(Extensions\DecoratorExtension::class)) {
1✔
231
                        Nette\Utils\Arrays::insertBefore($this->extensions, key($decorator), $this->getExtensions(Extensions\SearchExtension::class));
×
232
                }
233

234
                $extensions = array_diff_key($this->extensions, $first, [self::Services => 1]);
1✔
235
                foreach ($extensions as $name => $extension) {
1✔
236
                        $config = $this->processSchema($extension->getConfigSchema(), $this->configs[$name] ?? [], $name);
1✔
237
                        $extension->setConfig($this->config[$name] = $config);
1✔
238
                }
239

240
                foreach ($extensions as $extension) {
1✔
241
                        $extension->loadConfiguration();
1✔
242
                }
243

244
                foreach ($this->getExtensions(Extensions\ServicesExtension::class) as $name => $extension) {
1✔
245
                        $config = $this->processSchema($extension->getConfigSchema(), $this->configs[$name] ?? [], $name);
1✔
246
                        $extension->setConfig($this->config[$name] = $config);
1✔
247
                        $extension->loadConfiguration();
1✔
248
                }
249

250
                if ($extra = array_diff_key($this->extensions, $extensions, $first, [self::Services => 1])) {
1✔
251
                        throw new Nette\DeprecatedException(sprintf(
×
252
                                "Extensions '%s' were added while container was being compiled.",
×
253
                                implode("', '", array_keys($extra))
×
254
                        ));
255

256
                } elseif ($extra = key(array_diff_key($this->configs, $this->extensions))) {
1✔
257
                        $hint = Nette\Utils\Helpers::getSuggestion(array_keys($this->extensions), $extra);
×
258
                        throw new InvalidConfigurationException(
×
259
                                sprintf("Found section '%s' in configuration, but corresponding extension is missing", $extra)
×
260
                                . ($hint ? ", did you mean '$hint'?" : '.')
×
261
                        );
262
                }
263
        }
1✔
264

265

266
        private function processBeforeCompile(): void
267
        {
268
                $this->builder->resolve();
1✔
269

270
                foreach ($this->extensions as $extension) {
1✔
271
                        $extension->beforeCompile();
1✔
272
                        $this->dependencies->add([(new \ReflectionClass($extension))->getFileName()]);
1✔
273
                }
274

275
                $this->builder->complete();
1✔
276
        }
1✔
277

278

279
        /**
280
         * Merges and validates configurations against scheme.
281
         * @return array|object
282
         */
283
        private function processSchema(Schema\Schema $schema, array $configs, $name = null)
1✔
284
        {
285
                $processor = new Schema\Processor;
1✔
286
                $processor->onNewContext[] = function (Schema\Context $context) use ($name) {
1✔
287
                        $context->path = $name ? [$name] : [];
1✔
288
                        $context->dynamics = &$this->extensions[self::Parameters]->dynamicValidators;
1✔
289
                };
1✔
290
                try {
291
                        $res = $processor->processMultiple($schema, $configs);
1✔
292
                } catch (Schema\ValidationException $e) {
1✔
293
                        throw new Nette\DI\InvalidConfigurationException($e->getMessage());
1✔
294
                }
295

296
                foreach ($processor->getWarnings() as $warning) {
1✔
297
                        trigger_error($warning, E_USER_DEPRECATED);
×
298
                }
299

300
                return $res;
1✔
301
        }
302

303

304
        /** @internal */
305
        public function generateCode(): string
306
        {
307
                $generator = $this->createPhpGenerator();
1✔
308
                $class = $generator->generate($this->className);
1✔
309
                $this->dependencies->add($this->builder->getDependencies());
1✔
310

311
                foreach ($this->extensions as $extension) {
1✔
312
                        $extension->afterCompile($class);
1✔
313
                        $generator->addInitialization($class, $extension);
1✔
314
                }
315

316
                return $this->sources . "\n" . $generator->toString($class);
1✔
317
        }
318

319

320
        /**
321
         * Loads list of service definitions from configuration.
322
         */
323
        public function loadDefinitionsFromConfig(array $configList): void
324
        {
325
                $extension = $this->extensions[self::Services];
×
326
                assert($extension instanceof Extensions\ServicesExtension);
327
                $extension->loadDefinitions($this->processSchema($extension->getConfigSchema(), [$configList]));
×
328
        }
329

330

331
        protected function createPhpGenerator(): PhpGenerator
332
        {
333
                return new PhpGenerator($this->builder);
1✔
334
        }
335
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc