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

nette / di / 6280908789

23 Sep 2023 01:52AM UTC coverage: 93.239%. Remained the same
6280908789

push

github

dg
typo

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

2165 of 2322 relevant lines covered (93.24%)

0.93 hits per line

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

88.19
/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
                THIS_SERVICE = 'self',
25
                THIS_CONTAINER = 'container';
26

27
        /** @var array */
28
        public $parameters = [];
29

30
        /** @var Definition[] */
31
        private $definitions = [];
32

33
        /** @var array of alias => service */
34
        private $aliases = [];
35

36
        /** @var Autowiring */
37
        private $autowiring;
38

39
        /** @var bool */
40
        private $needsResolve = true;
41

42
        /** @var bool */
43
        private $resolving = false;
44

45
        /** @var array */
46
        private $dependencies = [];
47

48

49
        public function __construct()
50
        {
51
                $this->autowiring = new Autowiring($this);
1✔
52
                $this->addImportedDefinition(self::THIS_CONTAINER)->setType(Container::class);
1✔
53
        }
1✔
54

55

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

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

74
                } else {
75
                        $name = $this->aliases[$name] ?? $name;
1✔
76
                        if (isset($this->definitions[$name])) {
1✔
77
                                throw new Nette\InvalidStateException(sprintf("Service '%s' has already been added.", $name));
×
78
                        }
79

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

92
                $definition = $definition ?: new Definitions\ServiceDefinition;
1✔
93
                $definition->setName($name);
1✔
94
                $definition->setNotifier(function (): void {
1✔
95
                        $this->needsResolve = true;
1✔
96
                });
1✔
97
                return $this->definitions[$name] = $definition;
1✔
98
        }
99

100

101
        public function addAccessorDefinition(?string $name): Definitions\AccessorDefinition
1✔
102
        {
103
                return $this->addDefinition($name, new Definitions\AccessorDefinition);
1✔
104
        }
105

106

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

112

113
        public function addLocatorDefinition(?string $name): Definitions\LocatorDefinition
114
        {
115
                return $this->addDefinition($name, new Definitions\LocatorDefinition);
×
116
        }
117

118

119
        public function addImportedDefinition(?string $name): Definitions\ImportedDefinition
1✔
120
        {
121
                return $this->addDefinition($name, new Definitions\ImportedDefinition);
1✔
122
        }
123

124

125
        /**
126
         * Removes the specified service definition.
127
         */
128
        public function removeDefinition(string $name): void
1✔
129
        {
130
                $this->needsResolve = true;
1✔
131
                $name = $this->aliases[$name] ?? $name;
1✔
132
                unset($this->definitions[$name]);
1✔
133
        }
1✔
134

135

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

146
                return $this->definitions[$service];
1✔
147
        }
148

149

150
        /**
151
         * Gets all service definitions.
152
         * @return Definition[]
153
         */
154
        public function getDefinitions(): array
155
        {
156
                return $this->definitions;
1✔
157
        }
158

159

160
        /**
161
         * Does the service definition or alias exist?
162
         */
163
        public function hasDefinition(string $name): bool
1✔
164
        {
165
                $name = $this->aliases[$name] ?? $name;
1✔
166
                return isset($this->definitions[$name]);
1✔
167
        }
168

169

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

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

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

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

185
                $this->aliases[$alias] = $service;
1✔
186
        }
1✔
187

188

189
        /**
190
         * Removes the specified alias.
191
         */
192
        public function removeAlias(string $alias): void
1✔
193
        {
194
                unset($this->aliases[$alias]);
1✔
195
        }
1✔
196

197

198
        /**
199
         * Gets all service aliases.
200
         */
201
        public function getAliases(): array
202
        {
203
                return $this->aliases;
1✔
204
        }
205

206

207
        /**
208
         * @param  string[]  $types
209
         * @return static
210
         */
211
        public function addExcludedClasses(array $types)
1✔
212
        {
213
                $this->needsResolve = true;
1✔
214
                $this->autowiring->addExcludedClasses($types);
1✔
215
                return $this;
1✔
216
        }
217

218

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

230

231
        /**
232
         * Gets autowired service definition of the specified type.
233
         * @throws MissingServiceException
234
         */
235
        public function getDefinitionByType(string $type): Definition
1✔
236
        {
237
                return $this->getDefinition($this->getByType($type, true));
1✔
238
        }
239

240

241
        /**
242
         * Gets the autowired service names and definitions of the specified type.
243
         * @return Definition[]  service name is key
244
         * @internal
245
         */
246
        public function findAutowired(string $type): array
1✔
247
        {
248
                $this->needResolved();
1✔
249
                return $this->autowiring->findByType($type);
1✔
250
        }
251

252

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

267
                return $found;
1✔
268
        }
269

270

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

284
                return $found;
1✔
285
        }
286

287

288
        /********************* building ****************d*g**/
289

290

291
        /**
292
         * Checks services, resolves types and rebuilts autowiring classlist.
293
         */
294
        public function resolve(): void
295
        {
296
                if ($this->resolving) {
1✔
297
                        return;
×
298
                }
299

300
                $this->resolving = true;
1✔
301

302
                $resolver = new Resolver($this);
1✔
303
                foreach ($this->definitions as $def) {
1✔
304
                        $resolver->resolveDefinition($def);
1✔
305
                }
306

307
                $this->autowiring->rebuild();
1✔
308

309
                $this->resolving = $this->needsResolve = false;
1✔
310
        }
1✔
311

312

313
        private function needResolved(): void
314
        {
315
                if ($this->resolving) {
1✔
316
                        throw new NotAllowedDuringResolvingException;
1✔
317
                } elseif ($this->needsResolve) {
1✔
318
                        $this->resolve();
1✔
319
                }
320
        }
1✔
321

322

323
        public function complete(): void
324
        {
325
                $this->resolve();
1✔
326
                foreach ($this->definitions as $def) {
1✔
327
                        $def->setNotifier(null);
1✔
328
                }
329

330
                $resolver = new Resolver($this);
1✔
331
                foreach ($this->definitions as $def) {
1✔
332
                        $resolver->completeDefinition($def);
1✔
333
                }
334
        }
1✔
335

336

337
        /**
338
         * Adds item to the list of dependencies.
339
         * @param  \ReflectionClass|\ReflectionFunctionAbstract|string  $dep
340
         * @return static
341
         * @internal
342
         */
343
        public function addDependency($dep)
344
        {
345
                $this->dependencies[] = $dep;
1✔
346
                return $this;
1✔
347
        }
348

349

350
        /**
351
         * Returns the list of dependencies.
352
         */
353
        public function getDependencies(): array
354
        {
355
                return $this->dependencies;
1✔
356
        }
357

358

359
        /** @internal */
360
        public function exportMeta(): array
361
        {
362
                $defs = $this->definitions;
1✔
363
                ksort($defs);
1✔
364
                foreach ($defs as $name => $def) {
1✔
365
                        if ($def instanceof Definitions\ImportedDefinition) {
1✔
366
                                $meta['types'][$name] = $def->getType();
1✔
367
                        }
368

369
                        foreach ($def->getTags() as $tag => $value) {
1✔
370
                                $meta['tags'][$tag][$name] = $value;
1✔
371
                        }
372
                }
373

374
                $meta['aliases'] = $this->aliases;
1✔
375
                ksort($meta['aliases']);
1✔
376

377
                $all = [];
1✔
378
                foreach ($this->definitions as $name => $def) {
1✔
379
                        if ($type = $def->getType()) {
1✔
380
                                foreach (class_parents($type) + class_implements($type) + [$type] as $class) {
1✔
381
                                        $all[$class][] = $name;
1✔
382
                                }
383
                        }
384
                }
385

386
                [$low, $high] = $this->autowiring->getClassList();
1✔
387
                foreach ($all as $class => $names) {
1✔
388
                        $meta['wiring'][$class] = array_filter([
1✔
389
                                $high[$class] ?? [],
1✔
390
                                $low[$class] ?? [],
1✔
391
                                array_diff($names, $low[$class] ?? [], $high[$class] ?? []),
1✔
392
                        ]);
393
                }
394

395
                return $meta;
1✔
396
        }
397

398

399
        public static function literal(string $code, ?array $args = null): Nette\PhpGenerator\PhpLiteral
1✔
400
        {
401
                return new Nette\PhpGenerator\PhpLiteral(
1✔
402
                        $args === null ? $code : (new Nette\PhpGenerator\Dumper)->format($code, ...$args)
1✔
403
                );
404
        }
405

406

407
        /** @deprecated */
408
        public function formatPhp(string $statement, array $args): string
409
        {
410
                array_walk_recursive($args, function (&$val): void {
×
411
                        if ($val instanceof Nette\DI\Definitions\Statement) {
×
412
                                $val = (new Resolver($this))->completeStatement($val);
×
413

414
                        } elseif ($val instanceof Definition) {
×
415
                                $val = new Definitions\Reference($val->getName());
×
416
                        }
417
                });
×
418
                return (new PhpGenerator($this))->formatPhp($statement, $args);
×
419
        }
420

421

422
        /** @deprecated use resolve() */
423
        public function prepareClassList(): void
424
        {
425
                trigger_error(__METHOD__ . '() is deprecated, use resolve()', E_USER_DEPRECATED);
×
426
                $this->resolve();
×
427
        }
428
}
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