YamlFileLoader.php 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. <?php
  2. namespace Symfony\Components\DependencyInjection\Loader;
  3. use Symfony\Components\DependencyInjection\Container;
  4. use Symfony\Components\DependencyInjection\Definition;
  5. use Symfony\Components\DependencyInjection\Reference;
  6. use Symfony\Components\DependencyInjection\BuilderConfiguration;
  7. use Symfony\Components\YAML\YAML;
  8. /*
  9. * This file is part of the symfony framework.
  10. *
  11. * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
  12. *
  13. * This source file is subject to the MIT license that is bundled
  14. * with this source code in the file LICENSE.
  15. */
  16. /**
  17. * YamlFileLoader loads YAML files service definitions.
  18. *
  19. * The YAML format does not support anonymous services (cf. the XML loader).
  20. *
  21. * @package symfony
  22. * @subpackage dependency_injection
  23. * @author Fabien Potencier <fabien.potencier@symfony-project.com>
  24. */
  25. class YamlFileLoader extends FileLoader
  26. {
  27. /**
  28. * Loads an array of XML files.
  29. *
  30. * @param array $files An array of XML files
  31. *
  32. * @return array An array of definitions and parameters
  33. */
  34. public function load($files)
  35. {
  36. if (!is_array($files))
  37. {
  38. $files = array($files);
  39. }
  40. return $this->parse($this->getFilesAsArray($files));
  41. }
  42. protected function parse($data)
  43. {
  44. $configuration = new BuilderConfiguration();
  45. foreach ($data as $file => $content)
  46. {
  47. if (!$content)
  48. {
  49. continue;
  50. }
  51. // imports
  52. $this->parseImports($configuration, $content, $file);
  53. // parameters
  54. if (isset($content['parameters']))
  55. {
  56. foreach ($content['parameters'] as $key => $value)
  57. {
  58. $configuration->setParameter(strtolower($key), $this->resolveServices($value));
  59. }
  60. }
  61. // services
  62. $this->parseDefinitions($configuration, $content, $file);
  63. // extensions
  64. $this->loadFromExtensions($configuration, $content);
  65. }
  66. return $configuration;
  67. }
  68. protected function parseImports(BuilderConfiguration $configuration, $content, $file)
  69. {
  70. if (!isset($content['imports']))
  71. {
  72. return;
  73. }
  74. foreach ($content['imports'] as $import)
  75. {
  76. $configuration->merge($this->parseImport($import, $file));
  77. }
  78. }
  79. protected function parseImport($import, $file)
  80. {
  81. if (isset($import['class']) && $import['class'] != get_class($this))
  82. {
  83. $class = $import['class'];
  84. $loader = new $class($this->paths);
  85. }
  86. else
  87. {
  88. $loader = $this;
  89. }
  90. $importedFile = $this->getAbsolutePath($import['resource'], dirname($file));
  91. return $loader->load($importedFile);
  92. }
  93. protected function parseDefinitions(BuilderConfiguration $configuration, $content, $file)
  94. {
  95. if (!isset($content['services']))
  96. {
  97. return;
  98. }
  99. foreach ($content['services'] as $id => $service)
  100. {
  101. $this->parseDefinition($configuration, $id, $service, $file);
  102. }
  103. }
  104. protected function parseDefinition(BuilderConfiguration $configuration, $id, $service, $file)
  105. {
  106. if (is_string($service) && 0 === strpos($service, '@'))
  107. {
  108. $configuration->setAlias($id, substr($service, 1));
  109. return;
  110. }
  111. $definition = new Definition($service['class']);
  112. if (isset($service['shared']))
  113. {
  114. $definition->setShared($service['shared']);
  115. }
  116. if (isset($service['constructor']))
  117. {
  118. $definition->setConstructor($service['constructor']);
  119. }
  120. if (isset($service['file']))
  121. {
  122. $definition->setFile($service['file']);
  123. }
  124. if (isset($service['arguments']))
  125. {
  126. $definition->setArguments($this->resolveServices($service['arguments']));
  127. }
  128. if (isset($service['configurator']))
  129. {
  130. if (is_string($service['configurator']))
  131. {
  132. $definition->setConfigurator($service['configurator']);
  133. }
  134. else
  135. {
  136. $definition->setConfigurator(array($this->resolveServices($service['configurator'][0]), $service['configurator'][1]));
  137. }
  138. }
  139. if (isset($service['calls']))
  140. {
  141. foreach ($service['calls'] as $call)
  142. {
  143. $definition->addMethodCall($call[0], $this->resolveServices($call[1]));
  144. }
  145. }
  146. $configuration->setDefinition($id, $definition);
  147. }
  148. protected function getFilesAsArray(array $files)
  149. {
  150. $yamls = array();
  151. foreach ($files as $file)
  152. {
  153. $path = $this->getAbsolutePath($file);
  154. if (!file_exists($path))
  155. {
  156. throw new \InvalidArgumentException(sprintf('The service file "%s" does not exist (in: %s).', $file, implode(', ', $this->paths)));
  157. }
  158. $yamls[$path] = $this->validate(YAML::load($path), $path);
  159. }
  160. return $yamls;
  161. }
  162. protected function validate($content, $file)
  163. {
  164. if (null === $content)
  165. {
  166. return $content;
  167. }
  168. if (!is_array($content))
  169. {
  170. throw new \InvalidArgumentException(sprintf('The service file "%s" is not valid.', $file));
  171. }
  172. foreach (array_keys($content) as $key)
  173. {
  174. if (in_array($key, array('imports', 'parameters', 'services')))
  175. {
  176. continue;
  177. }
  178. // can it be handled by an extension?
  179. if (false !== strpos($key, '.'))
  180. {
  181. list($namespace, $tag) = explode('.', $key);
  182. if (!static::getExtension($namespace))
  183. {
  184. throw new \InvalidArgumentException(sprintf('There is no extension able to load the configuration for "%s" (in %s).', $key, $file));
  185. }
  186. continue;
  187. }
  188. throw new \InvalidArgumentException(sprintf('The "%s" tag is not valid (in %s).', $key, $file));
  189. }
  190. return $content;
  191. }
  192. protected function resolveServices($value)
  193. {
  194. if (is_array($value))
  195. {
  196. $value = array_map(array($this, 'resolveServices'), $value);
  197. }
  198. else if (is_string($value) && 0 === strpos($value, '@@'))
  199. {
  200. $value = new Reference(substr($value, 2), Container::IGNORE_ON_INVALID_REFERENCE);
  201. }
  202. else if (is_string($value) && 0 === strpos($value, '@'))
  203. {
  204. $value = new Reference(substr($value, 1));
  205. }
  206. return $value;
  207. }
  208. protected function loadFromExtensions(BuilderConfiguration $configuration, $content)
  209. {
  210. foreach ($content as $key => $config)
  211. {
  212. if (in_array($key, array('imports', 'parameters', 'services')))
  213. {
  214. continue;
  215. }
  216. list($namespace, $tag) = explode('.', $key);
  217. if (!is_array($config))
  218. {
  219. $config = array();
  220. }
  221. $configuration->merge(static::getExtension($namespace)->load($tag, $config));
  222. }
  223. }
  224. }