Namespaces

  • Nette
    • Application
      • Diagnostics
      • Responses
      • Routers
      • UI
    • Caching
      • Storages
    • ComponentModel
    • Config
      • Adapters
      • Extensions
    • Database
      • Diagnostics
      • Drivers
      • Reflection
      • Table
    • DI
      • Diagnostics
    • Diagnostics
    • Forms
      • Controls
      • Rendering
    • Http
    • Iterators
    • Latte
      • Macros
    • Loaders
    • Localization
    • Mail
    • Reflection
    • Security
      • Diagnostics
    • Templating
    • Utils
      • PhpGenerator
  • NetteModule
  • None
  • PHP

Classes

  • Container
  • ContainerBuilder
  • Helpers
  • ServiceDefinition
  • Statement

Exceptions

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