1: <?php
2:
3: 4: 5: 6: 7: 8: 9: 10:
11:
12: namespace Nette\DI;
13:
14: use Nette;
15:
16:
17: 18: 19: 20: 21:
22: class Container extends Nette\FreezableObject implements IContainer
23: {
24: const TAGS = 'tags';
25:
26:
27: public $parameters = array();
28:
29:
30: public $params = array();
31:
32:
33: public $classes = array();
34:
35:
36: private $registry = array();
37:
38:
39: private $factories = array();
40:
41:
42: public $meta = array();
43:
44:
45: private $creating;
46:
47:
48: public function __construct(array $params = array())
49: {
50: $this->parameters = $params + $this->parameters;
51: $this->params = &$this->parameters;
52: }
53:
54:
55: 56: 57:
58: public function getParameters()
59: {
60: return $this->parameters;
61: }
62:
63:
64: 65: 66: 67: 68: 69: 70:
71: public function addService($name, $service, array $meta = NULL)
72: {
73: $this->updating();
74: if (!is_string($name) || !$name) {
75: throw new Nette\InvalidArgumentException("Service name must be a non-empty string, " . gettype($name) . " given.");
76: }
77:
78: if (isset($this->registry[$name])) {
79: throw new Nette\InvalidStateException("Service '$name' has already been registered.");
80: }
81:
82: if (is_object($service) && !$service instanceof \Closure && !$service instanceof Nette\Callback) {
83: $this->registry[$name] = $service;
84: $this->meta[$name] = $meta;
85: return $this;
86:
87: } elseif (!is_string($service) || strpos($service, ':') !== FALSE) {
88: $service = new Nette\Callback($service);
89: }
90:
91: $this->factories[$name] = array($service);
92: $this->registry[$name] = & $this->factories[$name][1];
93: $this->meta[$name] = $meta;
94: return $this;
95: }
96:
97:
98: 99: 100: 101: 102:
103: public function removeService($name)
104: {
105: $this->updating();
106: unset($this->registry[$name], $this->factories[$name], $this->meta[$name]);
107: }
108:
109:
110: 111: 112: 113: 114:
115: public function getService($name)
116: {
117: if (isset($this->registry[$name])) {
118: return $this->registry[$name];
119:
120: } elseif (isset($this->creating[$name])) {
121: throw new Nette\InvalidStateException("Circular reference detected for services: "
122: . implode(', ', array_keys($this->creating)) . ".");
123: }
124:
125: if (isset($this->factories[$name])) {
126: list($factory) = $this->factories[$name];
127: if (is_string($factory)) {
128: if (!class_exists($factory)) {
129: throw new Nette\InvalidStateException("Cannot instantiate service, class '$factory' not found.");
130: }
131: try {
132: $this->creating[$name] = TRUE;
133: $service = new $factory;
134: } catch (\Exception $e) {}
135:
136: } elseif (!$factory->isCallable()) {
137: throw new Nette\InvalidStateException("Unable to create service '$name', factory '$factory' is not callable.");
138:
139: } else {
140: $this->creating[$name] = TRUE;
141: try {
142: $service = $factory($this);
143: } catch (\Exception $e) {}
144: }
145:
146: } elseif (method_exists($this, $factory = Container::getMethodName($name)) && $this->getReflection()->getMethod($factory)->getName() === $factory) {
147: $this->creating[$name] = TRUE;
148: try {
149: $service = $this->$factory();
150: } catch (\Exception $e) {}
151:
152: } else {
153: throw new MissingServiceException("Service '$name' not found.");
154: }
155:
156: unset($this->creating[$name]);
157:
158: if (isset($e)) {
159: throw $e;
160:
161: } elseif (!is_object($service)) {
162: throw new Nette\UnexpectedValueException("Unable to create service '$name', value returned by factory '$factory' is not object.");
163: }
164:
165: return $this->registry[$name] = $service;
166: }
167:
168:
169: 170: 171: 172: 173:
174: public function hasService($name)
175: {
176: return isset($this->registry[$name])
177: || isset($this->factories[$name])
178: || method_exists($this, $method = Container::getMethodName($name)) && $this->getReflection()->getMethod($method)->getName() === $method;
179: }
180:
181:
182: 183: 184: 185: 186:
187: public function isCreated($name)
188: {
189: if (!$this->hasService($name)) {
190: throw new MissingServiceException("Service '$name' not found.");
191: }
192: return isset($this->registry[$name]);
193: }
194:
195:
196: 197: 198: 199: 200: 201: 202:
203: public function getByType($class, $need = TRUE)
204: {
205: $lower = ltrim(strtolower($class), '\\');
206: if (!isset($this->classes[$lower])) {
207: if ($need) {
208: throw new MissingServiceException("Service of type $class not found.");
209: }
210: } elseif ($this->classes[$lower] === FALSE) {
211: throw new MissingServiceException("Multiple services of type $class found.");
212: } else {
213: return $this->getService($this->classes[$lower]);
214: }
215: }
216:
217:
218: 219: 220: 221: 222:
223: public function findByTag($tag)
224: {
225: $found = array();
226: foreach ($this->meta as $name => $meta) {
227: if (isset($meta[self::TAGS][$tag])) {
228: $found[$name] = $meta[self::TAGS][$tag];
229: }
230: }
231: return $found;
232: }
233:
234:
235:
236:
237:
238: 239: 240: 241: 242: 243: 244:
245: public function createInstance($class, array $args = array())
246: {
247: $rc = Nette\Reflection\ClassType::from($class);
248: if (!$rc->isInstantiable()) {
249: throw new ServiceCreationException("Class $class is not instantiable.");
250:
251: } elseif ($constructor = $rc->getConstructor()) {
252: return $rc->newInstanceArgs(Helpers::autowireArguments($constructor, $args, $this));
253:
254: } elseif ($args) {
255: throw new ServiceCreationException("Unable to pass arguments, class $class has no constructor.");
256: }
257: return new $class;
258: }
259:
260:
261: 262: 263: 264: 265: 266:
267: public function callMethod($function, array $args = array())
268: {
269: $callback = new Nette\Callback($function);
270: return $callback->invokeArgs(Helpers::autowireArguments($callback->toReflection(), $args, $this));
271: }
272:
273:
274:
275:
276:
277: 278: 279: 280: 281:
282: public function expand($s)
283: {
284: return Helpers::expand($s, $this->parameters);
285: }
286:
287:
288: 289: 290: 291: 292:
293: public function &__get($name)
294: {
295: if (!isset($this->registry[$name])) {
296: $this->getService($name);
297: }
298: return $this->registry[$name];
299: }
300:
301:
302: 303: 304: 305: 306: 307:
308: public function __set($name, $service)
309: {
310: $this->updating();
311: if (!is_string($name) || $name === '') {
312: throw new Nette\InvalidArgumentException("Service name must be a non-empty string, " . gettype($name) . " given.");
313:
314: } elseif (isset($this->registry[$name])) {
315: throw new Nette\InvalidStateException("Service '$name' has already been registered.");
316:
317: } elseif (!is_object($service)) {
318: throw new Nette\InvalidArgumentException("Service must be a object, " . gettype($service) . " given.");
319: }
320: $this->registry[$name] = $service;
321: }
322:
323:
324: 325: 326: 327: 328:
329: public function __isset($name)
330: {
331: return $this->hasService($name);
332: }
333:
334:
335: 336: 337: 338:
339: public function __unset($name)
340: {
341: $this->removeService($name);
342: }
343:
344:
345: public static function getMethodName($name, $isService = TRUE)
346: {
347: $uname = ucfirst($name);
348: return ($isService ? 'createService' : 'create') . ((string) $name === $uname ? '__' : '') . str_replace('.', '__', $uname);
349: }
350:
351: }
352: