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

nette / di / 4405560890

pending completion
4405560890

push

github

David Grudl
fixed exception messages

3 of 3 new or added lines in 1 file covered. (100.0%)

2182 of 2317 relevant lines covered (94.17%)

0.94 hits per line

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

89.44
/src/DI/ContainerBuilder.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\DI\Definitions\Definition;
14

15

16
/**
17
 * Container builder.
18
 */
19
class ContainerBuilder
20
{
21
        use Nette\SmartObject;
22

23
        public const
24
                ThisService = 'self',
25
                ThisContainer = 'container';
26

27
        /** @deprecated use ContainerBuilder::ThisService */
28
        public const THIS_SERVICE = self::ThisService;
29

30
        /** @deprecated use ContainerBuilder::ThisContainer */
31
        public const THIS_CONTAINER = self::ThisContainer;
32

33
        /** @var array */
34
        public $parameters = [];
35

36
        /** @var Definition[] */
37
        private $definitions = [];
38

39
        /** @var array of alias => service */
40
        private $aliases = [];
41

42
        /** @var Autowiring */
43
        private $autowiring;
44

45
        /** @var bool */
46
        private $needsResolve = true;
47

48
        /** @var bool */
49
        private $resolving = false;
50

51
        /** @var array */
52
        private $dependencies = [];
53

54

55
        public function __construct()
56
        {
57
                $this->autowiring = new Autowiring($this);
1✔
58
                $this->addImportedDefinition(self::ThisContainer)->setType(Container::class);
1✔
59
        }
1✔
60

61

62
        /**
63
         * Adds new service definition.
64
         * @return Definitions\ServiceDefinition
65
         */
66
        public function addDefinition(?string $name, ?Definition $definition = null): Definition
1✔
67
        {
68
                $this->needsResolve = true;
1✔
69
                if ($name === null) {
1✔
70
                        for (
71
                                $i = 1;
1✔
72
                                isset($this->definitions['0' . $i]) || isset($this->aliases['0' . $i]);
1✔
73
                                $i++
1✔
74
                        );
75
                        $name = '0' . $i; // prevents converting to integer in array key
1✔
76

77
                } elseif (is_int(key([$name => 1])) || !preg_match('#^\w+(\.\w+)*$#D', $name)) {
1✔
78
                        throw new Nette\InvalidArgumentException(sprintf("Service name must be a alpha-numeric string and not a number, '%s' given.", $name));
1✔
79

80
                } else {
81
                        $name = $this->aliases[$name] ?? $name;
1✔
82
                        if (isset($this->definitions[$name])) {
1✔
83
                                throw new Nette\InvalidStateException(sprintf("Service '%s' has already been added.", $name));
×
84
                        }
85

86
                        $lname = strtolower($name);
1✔
87
                        foreach ($this->definitions as $nm => $foo) {
1✔
88
                                if ($lname === strtolower($nm)) {
1✔
89
                                        throw new Nette\InvalidStateException(sprintf(
1✔
90
                                                "Service '%s' has the same name as '%s' in a case-insensitive manner.",
1✔
91
                                                $name,
92
                                                $nm
93
                                        ));
94
                                }
95
                        }
96
                }
97

98
                $definition = $definition ?: new Definitions\ServiceDefinition;
1✔
99
                $definition->setName($name);
1✔
100
                $definition->setNotifier(function (): void {
1✔
101
                        $this->needsResolve = true;
1✔
102
                });
1✔
103
                return $this->definitions[$name] = $definition;
1✔
104
        }
105

106

107
        public function addAccessorDefinition(?string $name): Definitions\AccessorDefinition
1✔
108
        {
109
                return $this->addDefinition($name, new Definitions\AccessorDefinition);
1✔
110
        }
111

112

113
        public function addFactoryDefinition(?string $name): Definitions\FactoryDefinition
1✔
114
        {
115
                return $this->addDefinition($name, new Definitions\FactoryDefinition);
1✔
116
        }
117

118

119
        public function addLocatorDefinition(?string $name): Definitions\LocatorDefinition
120
        {
121
                return $this->addDefinition($name, new Definitions\LocatorDefinition);
×
122
        }
123

124

125
        public function addImportedDefinition(?string $name): Definitions\ImportedDefinition
1✔
126
        {
127
                return $this->addDefinition($name, new Definitions\ImportedDefinition);
1✔
128
        }
129

130

131
        /**
132
         * Removes the specified service definition.
133
         */
134
        public function removeDefinition(string $name): void
1✔
135
        {
136
                $this->needsResolve = true;
1✔
137
                $name = $this->aliases[$name] ?? $name;
1✔
138
                unset($this->definitions[$name]);
1✔
139
        }
1✔
140

141

142
        /**
143
         * Gets the service definition.
144
         */
145
        public function getDefinition(string $name): Definition
1✔
146
        {
147
                $service = $this->aliases[$name] ?? $name;
1✔
148
                if (!isset($this->definitions[$service])) {
1✔
149
                        throw new MissingServiceException(sprintf("Service '%s' not found.", $name));
×
150
                }
151

152
                return $this->definitions[$service];
1✔
153
        }
154

155

156
        /**
157
         * Gets all service definitions.
158
         * @return Definition[]
159
         */
160
        public function getDefinitions(): array
161
        {
162
                return $this->definitions;
1✔
163
        }
164

165

166
        /**
167
         * Does the service definition or alias exist?
168
         */
169
        public function hasDefinition(string $name): bool
1✔
170
        {
171
                $name = $this->aliases[$name] ?? $name;
1✔
172
                return isset($this->definitions[$name]);
1✔
173
        }
174

175

176
        public function addAlias(string $alias, string $service): void
1✔
177
        {
178
                if (!$alias) { // builder is not ready for falsy names such as '0'
1✔
179
                        throw new Nette\InvalidArgumentException(sprintf("Alias name must be a non-empty string, '%s' given.", $alias));
×
180

181
                } elseif (!$service) { // builder is not ready for falsy names such as '0'
1✔
182
                        throw new Nette\InvalidArgumentException(sprintf("Service name must be a non-empty string, '%s' given.", $service));
×
183

184
                } elseif (isset($this->aliases[$alias])) {
1✔
185
                        throw new Nette\InvalidStateException(sprintf("Alias '%s' has already been added.", $alias));
×
186

187
                } elseif (isset($this->definitions[$alias])) {
1✔
188
                        throw new Nette\InvalidStateException(sprintf("Service '%s' has already been added.", $alias));
×
189
                }
190

191
                $this->aliases[$alias] = $service;
1✔
192
        }
1✔
193

194

195
        /**
196
         * Removes the specified alias.
197
         */
198
        public function removeAlias(string $alias): void
1✔
199
        {
200
                unset($this->aliases[$alias]);
1✔
201
        }
1✔
202

203

204
        /**
205
         * Gets all service aliases.
206
         */
207
        public function getAliases(): array
208
        {
209
                return $this->aliases;
1✔
210
        }
211

212

213
        /**
214
         * @param  string[]  $types
215
         * @return static
216
         */
217
        public function addExcludedClasses(array $types)
1✔
218
        {
219
                $this->needsResolve = true;
1✔
220
                $this->autowiring->addExcludedClasses($types);
1✔
221
                return $this;
1✔
222
        }
223

224

225
        /**
226
         * Resolves autowired service name by type.
227
         * @param  bool  $throw exception if service doesn't exist?
228
         * @throws MissingServiceException
229
         */
230
        public function getByType(string $type, bool $throw = false): ?string
1✔
231
        {
232
                $this->needResolved();
1✔
233
                return $this->autowiring->getByType($type, $throw);
1✔
234
        }
235

236

237
        /**
238
         * Gets autowired service definition of the specified type.
239
         * @throws MissingServiceException
240
         */
241
        public function getDefinitionByType(string $type): Definition
1✔
242
        {
243
                return $this->getDefinition($this->getByType($type, true));
1✔
244
        }
245

246

247
        /**
248
         * Gets the autowired service names and definitions of the specified type.
249
         * @return Definition[]  service name is key
250
         * @internal
251
         */
252
        public function findAutowired(string $type): array
1✔
253
        {
254
                $this->needResolved();
1✔
255
                return $this->autowiring->findByType($type);
1✔
256
        }
257

258

259
        /**
260
         * Gets the service names and definitions of the specified type.
261
         * @return Definition[]  service name is key
262
         */
263
        public function findByType(string $type): array
1✔
264
        {
265
                $this->needResolved();
1✔
266
                $found = [];
1✔
267
                foreach ($this->definitions as $name => $def) {
1✔
268
                        if (is_a($def->getType(), $type, true)) {
1✔
269
                                $found[$name] = $def;
1✔
270
                        }
271
                }
272

273
                return $found;
1✔
274
        }
275

276

277
        /**
278
         * Gets the service names and tag values.
279
         * @return array of [service name => tag attributes]
280
         */
281
        public function findByTag(string $tag): array
1✔
282
        {
283
                $found = [];
1✔
284
                foreach ($this->definitions as $name => $def) {
1✔
285
                        if (($tmp = $def->getTag($tag)) !== null) {
1✔
286
                                $found[$name] = $tmp;
1✔
287
                        }
288
                }
289

290
                return $found;
1✔
291
        }
292

293

294
        /********************* building ****************d*g**/
295

296

297
        /**
298
         * Checks services, resolves types and rebuilts autowiring classlist.
299
         */
300
        public function resolve(): void
301
        {
302
                if ($this->resolving) {
1✔
303
                        return;
×
304
                }
305

306
                $this->resolving = true;
1✔
307

308
                $resolver = new Resolver($this);
1✔
309
                foreach ($this->definitions as $def) {
1✔
310
                        $resolver->resolveDefinition($def);
1✔
311
                }
312

313
                $this->autowiring->rebuild();
1✔
314

315
                $this->resolving = $this->needsResolve = false;
1✔
316
        }
1✔
317

318

319
        private function needResolved(): void
320
        {
321
                if ($this->resolving) {
1✔
322
                        throw new NotAllowedDuringResolvingException;
1✔
323
                } elseif ($this->needsResolve) {
1✔
324
                        $this->resolve();
1✔
325
                }
326
        }
1✔
327

328

329
        public function complete(): void
330
        {
331
                $this->resolve();
1✔
332
                foreach ($this->definitions as $def) {
1✔
333
                        $def->setNotifier(null);
1✔
334
                }
335

336
                $resolver = new Resolver($this);
1✔
337
                foreach ($this->definitions as $def) {
1✔
338
                        $resolver->completeDefinition($def);
1✔
339
                }
340
        }
1✔
341

342

343
        /**
344
         * Adds item to the list of dependencies.
345
         * @param  \ReflectionClass|\ReflectionFunctionAbstract|string  $dep
346
         * @return static
347
         * @internal
348
         */
349
        public function addDependency($dep)
350
        {
351
                $this->dependencies[] = $dep;
1✔
352
                return $this;
1✔
353
        }
354

355

356
        /**
357
         * Returns the list of dependencies.
358
         */
359
        public function getDependencies(): array
360
        {
361
                return $this->dependencies;
1✔
362
        }
363

364

365
        /** @internal */
366
        public function exportMeta(): array
367
        {
368
                $defs = $this->definitions;
1✔
369
                ksort($defs);
1✔
370
                foreach ($defs as $name => $def) {
1✔
371
                        if ($def instanceof Definitions\ImportedDefinition) {
1✔
372
                                $meta['types'][$name] = $def->getType();
1✔
373
                        }
374

375
                        foreach ($def->getTags() as $tag => $value) {
1✔
376
                                $meta['tags'][$tag][$name] = $value;
1✔
377
                        }
378
                }
379

380
                $meta['aliases'] = $this->aliases;
1✔
381
                ksort($meta['aliases']);
1✔
382

383
                $all = [];
1✔
384
                foreach ($this->definitions as $name => $def) {
1✔
385
                        if ($type = $def->getType()) {
1✔
386
                                foreach (class_parents($type) + class_implements($type) + [$type] as $class) {
1✔
387
                                        $all[$class][] = $name;
1✔
388
                                }
389
                        }
390
                }
391

392
                [$low, $high] = $this->autowiring->getClassList();
1✔
393
                foreach ($all as $class => $names) {
1✔
394
                        $meta['wiring'][$class] = array_filter([
1✔
395
                                $high[$class] ?? [],
1✔
396
                                $low[$class] ?? [],
1✔
397
                                array_diff($names, $low[$class] ?? [], $high[$class] ?? []),
1✔
398
                        ]);
399
                }
400

401
                return $meta;
1✔
402
        }
403

404

405
        public static function literal(string $code, ?array $args = null): Nette\PhpGenerator\PhpLiteral
1✔
406
        {
407
                return new Nette\PhpGenerator\PhpLiteral(
1✔
408
                        $args === null ? $code : (new Nette\PhpGenerator\Dumper)->format($code, ...$args)
1✔
409
                );
410
        }
411

412

413
        /** @deprecated */
414
        public function formatPhp(string $statement, array $args): string
415
        {
416
                array_walk_recursive($args, function (&$val): void {
×
417
                        if ($val instanceof Statement) {
×
418
                                $val = (new Resolver($this))->completeStatement($val);
×
419

420
                        } elseif ($val instanceof Definition) {
×
421
                                $val = new Definitions\Reference($val->getName());
×
422
                        }
423
                });
×
424
                return (new PhpGenerator($this))->formatPhp($statement, $args);
×
425
        }
426
}
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