PageRenderTime 56ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 1ms

/vendor/nette/nette/Nette/Config/Compiler.php

https://bitbucket.org/iiic/iszp
PHP | 369 lines | 274 code | 52 blank | 43 comment | 14 complexity | d6e86d8d849ffb277c85deb238196e9b 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\\z)#", $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. $all = array_merge($services, $factories);
  159. uasort($all, function($a, $b) {
  160. return strcmp(Helpers::isInheriting($a), Helpers::isInheriting($b));
  161. });
  162. foreach ($all as $origName => $def) {
  163. $shared = array_key_exists($origName, $services);
  164. if ((string) (int) $origName === (string) $origName) {
  165. $name = (string) (count($container->getDefinitions()) + 1);
  166. } elseif ($shared && array_key_exists($origName, $factories)) {
  167. throw new Nette\DI\ServiceCreationException("It is not allowed to use services and factories with the same name: '$origName'.");
  168. } else {
  169. $name = ($namespace ? $namespace . '.' : '') . strtr($origName, '\\', '_');
  170. }
  171. if (($parent = Helpers::takeParent($def)) && $parent !== $name) {
  172. $container->removeDefinition($name);
  173. $definition = $container->addDefinition($name);
  174. if ($parent !== Helpers::OVERWRITE) {
  175. foreach ($container->getDefinition($parent) as $k => $v) {
  176. $definition->$k = unserialize(serialize($v)); // deep clone
  177. }
  178. }
  179. } elseif ($container->hasDefinition($name)) {
  180. $definition = $container->getDefinition($name);
  181. if ($definition->shared !== $shared) {
  182. throw new Nette\DI\ServiceCreationException("It is not allowed to use service and factory with the same name '$name'.");
  183. }
  184. } else {
  185. $definition = $container->addDefinition($name);
  186. }
  187. try {
  188. static::parseService($definition, $def, $shared);
  189. } catch (\Exception $e) {
  190. throw new Nette\DI\ServiceCreationException("Service '$name': " . $e->getMessage(), NULL, $e);
  191. }
  192. }
  193. }
  194. /**
  195. * Parses single service from configuration file.
  196. * @return void
  197. */
  198. public static function parseService(Nette\DI\ServiceDefinition $definition, $config, $shared = TRUE)
  199. {
  200. if ($config === NULL) {
  201. return;
  202. } elseif (!is_array($config)) {
  203. $config = array('class' => NULL, 'factory' => $config);
  204. }
  205. $known = $shared
  206. ? array('class', 'factory', 'arguments', 'setup', 'autowired', 'run', 'tags')
  207. : array('class', 'factory', 'arguments', 'setup', 'autowired', 'tags', 'internal', 'parameters');
  208. if ($error = array_diff(array_keys($config), $known)) {
  209. throw new Nette\InvalidStateException("Unknown key '" . implode("', '", $error) . "' in definition of service.");
  210. }
  211. $arguments = array();
  212. if (array_key_exists('arguments', $config)) {
  213. Validators::assertField($config, 'arguments', 'array');
  214. $arguments = self::filterArguments($config['arguments']);
  215. $definition->setArguments($arguments);
  216. }
  217. if (array_key_exists('class', $config) || array_key_exists('factory', $config)) {
  218. $definition->class = NULL;
  219. $definition->factory = NULL;
  220. }
  221. if (array_key_exists('class', $config)) {
  222. Validators::assertField($config, 'class', 'string|stdClass|null');
  223. if ($config['class'] instanceof \stdClass) {
  224. $definition->setClass($config['class']->value, self::filterArguments($config['class']->attributes));
  225. } else {
  226. $definition->setClass($config['class'], $arguments);
  227. }
  228. }
  229. if (array_key_exists('factory', $config)) {
  230. Validators::assertField($config, 'factory', 'callable|stdClass|null');
  231. if ($config['factory'] instanceof \stdClass) {
  232. $definition->setFactory($config['factory']->value, self::filterArguments($config['factory']->attributes));
  233. } else {
  234. $definition->setFactory($config['factory'], $arguments);
  235. }
  236. }
  237. if (isset($config['setup'])) {
  238. if (Helpers::takeParent($config['setup'])) {
  239. $definition->setup = array();
  240. }
  241. Validators::assertField($config, 'setup', 'list');
  242. foreach ($config['setup'] as $id => $setup) {
  243. Validators::assert($setup, 'callable|stdClass', "setup item #$id");
  244. if ($setup instanceof \stdClass) {
  245. Validators::assert($setup->value, 'callable', "setup item #$id");
  246. $definition->addSetup($setup->value, self::filterArguments($setup->attributes));
  247. } else {
  248. $definition->addSetup($setup);
  249. }
  250. }
  251. }
  252. $definition->setShared($shared);
  253. if (isset($config['parameters'])) {
  254. Validators::assertField($config, 'parameters', 'array');
  255. $definition->setParameters($config['parameters']);
  256. }
  257. if (isset($config['autowired'])) {
  258. Validators::assertField($config, 'autowired', 'bool');
  259. $definition->setAutowired($config['autowired']);
  260. }
  261. if (isset($config['internal'])) {
  262. Validators::assertField($config, 'internal', 'bool');
  263. $definition->setInternal($config['internal']);
  264. }
  265. if (isset($config['run'])) {
  266. $config['tags']['run'] = (bool) $config['run'];
  267. }
  268. if (isset($config['tags'])) {
  269. Validators::assertField($config, 'tags', 'array');
  270. if (Helpers::takeParent($config['tags'])) {
  271. $definition->tags = array();
  272. }
  273. foreach ($config['tags'] as $tag => $attrs) {
  274. if (is_int($tag) && is_string($attrs)) {
  275. $definition->addTag($attrs);
  276. } else {
  277. $definition->addTag($tag, $attrs);
  278. }
  279. }
  280. }
  281. }
  282. /**
  283. * Removes ... and replaces entities with Nette\DI\Statement.
  284. * @return array
  285. */
  286. public static function filterArguments(array $args)
  287. {
  288. foreach ($args as $k => $v) {
  289. if ($v === '...') {
  290. unset($args[$k]);
  291. } elseif ($v instanceof \stdClass && isset($v->value, $v->attributes)) {
  292. $args[$k] = new Nette\DI\Statement($v->value, self::filterArguments($v->attributes));
  293. }
  294. }
  295. return $args;
  296. }
  297. }