PageRenderTime 45ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/web/vendor/nette/di/src/DI/Compiler.php

https://gitlab.com/adam.kvita/MI-VMM-SIFT
PHP | 461 lines | 308 code | 87 blank | 66 comment | 50 complexity | 1d5c7e8cba91f18522f9f877942dadb5 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. use Nette\Utils\Validators;
  9. /**
  10. * DI container compiler.
  11. */
  12. class Compiler
  13. {
  14. use Nette\SmartObject;
  15. /** @var CompilerExtension[] */
  16. private $extensions = [];
  17. /** @var ContainerBuilder */
  18. private $builder;
  19. /** @var array */
  20. private $config = [];
  21. /** @var DependencyChecker */
  22. private $dependencies;
  23. /** @var string */
  24. private $className = 'Container';
  25. /** @var array reserved section names */
  26. private static $reserved = ['services' => 1, 'parameters' => 1];
  27. public function __construct(ContainerBuilder $builder = NULL)
  28. {
  29. $this->builder = $builder ?: new ContainerBuilder;
  30. $this->dependencies = new DependencyChecker;
  31. }
  32. /**
  33. * Add custom configurator extension.
  34. * @return self
  35. */
  36. public function addExtension($name, CompilerExtension $extension)
  37. {
  38. if (isset($this->extensions[$name]) || isset(self::$reserved[$name])) {
  39. throw new Nette\InvalidArgumentException("Name '$name' is already used or reserved.");
  40. }
  41. $this->extensions[$name] = $extension->setCompiler($this, $name);
  42. return $this;
  43. }
  44. /**
  45. * @return array
  46. */
  47. public function getExtensions($type = NULL)
  48. {
  49. return $type
  50. ? array_filter($this->extensions, function ($item) use ($type) { return $item instanceof $type; })
  51. : $this->extensions;
  52. }
  53. /**
  54. * @return ContainerBuilder
  55. */
  56. public function getContainerBuilder()
  57. {
  58. return $this->builder;
  59. }
  60. /**
  61. * @return self
  62. */
  63. public function setClassName($className)
  64. {
  65. $this->className = $className;
  66. return $this;
  67. }
  68. /**
  69. * Adds new configuration.
  70. * @return self
  71. */
  72. public function addConfig(array $config)
  73. {
  74. $this->config = Config\Helpers::merge($config, $this->config);
  75. return $this;
  76. }
  77. /**
  78. * Adds new configuration from file.
  79. * @return self
  80. */
  81. public function loadConfig($file)
  82. {
  83. $loader = new Config\Loader;
  84. $this->addConfig($loader->load($file));
  85. $this->dependencies->add($loader->getDependencies());
  86. return $this;
  87. }
  88. /**
  89. * Returns configuration.
  90. * @return array
  91. */
  92. public function getConfig()
  93. {
  94. return $this->config;
  95. }
  96. /**
  97. * Adds dependencies to the list.
  98. * @param array of ReflectionClass|\ReflectionFunctionAbstract|string
  99. * @return self
  100. */
  101. public function addDependencies(array $deps)
  102. {
  103. $this->dependencies->add(array_filter($deps));
  104. return $this;
  105. }
  106. /**
  107. * Exports dependencies.
  108. * @return array
  109. */
  110. public function exportDependencies()
  111. {
  112. return $this->dependencies->export();
  113. }
  114. /**
  115. * @return string
  116. */
  117. public function compile(array $config = NULL, $className = NULL, $parentName = NULL)
  118. {
  119. if (func_num_args()) {
  120. trigger_error(__METHOD__ . ' arguments are deprecated, use Compiler::addConfig() and Compiler::setClassName().', E_USER_DEPRECATED);
  121. $this->config = func_get_arg(0) ?: $this->config;
  122. $this->className = @func_get_arg(1) ?: $this->className;
  123. }
  124. $this->processParameters();
  125. $this->processExtensions();
  126. $this->processServices();
  127. $classes = $this->generateCode();
  128. return implode("\n\n\n", $classes);
  129. }
  130. /** @internal */
  131. public function processParameters()
  132. {
  133. if (isset($this->config['parameters'])) {
  134. $this->builder->parameters = Helpers::expand($this->config['parameters'], $this->config['parameters'], TRUE);
  135. }
  136. }
  137. /** @internal */
  138. public function processExtensions()
  139. {
  140. $this->config = Helpers::expand(array_diff_key($this->config, self::$reserved), $this->builder->parameters)
  141. + array_intersect_key($this->config, self::$reserved);
  142. foreach ($first = $this->getExtensions(Extensions\ExtensionsExtension::class) as $name => $extension) {
  143. $extension->setConfig(isset($this->config[$name]) ? $this->config[$name] : []);
  144. $extension->loadConfiguration();
  145. }
  146. $last = $this->getExtensions(Extensions\InjectExtension::class);
  147. $this->extensions = array_merge(array_diff_key($this->extensions, $last), $last);
  148. $extensions = array_diff_key($this->extensions, $first);
  149. foreach (array_intersect_key($extensions, $this->config) as $name => $extension) {
  150. $extension->setConfig($this->config[$name] ?: []);
  151. }
  152. foreach ($extensions as $extension) {
  153. $extension->loadConfiguration();
  154. }
  155. if ($extra = array_diff_key($this->extensions, $extensions, $first)) {
  156. $extra = implode("', '", array_keys($extra));
  157. throw new Nette\DeprecatedException("Extensions '$extra' were added while container was being compiled.");
  158. } elseif ($extra = key(array_diff_key($this->config, self::$reserved, $this->extensions))) {
  159. $hint = Nette\Utils\ObjectMixin::getSuggestion(array_keys(self::$reserved + $this->extensions), $extra);
  160. throw new Nette\InvalidStateException(
  161. "Found section '$extra' in configuration, but corresponding extension is missing"
  162. . ($hint ? ", did you mean '$hint'?" : '.')
  163. );
  164. }
  165. }
  166. /** @internal */
  167. public function processServices()
  168. {
  169. if (isset($this->config['services'])) {
  170. self::loadDefinitions($this->builder, $this->config['services']);
  171. }
  172. }
  173. /** @internal */
  174. public function generateCode()
  175. {
  176. if (func_num_args()) {
  177. trigger_error(__METHOD__ . ' arguments are deprecated, use Compiler::setClassName().', E_USER_DEPRECATED);
  178. $this->className = func_get_arg(0) ?: $this->className;
  179. }
  180. $this->builder->prepareClassList();
  181. foreach ($this->extensions as $extension) {
  182. $extension->beforeCompile();
  183. $this->dependencies->add([(new \ReflectionClass($extension))->getFileName()]);
  184. }
  185. $generator = new PhpGenerator($this->builder);
  186. $classes = $generator->generate($this->className);
  187. $classes[0]->addMethod('initialize');
  188. $this->dependencies->add($this->builder->getDependencies());
  189. foreach ($this->extensions as $extension) {
  190. $extension->afterCompile($classes[0]);
  191. }
  192. return $classes;
  193. }
  194. /********************* tools ****************d*g**/
  195. /**
  196. * Adds service definitions from configuration.
  197. * @return void
  198. */
  199. public static function loadDefinitions(ContainerBuilder $builder, array $services, $namespace = NULL)
  200. {
  201. $depths = [];
  202. foreach ($services as $name => $def) {
  203. $path = [];
  204. while (Config\Helpers::isInheriting($def)) {
  205. $path[] = $def;
  206. $def = isset($services[$def[Config\Helpers::EXTENDS_KEY]]) ? $services[$def[Config\Helpers::EXTENDS_KEY]] : [];
  207. if (in_array($def, $path, TRUE)) {
  208. throw new ServiceCreationException("Circular reference detected for service '$name'.");
  209. }
  210. }
  211. $depths[$name] = count($path);
  212. }
  213. array_multisort($depths, $services);
  214. foreach ($services as $name => $def) {
  215. if ((string) (int) $name === (string) $name) {
  216. $postfix = $def instanceof Statement && is_string($def->getEntity()) ? '.' . $def->getEntity() : (is_scalar($def) ? ".$def" : '');
  217. $name = (count($builder->getDefinitions()) + 1) . preg_replace('#\W+#', '_', $postfix);
  218. } elseif ($namespace) {
  219. $name = $namespace . '.' . $name;
  220. }
  221. if ($def === FALSE) {
  222. $builder->removeDefinition($name);
  223. continue;
  224. }
  225. if ($namespace) {
  226. $def = Helpers::prefixServiceName($def, $namespace);
  227. }
  228. $params = $builder->parameters;
  229. if (is_array($def) && isset($def['parameters'])) {
  230. foreach ((array) $def['parameters'] as $k => $v) {
  231. $v = explode(' ', is_int($k) ? $v : $k);
  232. $params[end($v)] = $builder::literal('$' . end($v));
  233. }
  234. }
  235. $def = Helpers::expand($def, $params);
  236. if (($parent = Config\Helpers::takeParent($def)) && $parent !== $name) {
  237. trigger_error("Section inheritance $name < $parent is deprecated.", E_USER_DEPRECATED);
  238. $builder->removeDefinition($name);
  239. $definition = $builder->addDefinition(
  240. $name,
  241. $parent === Config\Helpers::OVERWRITE ? NULL : clone $builder->getDefinition($parent)
  242. );
  243. } elseif ($builder->hasDefinition($name)) {
  244. $definition = $builder->getDefinition($name);
  245. } else {
  246. $definition = $builder->addDefinition($name);
  247. }
  248. try {
  249. static::loadDefinition($definition, $def);
  250. } catch (\Exception $e) {
  251. throw new ServiceCreationException("Service '$name': " . $e->getMessage(), 0, $e);
  252. }
  253. }
  254. }
  255. /**
  256. * Parses single service definition from configuration.
  257. * @return void
  258. */
  259. public static function loadDefinition(ServiceDefinition $definition, $config)
  260. {
  261. if ($config === NULL) {
  262. return;
  263. } elseif (is_string($config) && interface_exists($config)) {
  264. $config = ['class' => NULL, 'implement' => $config];
  265. } elseif ($config instanceof Statement && is_string($config->getEntity()) && interface_exists($config->getEntity())) {
  266. $config = ['class' => NULL, 'implement' => $config->getEntity(), 'factory' => array_shift($config->arguments)];
  267. } elseif (!is_array($config) || isset($config[0], $config[1])) {
  268. $config = ['class' => NULL, 'factory' => $config];
  269. }
  270. if (array_key_exists('create', $config)) {
  271. trigger_error("Key 'create' is deprecated, use 'factory' or 'class' in configuration.", E_USER_DEPRECATED);
  272. $config['factory'] = $config['create'];
  273. unset($config['create']);
  274. }
  275. $known = ['class', 'factory', 'arguments', 'setup', 'autowired', 'dynamic', 'inject', 'parameters', 'implement', 'run', 'tags'];
  276. if ($error = array_diff(array_keys($config), $known)) {
  277. $hints = array_filter(array_map(function ($error) use ($known) {
  278. return Nette\Utils\ObjectMixin::getSuggestion($known, $error);
  279. }, $error));
  280. $hint = $hints ? ", did you mean '" . implode("', '", $hints) . "'?" : '.';
  281. throw new Nette\InvalidStateException(sprintf("Unknown key '%s' in definition of service$hint", implode("', '", $error)));
  282. }
  283. $config = Helpers::filterArguments($config);
  284. if (array_key_exists('class', $config) || array_key_exists('factory', $config)) {
  285. $definition->setClass(NULL);
  286. $definition->setFactory(NULL);
  287. }
  288. if (array_key_exists('class', $config)) {
  289. Validators::assertField($config, 'class', 'string|Nette\DI\Statement|null');
  290. if (!$config['class'] instanceof Statement) {
  291. $definition->setClass($config['class']);
  292. }
  293. $definition->setFactory($config['class']);
  294. }
  295. if (array_key_exists('factory', $config)) {
  296. Validators::assertField($config, 'factory', 'callable|Nette\DI\Statement|null');
  297. $definition->setFactory($config['factory']);
  298. }
  299. if (array_key_exists('arguments', $config)) {
  300. Validators::assertField($config, 'arguments', 'array');
  301. $arguments = $config['arguments'];
  302. if (!Config\Helpers::takeParent($arguments) && !Nette\Utils\Arrays::isList($arguments) && $definition->getFactory()) {
  303. $arguments += $definition->getFactory()->arguments;
  304. }
  305. $definition->setArguments($arguments);
  306. }
  307. if (isset($config['setup'])) {
  308. if (Config\Helpers::takeParent($config['setup'])) {
  309. $definition->setSetup([]);
  310. }
  311. Validators::assertField($config, 'setup', 'list');
  312. foreach ($config['setup'] as $id => $setup) {
  313. Validators::assert($setup, 'callable|Nette\DI\Statement|array:1', "setup item #$id");
  314. if (is_array($setup)) {
  315. $setup = new Statement(key($setup), array_values($setup));
  316. }
  317. $definition->addSetup($setup);
  318. }
  319. }
  320. if (isset($config['parameters'])) {
  321. Validators::assertField($config, 'parameters', 'array');
  322. $definition->setParameters($config['parameters']);
  323. }
  324. if (isset($config['implement'])) {
  325. Validators::assertField($config, 'implement', 'string');
  326. $definition->setImplement($config['implement']);
  327. $definition->setAutowired(TRUE);
  328. }
  329. if (isset($config['autowired'])) {
  330. Validators::assertField($config, 'autowired', 'bool|string|array');
  331. $definition->setAutowired($config['autowired']);
  332. }
  333. if (isset($config['dynamic'])) {
  334. Validators::assertField($config, 'dynamic', 'bool');
  335. $definition->setDynamic($config['dynamic']);
  336. }
  337. if (isset($config['inject'])) {
  338. Validators::assertField($config, 'inject', 'bool');
  339. $definition->addTag(Extensions\InjectExtension::TAG_INJECT, $config['inject']);
  340. }
  341. if (isset($config['run'])) {
  342. trigger_error("Option 'run' is deprecated, use 'run' as tag.", E_USER_DEPRECATED);
  343. $config['tags']['run'] = (bool) $config['run'];
  344. }
  345. if (isset($config['tags'])) {
  346. Validators::assertField($config, 'tags', 'array');
  347. if (Config\Helpers::takeParent($config['tags'])) {
  348. $definition->setTags([]);
  349. }
  350. foreach ($config['tags'] as $tag => $attrs) {
  351. if (is_int($tag) && is_string($attrs)) {
  352. $definition->addTag($attrs);
  353. } else {
  354. $definition->addTag($tag, $attrs);
  355. }
  356. }
  357. }
  358. }
  359. /** @deprecated */
  360. public static function filterArguments(array $args)
  361. {
  362. return Helpers::filterArguments($args);
  363. }
  364. /** @deprecated */
  365. public static function parseServices(ContainerBuilder $builder, array $config, $namespace = NULL)
  366. {
  367. self::loadDefinitions($builder, isset($config['services']) ? $config['services'] : [], $namespace);
  368. }
  369. /** @deprecated */
  370. public static function parseService(ServiceDefinition $definition, $config)
  371. {
  372. self::loadDefinition($definition, $config);
  373. }
  374. }