Packages

  • 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

  • DIContainer
  • DIContainerBuilder
  • DIHelpers
  • DIServiceDefinition
  • DIStatement

Interfaces

  • IDIContainer

Exceptions

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