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