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