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