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