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: if (!empty($this->meta[self::TYPES][$class][TRUE])) {
183: if (count($names = $this->meta[self::TYPES][$class][TRUE]) === 1) {
184: return $this->getService($names[0]);
185: }
186: throw new MissingServiceException("Multiple services of type $class found: " . implode(', ', $names) . '.');
187:
188: } elseif ($need) {
189: throw new MissingServiceException("Service of type $class not found.");
190: }
191: }
192:
193:
194: 195: 196: 197: 198:
199: public function findByType($class)
200: {
201: $class = ltrim($class, '\\');
202: return empty($this->meta[self::TYPES][$class])
203: ? array()
204: : call_user_func_array('array_merge', $this->meta[self::TYPES][$class]);
205: }
206:
207:
208: 209: 210: 211: 212:
213: public function findByTag($tag)
214: {
215: return isset($this->meta[self::TAGS][$tag]) ? $this->meta[self::TAGS][$tag] : array();
216: }
217:
218:
219:
220:
221:
222: 223: 224: 225: 226: 227: 228:
229: public function createInstance($class, array $args = array())
230: {
231: $rc = new \ReflectionClass($class);
232: if (!$rc->isInstantiable()) {
233: throw new ServiceCreationException("Class $class is not instantiable.");
234:
235: } elseif ($constructor = $rc->getConstructor()) {
236: return $rc->newInstanceArgs(Helpers::autowireArguments($constructor, $args, $this));
237:
238: } elseif ($args) {
239: throw new ServiceCreationException("Unable to pass arguments, class $class has no constructor.");
240: }
241: return new $class;
242: }
243:
244:
245: 246: 247: 248: 249:
250: public function callInjects($service)
251: {
252: Extensions\InjectExtension::callInjects($this, $service);
253: }
254:
255:
256: 257: 258: 259:
260: public function callMethod($function, array $args = array())
261: {
262: return call_user_func_array(
263: $function,
264: Helpers::autowireArguments(Nette\Utils\Callback::toReflection($function), $args, $this)
265: );
266: }
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: public function &__get($name)
286: {
287: $this->error(__METHOD__, 'getService');
288: $tmp = $this->getService($name);
289: return $tmp;
290: }
291:
292:
293:
294: public function __set($name, $service)
295: {
296: $this->error(__METHOD__, 'addService');
297: $this->addService($name, $service);
298: }
299:
300:
301:
302: public function __isset($name)
303: {
304: $this->error(__METHOD__, 'hasService');
305: return $this->hasService($name);
306: }
307:
308:
309:
310: public function __unset($name)
311: {
312: $this->error(__METHOD__, 'removeService');
313: $this->removeService($name);
314: }
315:
316:
317: private function error($oldName, $newName)
318: {
319: if (empty($this->parameters['container']['accessors'])) {
320: trigger_error("$oldName() is deprecated; use $newName() or enable di.accessors in configuration.", E_USER_DEPRECATED);
321: }
322: }
323:
324:
325: public static function getMethodName($name)
326: {
327: $uname = ucfirst($name);
328: return 'createService' . ((string) $name === $uname ? '__' : '') . str_replace('.', '__', $uname);
329: }
330:
331: }
332: