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