PageRenderTime 53ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 1ms

/vendor/nette/di/src/DI/Container.php

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