PageRenderTime 37ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 1ms

/PHP/Test/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php

https://bitbucket.org/AdriVanHoudt/school
PHP | 418 lines | 242 code | 56 blank | 120 comment | 31 complexity | e0d97f4d618b2308caa0ebdca7dd8717 MD5 | raw file
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\DependencyInjection\Loader;
  11. use Symfony\Component\Config\Resource\FileResource;
  12. use Symfony\Component\Config\Util\XmlUtils;
  13. use Symfony\Component\DependencyInjection\DefinitionDecorator;
  14. use Symfony\Component\DependencyInjection\ContainerInterface;
  15. use Symfony\Component\DependencyInjection\Alias;
  16. use Symfony\Component\DependencyInjection\Definition;
  17. use Symfony\Component\DependencyInjection\Reference;
  18. use Symfony\Component\DependencyInjection\SimpleXMLElement;
  19. use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
  20. use Symfony\Component\DependencyInjection\Exception\RuntimeException;
  21. /**
  22. * XmlFileLoader loads XML files service definitions.
  23. *
  24. * @author Fabien Potencier <fabien@symfony.com>
  25. */
  26. class XmlFileLoader extends FileLoader
  27. {
  28. /**
  29. * Loads an XML file.
  30. *
  31. * @param mixed $file The resource
  32. * @param string $type The resource type
  33. */
  34. public function load($file, $type = null)
  35. {
  36. $path = $this->locator->locate($file);
  37. $xml = $this->parseFile($path);
  38. $xml->registerXPathNamespace('container', 'http://symfony.com/schema/dic/services');
  39. $this->container->addResource(new FileResource($path));
  40. // anonymous services
  41. $this->processAnonymousServices($xml, $path);
  42. // imports
  43. $this->parseImports($xml, $path);
  44. // parameters
  45. $this->parseParameters($xml, $path);
  46. // extensions
  47. $this->loadFromExtensions($xml);
  48. // services
  49. $this->parseDefinitions($xml, $path);
  50. }
  51. /**
  52. * Returns true if this class supports the given resource.
  53. *
  54. * @param mixed $resource A resource
  55. * @param string $type The resource type
  56. *
  57. * @return Boolean true if this class supports the given resource, false otherwise
  58. */
  59. public function supports($resource, $type = null)
  60. {
  61. return is_string($resource) && 'xml' === pathinfo($resource, PATHINFO_EXTENSION);
  62. }
  63. /**
  64. * Parses parameters
  65. *
  66. * @param SimpleXMLElement $xml
  67. * @param string $file
  68. */
  69. private function parseParameters(SimpleXMLElement $xml, $file)
  70. {
  71. if (!$xml->parameters) {
  72. return;
  73. }
  74. $this->container->getParameterBag()->add($xml->parameters->getArgumentsAsPhp('parameter'));
  75. }
  76. /**
  77. * Parses imports
  78. *
  79. * @param SimpleXMLElement $xml
  80. * @param string $file
  81. */
  82. private function parseImports(SimpleXMLElement $xml, $file)
  83. {
  84. if (false === $imports = $xml->xpath('//container:imports/container:import')) {
  85. return;
  86. }
  87. foreach ($imports as $import) {
  88. $this->setCurrentDir(dirname($file));
  89. $this->import((string) $import['resource'], null, (Boolean) $import->getAttributeAsPhp('ignore-errors'), $file);
  90. }
  91. }
  92. /**
  93. * Parses multiple definitions
  94. *
  95. * @param SimpleXMLElement $xml
  96. * @param string $file
  97. */
  98. private function parseDefinitions(SimpleXMLElement $xml, $file)
  99. {
  100. if (false === $services = $xml->xpath('//container:services/container:service')) {
  101. return;
  102. }
  103. foreach ($services as $service) {
  104. $this->parseDefinition((string) $service['id'], $service, $file);
  105. }
  106. }
  107. /**
  108. * Parses an individual Definition
  109. *
  110. * @param string $id
  111. * @param SimpleXMLElement $service
  112. * @param string $file
  113. */
  114. private function parseDefinition($id, $service, $file)
  115. {
  116. if ((string) $service['alias']) {
  117. $public = true;
  118. if (isset($service['public'])) {
  119. $public = $service->getAttributeAsPhp('public');
  120. }
  121. $this->container->setAlias($id, new Alias((string) $service['alias'], $public));
  122. return;
  123. }
  124. if (isset($service['parent'])) {
  125. $definition = new DefinitionDecorator((string) $service['parent']);
  126. } else {
  127. $definition = new Definition();
  128. }
  129. foreach (array('class', 'scope', 'public', 'factory-class', 'factory-method', 'factory-service', 'synthetic', 'abstract') as $key) {
  130. if (isset($service[$key])) {
  131. $method = 'set'.str_replace('-', '', $key);
  132. $definition->$method((string) $service->getAttributeAsPhp($key));
  133. }
  134. }
  135. if ($service->file) {
  136. $definition->setFile((string) $service->file);
  137. }
  138. $definition->setArguments($service->getArgumentsAsPhp('argument'));
  139. $definition->setProperties($service->getArgumentsAsPhp('property'));
  140. if (isset($service->configurator)) {
  141. if (isset($service->configurator['function'])) {
  142. $definition->setConfigurator((string) $service->configurator['function']);
  143. } else {
  144. if (isset($service->configurator['service'])) {
  145. $class = new Reference((string) $service->configurator['service'], ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, false);
  146. } else {
  147. $class = (string) $service->configurator['class'];
  148. }
  149. $definition->setConfigurator(array($class, (string) $service->configurator['method']));
  150. }
  151. }
  152. foreach ($service->call as $call) {
  153. $definition->addMethodCall((string) $call['method'], $call->getArgumentsAsPhp('argument'));
  154. }
  155. foreach ($service->tag as $tag) {
  156. $parameters = array();
  157. foreach ($tag->attributes() as $name => $value) {
  158. if ('name' === $name) {
  159. continue;
  160. }
  161. $parameters[$name] = SimpleXMLElement::phpize($value);
  162. }
  163. $definition->addTag((string) $tag['name'], $parameters);
  164. }
  165. $this->container->setDefinition($id, $definition);
  166. }
  167. /**
  168. * Parses a XML file.
  169. *
  170. * @param string $file Path to a file
  171. *
  172. * @return SimpleXMLElement
  173. *
  174. * @throws InvalidArgumentException When loading of XML file returns error
  175. */
  176. protected function parseFile($file)
  177. {
  178. try {
  179. $dom = XmlUtils::loadFile($file, array($this, 'validateSchema'));
  180. } catch (\InvalidArgumentException $e) {
  181. throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
  182. }
  183. $this->validateExtensions($dom, $file);
  184. return simplexml_import_dom($dom, 'Symfony\\Component\\DependencyInjection\\SimpleXMLElement');
  185. }
  186. /**
  187. * Processes anonymous services
  188. *
  189. * @param SimpleXMLElement $xml
  190. * @param string $file
  191. */
  192. private function processAnonymousServices(SimpleXMLElement $xml, $file)
  193. {
  194. $definitions = array();
  195. $count = 0;
  196. // anonymous services as arguments/properties
  197. if (false !== $nodes = $xml->xpath('//container:argument[@type="service"][not(@id)]|//container:property[@type="service"][not(@id)]')) {
  198. foreach ($nodes as $node) {
  199. // give it a unique name
  200. $node['id'] = sprintf('%s_%d', md5($file), ++$count);
  201. $definitions[(string) $node['id']] = array($node->service, $file, false);
  202. $node->service['id'] = (string) $node['id'];
  203. }
  204. }
  205. // anonymous services "in the wild"
  206. if (false !== $nodes = $xml->xpath('//container:services/container:service[not(@id)]')) {
  207. foreach ($nodes as $node) {
  208. // give it a unique name
  209. $node['id'] = sprintf('%s_%d', md5($file), ++$count);
  210. $definitions[(string) $node['id']] = array($node, $file, true);
  211. $node->service['id'] = (string) $node['id'];
  212. }
  213. }
  214. // resolve definitions
  215. krsort($definitions);
  216. foreach ($definitions as $id => $def) {
  217. // anonymous services are always private
  218. $def[0]['public'] = false;
  219. $this->parseDefinition($id, $def[0], $def[1]);
  220. $oNode = dom_import_simplexml($def[0]);
  221. if (true === $def[2]) {
  222. $nNode = new \DOMElement('_services');
  223. $oNode->parentNode->replaceChild($nNode, $oNode);
  224. $nNode->setAttribute('id', $id);
  225. } else {
  226. $oNode->parentNode->removeChild($oNode);
  227. }
  228. }
  229. }
  230. /**
  231. * Validates a documents XML schema.
  232. *
  233. * @param \DOMDocument $dom
  234. *
  235. * @return Boolean
  236. *
  237. * @throws RuntimeException When extension references a non-existent XSD file
  238. */
  239. public function validateSchema(\DOMDocument $dom)
  240. {
  241. $schemaLocations = array('http://symfony.com/schema/dic/services' => str_replace('\\', '/', __DIR__.'/schema/dic/services/services-1.0.xsd'));
  242. if ($element = $dom->documentElement->getAttributeNS('http://www.w3.org/2001/XMLSchema-instance', 'schemaLocation')) {
  243. $items = preg_split('/\s+/', $element);
  244. for ($i = 0, $nb = count($items); $i < $nb; $i += 2) {
  245. if (!$this->container->hasExtension($items[$i])) {
  246. continue;
  247. }
  248. if (($extension = $this->container->getExtension($items[$i])) && false !== $extension->getXsdValidationBasePath()) {
  249. $path = str_replace($extension->getNamespace(), str_replace('\\', '/', $extension->getXsdValidationBasePath()).'/', $items[$i + 1]);
  250. if (!is_file($path)) {
  251. throw new RuntimeException(sprintf('Extension "%s" references a non-existent XSD file "%s"', get_class($extension), $path));
  252. }
  253. $schemaLocations[$items[$i]] = $path;
  254. }
  255. }
  256. }
  257. $tmpfiles = array();
  258. $imports = '';
  259. foreach ($schemaLocations as $namespace => $location) {
  260. $parts = explode('/', $location);
  261. if (0 === stripos($location, 'phar://')) {
  262. $tmpfile = tempnam(sys_get_temp_dir(), 'sf2');
  263. if ($tmpfile) {
  264. copy($location, $tmpfile);
  265. $tmpfiles[] = $tmpfile;
  266. $parts = explode('/', str_replace('\\', '/', $tmpfile));
  267. }
  268. }
  269. $drive = '\\' === DIRECTORY_SEPARATOR ? array_shift($parts).'/' : '';
  270. $location = 'file:///'.$drive.implode('/', array_map('rawurlencode', $parts));
  271. $imports .= sprintf(' <xsd:import namespace="%s" schemaLocation="%s" />'."\n", $namespace, $location);
  272. }
  273. $source = <<<EOF
  274. <?xml version="1.0" encoding="utf-8" ?>
  275. <xsd:schema xmlns="http://symfony.com/schema"
  276. xmlns:xsd="http://www.w3.org/2001/XMLSchema"
  277. targetNamespace="http://symfony.com/schema"
  278. elementFormDefault="qualified">
  279. <xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
  280. $imports
  281. </xsd:schema>
  282. EOF
  283. ;
  284. $valid = @$dom->schemaValidateSource($source);
  285. foreach ($tmpfiles as $tmpfile) {
  286. @unlink($tmpfile);
  287. }
  288. return $valid;
  289. }
  290. /**
  291. * Validates an extension.
  292. *
  293. * @param \DOMDocument $dom
  294. * @param string $file
  295. *
  296. * @throws InvalidArgumentException When no extension is found corresponding to a tag
  297. */
  298. private function validateExtensions(\DOMDocument $dom, $file)
  299. {
  300. foreach ($dom->documentElement->childNodes as $node) {
  301. if (!$node instanceof \DOMElement || 'http://symfony.com/schema/dic/services' === $node->namespaceURI) {
  302. continue;
  303. }
  304. // can it be handled by an extension?
  305. if (!$this->container->hasExtension($node->namespaceURI)) {
  306. $extensionNamespaces = array_filter(array_map(function ($ext) { return $ext->getNamespace(); }, $this->container->getExtensions()));
  307. throw new InvalidArgumentException(sprintf(
  308. 'There is no extension able to load the configuration for "%s" (in %s). Looked for namespace "%s", found %s',
  309. $node->tagName,
  310. $file,
  311. $node->namespaceURI,
  312. $extensionNamespaces ? sprintf('"%s"', implode('", "', $extensionNamespaces)) : 'none'
  313. ));
  314. }
  315. }
  316. }
  317. /**
  318. * Loads from an extension.
  319. *
  320. * @param SimpleXMLElement $xml
  321. */
  322. private function loadFromExtensions(SimpleXMLElement $xml)
  323. {
  324. foreach (dom_import_simplexml($xml)->childNodes as $node) {
  325. if (!$node instanceof \DOMElement || $node->namespaceURI === 'http://symfony.com/schema/dic/services') {
  326. continue;
  327. }
  328. $values = static::convertDomElementToArray($node);
  329. if (!is_array($values)) {
  330. $values = array();
  331. }
  332. $this->container->loadFromExtension($node->namespaceURI, $values);
  333. }
  334. }
  335. /**
  336. * Converts a \DomElement object to a PHP array.
  337. *
  338. * The following rules applies during the conversion:
  339. *
  340. * * Each tag is converted to a key value or an array
  341. * if there is more than one "value"
  342. *
  343. * * The content of a tag is set under a "value" key (<foo>bar</foo>)
  344. * if the tag also has some nested tags
  345. *
  346. * * The attributes are converted to keys (<foo foo="bar"/>)
  347. *
  348. * * The nested-tags are converted to keys (<foo><foo>bar</foo></foo>)
  349. *
  350. * @param \DomElement $element A \DomElement instance
  351. *
  352. * @return array A PHP array
  353. */
  354. public static function convertDomElementToArray(\DomElement $element)
  355. {
  356. return XmlUtils::convertDomElementToArray($element);
  357. }
  358. }