PageRenderTime 55ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/application/libs/Nette/Config/Compiler.php

https://bitbucket.org/pavoleichler/2013_erichseemann_vypestujdobro
PHP | 367 lines | 244 code | 72 blank | 51 comment | 43 complexity | e31602284a105663c1c7a200dbe49870 MD5 | raw file
  1. <?php
  2. /**
  3. * This file is part of the Nette Framework (http://nette.org)
  4. *
  5. * Copyright (c) 2004 David Grudl (http://davidgrudl.com)
  6. *
  7. * For the full copyright and license information, please view
  8. * the file license.txt that was distributed with this source code.
  9. */
  10. namespace Nette\Config;
  11. use Nette,
  12. Nette\Utils\Validators;
  13. /**
  14. * DI container compiler.
  15. *
  16. * @author David Grudl
  17. *
  18. * @property-read CompilerExtension[] $extensions
  19. * @property-read Nette\DI\ContainerBuilder $containerBuilder
  20. * @property-read array $config
  21. */
  22. class Compiler extends Nette\Object
  23. {
  24. /** @var CompilerExtension[] */
  25. private $extensions = array();
  26. /** @var Nette\DI\ContainerBuilder */
  27. private $container;
  28. /** @var array */
  29. private $config;
  30. /** @var array reserved section names */
  31. private static $reserved = array('services' => 1, 'factories' => 1, 'parameters' => 1);
  32. /**
  33. * Add custom configurator extension.
  34. * @return Compiler provides a fluent interface
  35. */
  36. public function addExtension($name, CompilerExtension $extension)
  37. {
  38. if (isset(self::$reserved[$name])) {
  39. throw new Nette\InvalidArgumentException("Name '$name' is reserved.");
  40. }
  41. $this->extensions[$name] = $extension->setCompiler($this, $name);
  42. return $this;
  43. }
  44. /**
  45. * @return array
  46. */
  47. public function getExtensions()
  48. {
  49. return $this->extensions;
  50. }
  51. /**
  52. * @return Nette\DI\ContainerBuilder
  53. */
  54. public function getContainerBuilder()
  55. {
  56. return $this->container;
  57. }
  58. /**
  59. * Returns configuration without expanded parameters.
  60. * @return array
  61. */
  62. public function getConfig()
  63. {
  64. return $this->config;
  65. }
  66. /**
  67. * @return string
  68. */
  69. public function compile(array $config, $className, $parentName)
  70. {
  71. $this->config = $config;
  72. $this->container = new Nette\DI\ContainerBuilder;
  73. $this->processParameters();
  74. $this->processExtensions();
  75. $this->processServices();
  76. return $this->generateCode($className, $parentName);
  77. }
  78. public function processParameters()
  79. {
  80. if (isset($this->config['parameters'])) {
  81. $this->container->parameters = $this->config['parameters'];
  82. }
  83. }
  84. public function processExtensions()
  85. {
  86. for ($i = 0; $slice = array_slice($this->extensions, $i, 1); $i++) {
  87. reset($slice)->loadConfiguration();
  88. }
  89. if ($extra = array_diff_key($this->config, self::$reserved, $this->extensions)) {
  90. $extra = implode("', '", array_keys($extra));
  91. throw new Nette\InvalidStateException("Found sections '$extra' in configuration, but corresponding extensions are missing.");
  92. }
  93. }
  94. public function processServices()
  95. {
  96. $this->parseServices($this->container, $this->config);
  97. foreach ($this->extensions as $name => $extension) {
  98. $this->container->addDefinition($name)
  99. ->setClass('Nette\DI\NestedAccessor', array('@container', $name))
  100. ->setAutowired(FALSE);
  101. if (isset($this->config[$name])) {
  102. $this->parseServices($this->container, $this->config[$name], $name);
  103. }
  104. }
  105. foreach ($this->container->getDefinitions() as $name => $def) {
  106. $factory = $name . 'Factory';
  107. if (!$def->shared && !$def->internal && !$this->container->hasDefinition($factory)) {
  108. $this->container->addDefinition($factory)
  109. ->setClass('Nette\Callback', array('@container', Nette\DI\Container::getMethodName($name, FALSE)))
  110. ->setAutowired(FALSE)
  111. ->tags = $def->tags;
  112. }
  113. }
  114. }
  115. public function generateCode($className, $parentName)
  116. {
  117. foreach ($this->extensions as $extension) {
  118. $extension->beforeCompile();
  119. $this->container->addDependency(Nette\Reflection\ClassType::from($extension)->getFileName());
  120. }
  121. $classes[] = $class = $this->container->generateClass($parentName);
  122. $class->setName($className)
  123. ->addMethod('initialize');
  124. foreach ($this->extensions as $extension) {
  125. $extension->afterCompile($class);
  126. }
  127. $defs = $this->container->getDefinitions();
  128. ksort($defs);
  129. $list = array_keys($defs);
  130. foreach (array_reverse($defs, TRUE) as $name => $def) {
  131. if ($def->class === 'Nette\DI\NestedAccessor' && ($found = preg_grep('#^'.$name.'\.#i', $list))) {
  132. $list = array_diff($list, $found);
  133. $def->class = $className . '_' . preg_replace('#\W+#', '_', $name);
  134. $class->documents = preg_replace("#\S+(?= \\$$name$)#", $def->class, $class->documents);
  135. $classes[] = $accessor = new Nette\Utils\PhpGenerator\ClassType($def->class);
  136. foreach ($found as $item) {
  137. if ($defs[$item]->internal) {
  138. continue;
  139. }
  140. $short = substr($item, strlen($name) + 1);
  141. $accessor->addDocument($defs[$item]->shared
  142. ? "@property {$defs[$item]->class} \$$short"
  143. : "@method {$defs[$item]->class} create" . ucfirst("$short()"));
  144. }
  145. }
  146. }
  147. return implode("\n\n\n", $classes);
  148. }
  149. /********************* tools ****************d*g**/
  150. /**
  151. * Parses section 'services' from configuration file.
  152. * @return void
  153. */
  154. public static function parseServices(Nette\DI\ContainerBuilder $container, array $config, $namespace = NULL)
  155. {
  156. $services = isset($config['services']) ? $config['services'] : array();
  157. $factories = isset($config['factories']) ? $config['factories'] : array();
  158. if ($tmp = array_intersect_key($services, $factories)) {
  159. $tmp = implode("', '", array_keys($tmp));
  160. throw new Nette\DI\ServiceCreationException("It is not allowed to use services and factories with the same names: '$tmp'.");
  161. }
  162. $all = $services + $factories;
  163. uasort($all, function($a, $b) {
  164. return strcmp(Helpers::isInheriting($a), Helpers::isInheriting($b));
  165. });
  166. foreach ($all as $name => $def) {
  167. $shared = array_key_exists($name, $services);
  168. $name = ($namespace ? $namespace . '.' : '') . $name;
  169. if (($parent = Helpers::takeParent($def)) && $parent !== $name) {
  170. $container->removeDefinition($name);
  171. $definition = $container->addDefinition($name);
  172. if ($parent !== Helpers::OVERWRITE) {
  173. foreach ($container->getDefinition($parent) as $k => $v) {
  174. $definition->$k = unserialize(serialize($v)); // deep clone
  175. }
  176. }
  177. } elseif ($container->hasDefinition($name)) {
  178. $definition = $container->getDefinition($name);
  179. if ($definition->shared !== $shared) {
  180. throw new Nette\DI\ServiceCreationException("It is not allowed to use service and factory with the same name '$name'.");
  181. }
  182. } else {
  183. $definition = $container->addDefinition($name);
  184. }
  185. try {
  186. static::parseService($definition, $def, $shared);
  187. } catch (\Exception $e) {
  188. throw new Nette\DI\ServiceCreationException("Service '$name': " . $e->getMessage(), NULL, $e);
  189. }
  190. }
  191. }
  192. /**
  193. * Parses single service from configuration file.
  194. * @return void
  195. */
  196. public static function parseService(Nette\DI\ServiceDefinition $definition, $config, $shared = TRUE)
  197. {
  198. if ($config === NULL) {
  199. return;
  200. } elseif (!is_array($config)) {
  201. $config = array('class' => NULL, 'factory' => $config);
  202. }
  203. $known = $shared
  204. ? array('class', 'factory', 'arguments', 'setup', 'autowired', 'run', 'tags')
  205. : array('class', 'factory', 'arguments', 'setup', 'autowired', 'tags', 'internal', 'parameters');
  206. if ($error = array_diff(array_keys($config), $known)) {
  207. throw new Nette\InvalidStateException("Unknown key '" . implode("', '", $error) . "' in definition of service.");
  208. }
  209. $arguments = array();
  210. if (array_key_exists('arguments', $config)) {
  211. Validators::assertField($config, 'arguments', 'array');
  212. $arguments = self::filterArguments($config['arguments']);
  213. $definition->setArguments($arguments);
  214. }
  215. if (array_key_exists('class', $config) || array_key_exists('factory', $config)) {
  216. $definition->class = NULL;
  217. $definition->factory = NULL;
  218. }
  219. if (array_key_exists('class', $config)) {
  220. Validators::assertField($config, 'class', 'string|stdClass|null');
  221. if ($config['class'] instanceof \stdClass) {
  222. $definition->setClass($config['class']->value, self::filterArguments($config['class']->attributes));
  223. } else {
  224. $definition->setClass($config['class'], $arguments);
  225. }
  226. }
  227. if (array_key_exists('factory', $config)) {
  228. Validators::assertField($config, 'factory', 'callable|stdClass|null');
  229. if ($config['factory'] instanceof \stdClass) {
  230. $definition->setFactory($config['factory']->value, self::filterArguments($config['factory']->attributes));
  231. } else {
  232. $definition->setFactory($config['factory'], $arguments);
  233. }
  234. }
  235. if (isset($config['setup'])) {
  236. if (Helpers::takeParent($config['setup'])) {
  237. $definition->setup = array();
  238. }
  239. Validators::assertField($config, 'setup', 'list');
  240. foreach ($config['setup'] as $id => $setup) {
  241. Validators::assert($setup, 'callable|stdClass', "setup item #$id");
  242. if ($setup instanceof \stdClass) {
  243. Validators::assert($setup->value, 'callable', "setup item #$id");
  244. $definition->addSetup($setup->value, self::filterArguments($setup->attributes));
  245. } else {
  246. $definition->addSetup($setup);
  247. }
  248. }
  249. }
  250. $definition->setShared($shared);
  251. if (isset($config['parameters'])) {
  252. Validators::assertField($config, 'parameters', 'array');
  253. $definition->setParameters($config['parameters']);
  254. }
  255. if (isset($config['autowired'])) {
  256. Validators::assertField($config, 'autowired', 'bool');
  257. $definition->setAutowired($config['autowired']);
  258. }
  259. if (isset($config['internal'])) {
  260. Validators::assertField($config, 'internal', 'bool');
  261. $definition->setInternal($config['internal']);
  262. }
  263. if (isset($config['run'])) {
  264. $config['tags']['run'] = (bool) $config['run'];
  265. }
  266. if (isset($config['tags'])) {
  267. Validators::assertField($config, 'tags', 'array');
  268. if (Helpers::takeParent($config['tags'])) {
  269. $definition->tags = array();
  270. }
  271. foreach ($config['tags'] as $tag => $attrs) {
  272. if (is_int($tag) && is_string($attrs)) {
  273. $definition->addTag($attrs);
  274. } else {
  275. $definition->addTag($tag, $attrs);
  276. }
  277. }
  278. }
  279. }
  280. /**
  281. * Removes ... and replaces entities with Nette\DI\Statement.
  282. * @return array
  283. */
  284. public static function filterArguments(array $args)
  285. {
  286. foreach ($args as $k => $v) {
  287. if ($v === '...') {
  288. unset($args[$k]);
  289. } elseif ($v instanceof \stdClass && isset($v->value, $v->attributes)) {
  290. $args[$k] = new Nette\DI\Statement($v->value, self::filterArguments($v->attributes));
  291. }
  292. }
  293. return $args;
  294. }
  295. }