1: <?php
2:
3: 4: 5: 6:
7:
8: namespace Nette\DI\Extensions;
9:
10: use Nette;
11: use Nette\DI;
12: use Nette\Utils\Reflection;
13:
14:
15: 16: 17:
18: class InjectExtension extends DI\CompilerExtension
19: {
20: const TAG_INJECT = 'inject';
21:
22:
23: public function beforeCompile()
24: {
25: foreach ($this->getContainerBuilder()->getDefinitions() as $def) {
26: if ($def->getTag(self::TAG_INJECT) && $def->getType()) {
27: $this->updateDefinition($def);
28: }
29: }
30: }
31:
32:
33: private function updateDefinition(DI\ServiceDefinition $def)
34: {
35: $class = $def->getType();
36: $setups = $def->getSetup();
37:
38: foreach (self::getInjectProperties($class) as $property => $type) {
39: $builder = $this->getContainerBuilder();
40: $inject = new DI\Statement('$' . $property, ['@\\' . ltrim($type, '\\')]);
41: foreach ($setups as $key => $setup) {
42: if ($setup->getEntity() === $inject->getEntity()) {
43: $inject = $setup;
44: $builder = null;
45: unset($setups[$key]);
46: }
47: }
48: self::checkType($class, $property, $type, $builder);
49: array_unshift($setups, $inject);
50: }
51:
52: foreach (array_reverse(self::getInjectMethods($def->getType())) as $method) {
53: $inject = new DI\Statement($method);
54: foreach ($setups as $key => $setup) {
55: if ($setup->getEntity() === $inject->getEntity()) {
56: $inject = $setup;
57: unset($setups[$key]);
58: }
59: }
60: array_unshift($setups, $inject);
61: }
62:
63: $def->setSetup($setups);
64: }
65:
66:
67: 68: 69: 70: 71:
72: public static function getInjectMethods($class)
73: {
74: $res = [];
75: foreach (get_class_methods($class) as $name) {
76: if (substr($name, 0, 6) === 'inject') {
77: $res[$name] = (new \ReflectionMethod($class, $name))->getDeclaringClass()->getName();
78: }
79: }
80: uksort($res, function ($a, $b) use ($res) {
81: return $res[$a] === $res[$b]
82: ? strcmp($a, $b)
83: : (is_a($res[$a], $res[$b], true) ? 1 : -1);
84: });
85: return array_keys($res);
86: }
87:
88:
89: 90: 91: 92: 93:
94: public static function getInjectProperties($class)
95: {
96: $res = [];
97: foreach (get_class_vars($class) as $name => $foo) {
98: $rp = new \ReflectionProperty($class, $name);
99: if (DI\Helpers::parseAnnotation($rp, 'inject') !== null) {
100: if ($type = DI\Helpers::parseAnnotation($rp, 'var')) {
101: $type = Reflection::expandClassName($type, Reflection::getPropertyDeclaringClass($rp));
102: }
103: $res[$name] = $type;
104: }
105: }
106: ksort($res);
107: return $res;
108: }
109:
110:
111: 112: 113: 114:
115: public static function callInjects(DI\Container $container, $service)
116: {
117: if (!is_object($service)) {
118: throw new Nette\InvalidArgumentException(sprintf('Service must be object, %s given.', gettype($service)));
119: }
120:
121: foreach (self::getInjectMethods($service) as $method) {
122: $container->callMethod([$service, $method]);
123: }
124:
125: foreach (self::getInjectProperties(get_class($service)) as $property => $type) {
126: self::checkType($service, $property, $type, $container);
127: $service->$property = $container->getByType($type);
128: }
129: }
130:
131:
132:
133: private static function checkType($class, $name, $type, $container = null)
134: {
135: $propName = Reflection::toString(new \ReflectionProperty($class, $name));
136: if (!$type) {
137: throw new Nette\InvalidStateException("Property $propName has no @var annotation.");
138: } elseif (!class_exists($type) && !interface_exists($type)) {
139: throw new Nette\InvalidStateException("Class or interface '$type' used in @var annotation at $propName not found. Check annotation and 'use' statements.");
140: } elseif ($container && !$container->getByType($type, false)) {
141: throw new Nette\InvalidStateException("Service of type $type used in @var annotation at $propName not found. Did you register it in configuration file?");
142: }
143: }
144: }
145: