YamlFileLoader.php 7.7 KB

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