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

nette / di / 4405606637

pending completion
4405606637

push

github

David Grudl
more self explanatory message for factory and service mismatch (closes #199) (#284)

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

2118 of 2238 relevant lines covered (94.64%)

0.95 hits per line

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

98.68
/src/DI/Extensions/InjectExtension.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\Extensions;
11

12
use Nette;
13
use Nette\DI;
14
use Nette\DI\Definitions;
15
use Nette\Utils\Reflection;
16

17

18
/**
19
 * Calls inject methods and fills @inject properties.
20
 */
21
final class InjectExtension extends DI\CompilerExtension
22
{
23
        public const TagInject = 'nette.inject';
24

25
        /** @deprecated use InjectExtension::TagInject */
26
        public const TAG_INJECT = self::TagInject;
27

28

29
        public function getConfigSchema(): Nette\Schema\Schema
30
        {
31
                return Nette\Schema\Expect::structure([]);
1✔
32
        }
33

34

35
        public function beforeCompile()
36
        {
37
                foreach ($this->getContainerBuilder()->getDefinitions() as $def) {
1✔
38
                        if ($def->getTag(self::TagInject)) {
1✔
39
                                $def = $def instanceof Definitions\FactoryDefinition
1✔
40
                                        ? $def->getResultDefinition()
1✔
41
                                        : $def;
1✔
42
                                if ($def instanceof Definitions\ServiceDefinition) {
1✔
43
                                        $this->updateDefinition($def);
1✔
44
                                }
45
                        }
46
                }
47
        }
1✔
48

49

50
        private function updateDefinition(Definitions\ServiceDefinition $def): void
1✔
51
        {
52
                $resolvedType = (new DI\Resolver($this->getContainerBuilder()))->resolveEntityType($def->getCreator());
1✔
53
                $class = is_subclass_of($resolvedType, $def->getType())
1✔
54
                        ? $resolvedType
1✔
55
                        : $def->getType();
1✔
56
                $setups = $def->getSetup();
1✔
57

58
                foreach (self::getInjectProperties($class) as $property => $type) {
1✔
59
                        $builder = $this->getContainerBuilder();
1✔
60
                        $inject = new Definitions\Statement('$' . $property, [Definitions\Reference::fromType((string) $type)]);
1✔
61
                        foreach ($setups as $key => $setup) {
1✔
62
                                if ($setup->getEntity() === $inject->getEntity()) {
1✔
63
                                        $inject = $setup;
1✔
64
                                        $builder = null;
1✔
65
                                        unset($setups[$key]);
1✔
66
                                }
67
                        }
68

69
                        self::checkType($class, $property, $type, $builder, $def);
1✔
70
                        array_unshift($setups, $inject);
1✔
71
                }
72

73
                foreach (array_reverse(self::getInjectMethods($class)) as $method) {
1✔
74
                        $inject = new Definitions\Statement($method);
1✔
75
                        foreach ($setups as $key => $setup) {
1✔
76
                                if ($setup->getEntity() === $inject->getEntity()) {
1✔
77
                                        $inject = $setup;
1✔
78
                                        unset($setups[$key]);
1✔
79
                                }
80
                        }
81

82
                        array_unshift($setups, $inject);
1✔
83
                }
84

85
                $def->setSetup($setups);
1✔
86
        }
1✔
87

88

89
        /**
90
         * Generates list of inject methods.
91
         * @internal
92
         */
93
        public static function getInjectMethods(string $class): array
1✔
94
        {
95
                $classes = [];
1✔
96
                foreach (get_class_methods($class) as $name) {
1✔
97
                        if (str_starts_with($name, 'inject')) {
1✔
98
                                $classes[$name] = (new \ReflectionMethod($class, $name))->getDeclaringClass()->name;
1✔
99
                        }
100
                }
101

102
                $methods = array_keys($classes);
1✔
103
                uksort($classes, fn(string $a, string $b): int => $classes[$a] === $classes[$b]
1✔
104
                                ? array_search($a, $methods, true) <=> array_search($b, $methods, true)
1✔
105
                                : (is_a($classes[$a], $classes[$b], true) ? 1 : -1));
1✔
106
                return array_keys($classes);
1✔
107
        }
108

109

110
        /**
111
         * Generates list of properties with annotation @inject.
112
         * @internal
113
         */
114
        public static function getInjectProperties(string $class): array
1✔
115
        {
116
                $res = [];
1✔
117
                foreach ((new \ReflectionClass($class))->getProperties() as $rp) {
1✔
118
                        $name = $rp->getName();
1✔
119
                        $hasAttr = $rp->getAttributes(DI\Attributes\Inject::class);
1✔
120
                        if ($hasAttr || DI\Helpers::parseAnnotation($rp, 'inject') !== null) {
1✔
121
                                if (!$rp->isPublic() || $rp->isStatic()) {
1✔
122
                                        trigger_error(sprintf('Property %s for injection must be public and non-static.', Reflection::toString($rp)), E_USER_WARNING);
1✔
123
                                        continue;
1✔
124
                                }
125

126
                                if (PHP_VERSION_ID >= 80100 && $rp->isReadOnly()) {
1✔
127
                                        throw new Nette\InvalidStateException(sprintf('Property %s for injection must not be readonly.', Reflection::toString($rp)));
×
128
                                }
129

130
                                $type = Nette\Utils\Type::fromReflection($rp);
1✔
131
                                if (!$type && !$hasAttr && ($annotation = DI\Helpers::parseAnnotation($rp, 'var'))) {
1✔
132
                                        $annotation = Reflection::expandClassName($annotation, Reflection::getPropertyDeclaringClass($rp));
1✔
133
                                        $type = Nette\Utils\Type::fromString($annotation);
1✔
134
                                }
135

136
                                $res[$rp->getName()] = DI\Helpers::ensureClassType($type, 'type of property ' . Reflection::toString($rp));
1✔
137
                        }
138
                }
139

140
                ksort($res);
1✔
141
                return $res;
1✔
142
        }
143

144

145
        /**
146
         * Calls all methods starting with with "inject" using autowiring.
147
         */
148
        public static function callInjects(DI\Container $container, object $service): void
1✔
149
        {
150
                foreach (self::getInjectMethods($service::class) as $method) {
1✔
151
                        $container->callMethod([$service, $method]);
1✔
152
                }
153

154
                foreach (self::getInjectProperties($service::class) as $property => $type) {
1✔
155
                        self::checkType($service, $property, $type, $container);
1✔
156
                        $service->$property = $container->getByType($type);
1✔
157
                }
158
        }
1✔
159

160

161
        private static function checkType(
1✔
162
                object|string $class,
163
                string $name,
164
                ?string $type,
165
                DI\Container|DI\ContainerBuilder|null $container,
166
                Definitions\Definition $def = null,
167
        ): void
168
        {
169
                if ($container && !$container->getByType($type, false)) {
1✔
170
                        throw new Nette\DI\MissingServiceException(sprintf(
1✔
171
                                "%sService of type %s required by %s not found.\nDid you add it to configuration file?",
1✔
172
                                $def ? '[' . $def->getDescriptor() . "]\n" : '',
1✔
173
                                $type,
174
                                Reflection::toString(new \ReflectionProperty($class, $name)),
1✔
175
                        ));
176
                }
177
        }
1✔
178
}
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