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: public function hasService($name)
115: {
116: $name = isset($this->meta[self::ALIASES][$name]) ? $this->meta[self::ALIASES][$name] : $name;
117: return isset($this->registry[$name])
118: || (method_exists($this, $method = self::getMethodName($name))
119: && ($rm = new \ReflectionMethod($this, $method)) && $rm->getName() === $method);
120: }
121:
122:
123: 124: 125: 126: 127:
128: public function isCreated($name)
129: {
130: if (!$this->hasService($name)) {
131: throw new MissingServiceException("Service '$name' not found.");
132: }
133: $name = isset($this->meta[self::ALIASES][$name]) ? $this->meta[self::ALIASES][$name] : $name;
134: return isset($this->registry[$name]);
135: }
136:
137:
138: 139: 140: 141: 142: 143:
144: public function createService($name, array $args = array())
145: {
146: $name = isset($this->meta[self::ALIASES][$name]) ? $this->meta[self::ALIASES][$name] : $name;
147: $method = self::getMethodName($name);
148: if (isset($this->creating[$name])) {
149: throw new Nette\InvalidStateException(sprintf('Circular reference detected for services: %s.', implode(', ', array_keys($this->creating))));
150:
151: } elseif (!method_exists($this, $method) || !($rm = new \ReflectionMethod($this, $method)) || $rm->getName() !== $method) {
152: throw new MissingServiceException("Service '$name' not found.");
153: }
154:
155: $this->creating[$name] = TRUE;
156: try {
157: $service = call_user_func_array(array($this, $method), $args);
158: } catch (\Exception $e) {
159: unset($this->creating[$name]);
160: throw $e;
161: }
162: unset($this->creating[$name]);
163:
164: if (!is_object($service)) {
165: throw new Nette\UnexpectedValueException("Unable to create service '$name', value returned by method $method() is not object.");
166: }
167:
168: return $service;
169: }
170:
171:
172: 173: 174: 175: 176: 177: 178:
179: public function getByType($class, $need = TRUE)
180: {
181: $class = ltrim($class, '\\');
182: $names = & $this->meta[self::TYPES][$class][TRUE];
183: if (count($names) === 1) {
184: return $this->getService($names[0]);
185: } elseif (count($names) > 1) {
186: throw new MissingServiceException("Multiple services of type $class found: " . implode(', ', $names) . '.');
187: } elseif ($need) {
188: throw new MissingServiceException("Service of type $class not found.");
189: }
190: }
191:
192:
193: 194: 195: 196: 197:
198: public function findByType($class)
199: {
200: $class = ltrim($class, '\\');
201: $meta = & $this->meta[self::TYPES];
202: return array_merge(
203: isset($meta[$class][TRUE]) ? $meta[$class][TRUE] : array(),
204: isset($meta[$class][FALSE]) ? $meta[$class][FALSE] : array()
205: );
206: }
207:
208:
209: 210: 211: 212: 213:
214: public function findByTag($tag)
215: {
216: return isset($this->meta[self::TAGS][$tag]) ? $this->meta[self::TAGS][$tag] : array();
217: }
218:
219:
220:
221:
222:
223: 224: 225: 226: 227: 228: 229:
230: public function createInstance($class, array $args = array())
231: {
232: $rc = new \ReflectionClass($class);
233: if (!$rc->isInstantiable()) {
234: throw new ServiceCreationException("Class $class is not instantiable.");
235:
236: } elseif ($constructor = $rc->getConstructor()) {
237: return $rc->newInstanceArgs(Helpers::autowireArguments($constructor, $args, $this));
238:
239: } elseif ($args) {
240: throw new ServiceCreationException("Unable to pass arguments, class $class has no constructor.");
241: }
242: return new $class;
243: }
244:
245:
246: 247: 248: 249: 250:
251: public function callInjects($service)
252: {
253: Extensions\InjectExtension::callInjects($this, $service);
254: }
255:
256:
257: 258: 259: 260: 261: 262:
263: public function callMethod($function, array $args = array())
264: {
265: return call_user_func_array(
266: $function,
267: Helpers::autowireArguments(Nette\Utils\Callback::toReflection($function), $args, $this)
268: );
269: }
270:
271:
272:
273:
274:
275: 276: 277: 278: 279: 280:
281: public function expand($s)
282: {
283: return Helpers::expand($s, $this->parameters);
284: }
285:
286:
287:
288: public function &__get($name)
289: {
290: $this->error(__METHOD__, 'getService');
291: $tmp = $this->getService($name);
292: return $tmp;
293: }
294:
295:
296:
297: public function __set($name, $service)
298: {
299: $this->error(__METHOD__, 'addService');
300: $this->addService($name, $service);
301: }
302:
303:
304:
305: public function __isset($name)
306: {
307: $this->error(__METHOD__, 'hasService');
308: return $this->hasService($name);
309: }
310:
311:
312:
313: public function __unset($name)
314: {
315: $this->error(__METHOD__, 'removeService');
316: $this->removeService($name);
317: }
318:
319:
320: private function error($oldName, $newName)
321: {
322: if (empty($this->parameters['container']['accessors'])) {
323: trigger_error("$oldName() is deprecated; use $newName() or enable di.accessors in configuration.", E_USER_DEPRECATED);
324: }
325: }
326:
327:
328: public static function getMethodName($name)
329: {
330: $uname = ucfirst($name);
331: return 'createService' . ((string) $name === $uname ? '__' : '') . str_replace('.', '__', $uname);
332: }
333:
334: }
335: