PageRenderTime 27ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/libs/Nette/DI/Container.php

https://bitbucket.org/jelito/asdf
PHP | 337 lines | 238 code | 38 blank | 61 comment | 17 complexity | 378dfa8e4f5c8fe1972862d525088884 MD5 | raw file
  1. <?php
  2. /**
  3. * This file is part of the Nette Framework (http://nette.org)
  4. * Copyright (c) 2004 David Grudl (http://davidgrudl.com)
  5. */
  6. namespace Nette\DI;
  7. use Nette;
  8. /**
  9. * The dependency injection container default implementation.
  10. *
  11. * @author David Grudl
  12. */
  13. class Container extends Nette\Object
  14. {
  15. const TAGS = 'tags';
  16. const TYPES = 'types';
  17. /** @var array user parameters */
  18. /*private*/public $parameters = array();
  19. /** @var array storage for shared objects */
  20. private $registry = array();
  21. /** @var array[] */
  22. protected $meta = array();
  23. /** @var array circular reference detector */
  24. private $creating;
  25. public function __construct(array $params = array())
  26. {
  27. $this->parameters = $params + $this->parameters;
  28. }
  29. /**
  30. * @return array
  31. */
  32. public function getParameters()
  33. {
  34. return $this->parameters;
  35. }
  36. /**
  37. * Adds the service to the container.
  38. * @param string
  39. * @param object
  40. * @return self
  41. */
  42. public function addService($name, $service)
  43. {
  44. if (func_num_args() > 2) {
  45. throw new Nette\DeprecatedException('Parameter $meta has been removed.');
  46. } elseif (!is_string($name) || !$name) {
  47. throw new Nette\InvalidArgumentException(sprintf('Service name must be a non-empty string, %s given.', gettype($name)));
  48. } elseif (isset($this->registry[$name])) {
  49. throw new Nette\InvalidStateException("Service '$name' already exists.");
  50. } elseif (is_string($service) || is_array($service) || $service instanceof \Closure || $service instanceof Nette\Callback) {
  51. trigger_error(sprintf('Passing factories to %s() is deprecated; pass the object itself.', __METHOD__), E_USER_DEPRECATED);
  52. $service = is_string($service) && !preg_match('#\x00|:#', $service) ? new $service : call_user_func($service, $this);
  53. }
  54. if (!is_object($service)) {
  55. throw new Nette\InvalidArgumentException(sprintf('Service must be a object, %s given.', gettype($service)));
  56. }
  57. $this->registry[$name] = $service;
  58. return $this;
  59. }
  60. /**
  61. * Removes the service from the container.
  62. * @param string
  63. * @return void
  64. */
  65. public function removeService($name)
  66. {
  67. unset($this->registry[$name]);
  68. }
  69. /**
  70. * Gets the service object by name.
  71. * @param string
  72. * @return object
  73. * @throws MissingServiceException
  74. */
  75. public function getService($name)
  76. {
  77. if (!isset($this->registry[$name])) {
  78. $this->registry[$name] = $this->createService($name);
  79. }
  80. return $this->registry[$name];
  81. }
  82. /**
  83. * Does the service exist?
  84. * @param string service name
  85. * @return bool
  86. */
  87. public function hasService($name)
  88. {
  89. return isset($this->registry[$name])
  90. || method_exists($this, $method = Container::getMethodName($name)) && $this->getReflection()->getMethod($method)->getName() === $method;
  91. }
  92. /**
  93. * Is the service created?
  94. * @param string service name
  95. * @return bool
  96. */
  97. public function isCreated($name)
  98. {
  99. if (!$this->hasService($name)) {
  100. throw new MissingServiceException("Service '$name' not found.");
  101. }
  102. return isset($this->registry[$name]);
  103. }
  104. /**
  105. * Creates new instance of the service.
  106. * @param string service name
  107. * @return object
  108. * @throws MissingServiceException
  109. */
  110. public function createService($name, array $args = array())
  111. {
  112. $method = Container::getMethodName($name);
  113. if (isset($this->creating[$name])) {
  114. throw new Nette\InvalidStateException(sprintf('Circular reference detected for services: %s.', implode(', ', array_keys($this->creating))));
  115. } elseif (!method_exists($this, $method) || $this->getReflection()->getMethod($method)->getName() !== $method) {
  116. throw new MissingServiceException("Service '$name' not found.");
  117. }
  118. $this->creating[$name] = TRUE;
  119. try {
  120. $service = call_user_func_array(array($this, $method), $args);
  121. } catch (\Exception $e) {
  122. unset($this->creating[$name]);
  123. throw $e;
  124. }
  125. unset($this->creating[$name]);
  126. if (!is_object($service)) {
  127. throw new Nette\UnexpectedValueException("Unable to create service '$name', value returned by method $method() is not object.");
  128. }
  129. return $service;
  130. }
  131. /**
  132. * Resolves service by type.
  133. * @param string class or interface
  134. * @param bool throw exception if service doesn't exist?
  135. * @return object service or NULL
  136. * @throws MissingServiceException
  137. */
  138. public function getByType($class, $need = TRUE)
  139. {
  140. $names = $this->findByType($class);
  141. if (!$names) {
  142. if ($need) {
  143. throw new MissingServiceException("Service of type $class not found.");
  144. }
  145. } elseif (count($names) > 1) {
  146. throw new MissingServiceException("Multiple services of type $class found: " . implode(', ', $names) . '.');
  147. } else {
  148. return $this->getService($names[0]);
  149. }
  150. }
  151. /**
  152. * Gets the service names of the specified type.
  153. * @param string
  154. * @return string[]
  155. */
  156. public function findByType($class)
  157. {
  158. $class = ltrim(strtolower($class), '\\');
  159. return isset($this->meta[self::TYPES][$class]) ? $this->meta[self::TYPES][$class] : array();
  160. }
  161. /**
  162. * Gets the service names of the specified tag.
  163. * @param string
  164. * @return array of [service name => tag attributes]
  165. */
  166. public function findByTag($tag)
  167. {
  168. return isset($this->meta[self::TAGS][$tag]) ? $this->meta[self::TAGS][$tag] : array();
  169. }
  170. /********************* autowiring ****************d*g**/
  171. /**
  172. * Creates new instance using autowiring.
  173. * @param string class
  174. * @param array arguments
  175. * @return object
  176. * @throws Nette\InvalidArgumentException
  177. */
  178. public function createInstance($class, array $args = array())
  179. {
  180. $rc = Nette\Reflection\ClassType::from($class);
  181. if (!$rc->isInstantiable()) {
  182. throw new ServiceCreationException("Class $class is not instantiable.");
  183. } elseif ($constructor = $rc->getConstructor()) {
  184. return $rc->newInstanceArgs(Helpers::autowireArguments($constructor, $args, $this));
  185. } elseif ($args) {
  186. throw new ServiceCreationException("Unable to pass arguments, class $class has no constructor.");
  187. }
  188. return new $class;
  189. }
  190. /**
  191. * Calls all methods starting with with "inject" using autowiring.
  192. * @param object
  193. * @return void
  194. */
  195. public function callInjects($service)
  196. {
  197. if (!is_object($service)) {
  198. throw new Nette\InvalidArgumentException(sprintf('Service must be object, %s given.', gettype($service)));
  199. }
  200. foreach (array_reverse(get_class_methods($service)) as $method) {
  201. if (substr($method, 0, 6) === 'inject') {
  202. $this->callMethod(array($service, $method));
  203. }
  204. }
  205. foreach (Helpers::getInjectProperties(Nette\Reflection\ClassType::from($service)) as $property => $type) {
  206. $service->$property = $this->getByType($type);
  207. }
  208. }
  209. /**
  210. * Calls method using autowiring.
  211. * @param mixed class, object, function, callable
  212. * @param array arguments
  213. * @return mixed
  214. */
  215. public function callMethod($function, array $args = array())
  216. {
  217. return call_user_func_array(
  218. $function,
  219. Helpers::autowireArguments(Nette\Utils\Callback::toReflection($function), $args, $this)
  220. );
  221. }
  222. /********************* shortcuts ****************d*g**/
  223. /**
  224. * Expands %placeholders%.
  225. * @param mixed
  226. * @return mixed
  227. */
  228. public function expand($s)
  229. {
  230. return Helpers::expand($s, $this->parameters);
  231. }
  232. /** @deprecated */
  233. public function &__get($name)
  234. {
  235. $this->error(__METHOD__, 'getService');
  236. $tmp = $this->getService($name);
  237. return $tmp;
  238. }
  239. /** @deprecated */
  240. public function __set($name, $service)
  241. {
  242. $this->error(__METHOD__, 'addService');
  243. $this->addService($name, $service);
  244. }
  245. /** @deprecated */
  246. public function __isset($name)
  247. {
  248. $this->error(__METHOD__, 'hasService');
  249. return $this->hasService($name);
  250. }
  251. /** @deprecated */
  252. public function __unset($name)
  253. {
  254. $this->error(__METHOD__, 'removeService');
  255. $this->removeService($name);
  256. }
  257. private function error($oldName, $newName)
  258. {
  259. if (empty($this->parameters['container']['accessors'])) {
  260. trigger_error("$oldName() is deprecated; use $newName() or enable nette.container.accessors in configuration.", E_USER_DEPRECATED);
  261. }
  262. }
  263. public static function getMethodName($name)
  264. {
  265. $uname = ucfirst($name);
  266. return 'createService' . ((string) $name === $uname ? '__' : '') . str_replace('.', '__', $uname);
  267. }
  268. }