Chris@0: Chris@0: * Chris@0: * For the full copyright and license information, please view the LICENSE Chris@0: * file that was distributed with this source code. Chris@0: */ Chris@0: Chris@0: namespace Symfony\Component\DependencyInjection\Dumper; Chris@0: Chris@17: use Symfony\Component\DependencyInjection\Alias; Chris@14: use Symfony\Component\DependencyInjection\Argument\IteratorArgument; Chris@14: use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; Chris@14: use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument; Chris@0: use Symfony\Component\DependencyInjection\ContainerInterface; Chris@17: use Symfony\Component\DependencyInjection\Definition; Chris@17: use Symfony\Component\DependencyInjection\Exception\RuntimeException; Chris@0: use Symfony\Component\DependencyInjection\Parameter; Chris@0: use Symfony\Component\DependencyInjection\Reference; Chris@0: use Symfony\Component\ExpressionLanguage\Expression; Chris@0: Chris@0: /** Chris@0: * XmlDumper dumps a service container as an XML string. Chris@0: * Chris@0: * @author Fabien Potencier Chris@0: * @author Martin HasoĊˆ Chris@0: */ Chris@0: class XmlDumper extends Dumper Chris@0: { Chris@0: /** Chris@0: * @var \DOMDocument Chris@0: */ Chris@0: private $document; Chris@0: Chris@0: /** Chris@0: * Dumps the service container as an XML string. Chris@0: * Chris@0: * @return string An xml string representing of the service container Chris@0: */ Chris@17: public function dump(array $options = []) Chris@0: { Chris@0: $this->document = new \DOMDocument('1.0', 'utf-8'); Chris@0: $this->document->formatOutput = true; Chris@0: Chris@0: $container = $this->document->createElementNS('http://symfony.com/schema/dic/services', 'container'); Chris@0: $container->setAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance'); Chris@18: $container->setAttribute('xsi:schemaLocation', 'http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd'); Chris@0: Chris@0: $this->addParameters($container); Chris@0: $this->addServices($container); Chris@0: Chris@0: $this->document->appendChild($container); Chris@0: $xml = $this->document->saveXML(); Chris@0: $this->document = null; Chris@0: Chris@0: return $this->container->resolveEnvPlaceholders($xml); Chris@0: } Chris@0: Chris@0: private function addParameters(\DOMElement $parent) Chris@0: { Chris@0: $data = $this->container->getParameterBag()->all(); Chris@0: if (!$data) { Chris@0: return; Chris@0: } Chris@0: Chris@14: if ($this->container->isCompiled()) { Chris@0: $data = $this->escape($data); Chris@0: } Chris@0: Chris@0: $parameters = $this->document->createElement('parameters'); Chris@0: $parent->appendChild($parameters); Chris@0: $this->convertParameters($data, 'parameter', $parameters); Chris@0: } Chris@0: Chris@0: private function addMethodCalls(array $methodcalls, \DOMElement $parent) Chris@0: { Chris@0: foreach ($methodcalls as $methodcall) { Chris@0: $call = $this->document->createElement('call'); Chris@0: $call->setAttribute('method', $methodcall[0]); Chris@17: if (\count($methodcall[1])) { Chris@0: $this->convertParameters($methodcall[1], 'argument', $call); Chris@0: } Chris@0: $parent->appendChild($call); Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * Adds a service. Chris@0: * Chris@0: * @param Definition $definition Chris@0: * @param string $id Chris@0: * @param \DOMElement $parent Chris@0: */ Chris@0: private function addService($definition, $id, \DOMElement $parent) Chris@0: { Chris@0: $service = $this->document->createElement('service'); Chris@0: if (null !== $id) { Chris@0: $service->setAttribute('id', $id); Chris@0: } Chris@0: if ($class = $definition->getClass()) { Chris@0: if ('\\' === substr($class, 0, 1)) { Chris@0: $class = substr($class, 1); Chris@0: } Chris@0: Chris@0: $service->setAttribute('class', $class); Chris@0: } Chris@0: if (!$definition->isShared()) { Chris@0: $service->setAttribute('shared', 'false'); Chris@0: } Chris@14: if (!$definition->isPrivate()) { Chris@14: $service->setAttribute('public', $definition->isPublic() ? 'true' : 'false'); Chris@0: } Chris@0: if ($definition->isSynthetic()) { Chris@0: $service->setAttribute('synthetic', 'true'); Chris@0: } Chris@0: if ($definition->isLazy()) { Chris@0: $service->setAttribute('lazy', 'true'); Chris@0: } Chris@0: if (null !== $decorated = $definition->getDecoratedService()) { Chris@0: list($decorated, $renamedId, $priority) = $decorated; Chris@0: $service->setAttribute('decorates', $decorated); Chris@0: if (null !== $renamedId) { Chris@0: $service->setAttribute('decoration-inner-name', $renamedId); Chris@0: } Chris@0: if (0 !== $priority) { Chris@0: $service->setAttribute('decoration-priority', $priority); Chris@0: } Chris@0: } Chris@0: Chris@0: foreach ($definition->getTags() as $name => $tags) { Chris@0: foreach ($tags as $attributes) { Chris@0: $tag = $this->document->createElement('tag'); Chris@0: $tag->setAttribute('name', $name); Chris@0: foreach ($attributes as $key => $value) { Chris@0: $tag->setAttribute($key, $value); Chris@0: } Chris@0: $service->appendChild($tag); Chris@0: } Chris@0: } Chris@0: Chris@0: if ($definition->getFile()) { Chris@0: $file = $this->document->createElement('file'); Chris@0: $file->appendChild($this->document->createTextNode($definition->getFile())); Chris@0: $service->appendChild($file); Chris@0: } Chris@0: Chris@0: if ($parameters = $definition->getArguments()) { Chris@0: $this->convertParameters($parameters, 'argument', $service); Chris@0: } Chris@0: Chris@0: if ($parameters = $definition->getProperties()) { Chris@0: $this->convertParameters($parameters, 'property', $service, 'name'); Chris@0: } Chris@0: Chris@0: $this->addMethodCalls($definition->getMethodCalls(), $service); Chris@0: Chris@0: if ($callable = $definition->getFactory()) { Chris@0: $factory = $this->document->createElement('factory'); Chris@0: Chris@17: if (\is_array($callable) && $callable[0] instanceof Definition) { Chris@0: $this->addService($callable[0], null, $factory); Chris@0: $factory->setAttribute('method', $callable[1]); Chris@17: } elseif (\is_array($callable)) { Chris@14: if (null !== $callable[0]) { Chris@14: $factory->setAttribute($callable[0] instanceof Reference ? 'service' : 'class', $callable[0]); Chris@14: } Chris@0: $factory->setAttribute('method', $callable[1]); Chris@0: } else { Chris@0: $factory->setAttribute('function', $callable); Chris@0: } Chris@0: $service->appendChild($factory); Chris@0: } Chris@0: Chris@0: if ($definition->isDeprecated()) { Chris@0: $deprecated = $this->document->createElement('deprecated'); Chris@0: $deprecated->appendChild($this->document->createTextNode($definition->getDeprecationMessage('%service_id%'))); Chris@0: Chris@0: $service->appendChild($deprecated); Chris@0: } Chris@0: Chris@0: if ($definition->isAutowired()) { Chris@0: $service->setAttribute('autowire', 'true'); Chris@0: } Chris@0: Chris@14: foreach ($definition->getAutowiringTypes(false) as $autowiringTypeValue) { Chris@0: $autowiringType = $this->document->createElement('autowiring-type'); Chris@0: $autowiringType->appendChild($this->document->createTextNode($autowiringTypeValue)); Chris@0: Chris@0: $service->appendChild($autowiringType); Chris@0: } Chris@0: Chris@14: if ($definition->isAutoconfigured()) { Chris@14: $service->setAttribute('autoconfigure', 'true'); Chris@14: } Chris@14: Chris@12: if ($definition->isAbstract()) { Chris@12: $service->setAttribute('abstract', 'true'); Chris@12: } Chris@12: Chris@0: if ($callable = $definition->getConfigurator()) { Chris@0: $configurator = $this->document->createElement('configurator'); Chris@0: Chris@17: if (\is_array($callable) && $callable[0] instanceof Definition) { Chris@0: $this->addService($callable[0], null, $configurator); Chris@0: $configurator->setAttribute('method', $callable[1]); Chris@17: } elseif (\is_array($callable)) { Chris@0: $configurator->setAttribute($callable[0] instanceof Reference ? 'service' : 'class', $callable[0]); Chris@0: $configurator->setAttribute('method', $callable[1]); Chris@0: } else { Chris@0: $configurator->setAttribute('function', $callable); Chris@0: } Chris@0: $service->appendChild($configurator); Chris@0: } Chris@0: Chris@0: $parent->appendChild($service); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Adds a service alias. Chris@0: * Chris@0: * @param string $alias Chris@0: * @param Alias $id Chris@0: * @param \DOMElement $parent Chris@0: */ Chris@0: private function addServiceAlias($alias, Alias $id, \DOMElement $parent) Chris@0: { Chris@0: $service = $this->document->createElement('service'); Chris@0: $service->setAttribute('id', $alias); Chris@0: $service->setAttribute('alias', $id); Chris@14: if (!$id->isPrivate()) { Chris@14: $service->setAttribute('public', $id->isPublic() ? 'true' : 'false'); Chris@0: } Chris@0: $parent->appendChild($service); Chris@0: } Chris@0: Chris@0: private function addServices(\DOMElement $parent) Chris@0: { Chris@0: $definitions = $this->container->getDefinitions(); Chris@0: if (!$definitions) { Chris@0: return; Chris@0: } Chris@0: Chris@0: $services = $this->document->createElement('services'); Chris@0: foreach ($definitions as $id => $definition) { Chris@0: $this->addService($definition, $id, $services); Chris@0: } Chris@0: Chris@0: $aliases = $this->container->getAliases(); Chris@0: foreach ($aliases as $alias => $id) { Chris@0: while (isset($aliases[(string) $id])) { Chris@0: $id = $aliases[(string) $id]; Chris@0: } Chris@0: $this->addServiceAlias($alias, $id, $services); Chris@0: } Chris@0: $parent->appendChild($services); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Converts parameters. Chris@0: * Chris@0: * @param array $parameters Chris@0: * @param string $type Chris@0: * @param \DOMElement $parent Chris@0: * @param string $keyAttribute Chris@0: */ Chris@0: private function convertParameters(array $parameters, $type, \DOMElement $parent, $keyAttribute = 'key') Chris@0: { Chris@17: $withKeys = array_keys($parameters) !== range(0, \count($parameters) - 1); Chris@0: foreach ($parameters as $key => $value) { Chris@0: $element = $this->document->createElement($type); Chris@0: if ($withKeys) { Chris@0: $element->setAttribute($keyAttribute, $key); Chris@0: } Chris@0: Chris@14: if ($value instanceof ServiceClosureArgument) { Chris@14: $value = $value->getValues()[0]; Chris@14: } Chris@17: if (\is_array($value)) { Chris@0: $element->setAttribute('type', 'collection'); Chris@0: $this->convertParameters($value, $type, $element, 'key'); Chris@14: } elseif ($value instanceof TaggedIteratorArgument) { Chris@14: $element->setAttribute('type', 'tagged'); Chris@14: $element->setAttribute('tag', $value->getTag()); Chris@14: } elseif ($value instanceof IteratorArgument) { Chris@14: $element->setAttribute('type', 'iterator'); Chris@14: $this->convertParameters($value->getValues(), $type, $element, 'key'); Chris@0: } elseif ($value instanceof Reference) { Chris@0: $element->setAttribute('type', 'service'); Chris@0: $element->setAttribute('id', (string) $value); Chris@18: $behavior = $value->getInvalidBehavior(); Chris@18: if (ContainerInterface::NULL_ON_INVALID_REFERENCE == $behavior) { Chris@0: $element->setAttribute('on-invalid', 'null'); Chris@18: } elseif (ContainerInterface::IGNORE_ON_INVALID_REFERENCE == $behavior) { Chris@0: $element->setAttribute('on-invalid', 'ignore'); Chris@18: } elseif (ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE == $behavior) { Chris@14: $element->setAttribute('on-invalid', 'ignore_uninitialized'); Chris@0: } Chris@0: } elseif ($value instanceof Definition) { Chris@0: $element->setAttribute('type', 'service'); Chris@0: $this->addService($value, null, $element); Chris@0: } elseif ($value instanceof Expression) { Chris@0: $element->setAttribute('type', 'expression'); Chris@0: $text = $this->document->createTextNode(self::phpToXml((string) $value)); Chris@0: $element->appendChild($text); Chris@0: } else { Chris@17: if (\in_array($value, ['null', 'true', 'false'], true)) { Chris@0: $element->setAttribute('type', 'string'); Chris@0: } Chris@0: $text = $this->document->createTextNode(self::phpToXml($value)); Chris@0: $element->appendChild($text); Chris@0: } Chris@0: $parent->appendChild($element); Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * Escapes arguments. Chris@0: * Chris@0: * @return array Chris@0: */ Chris@0: private function escape(array $arguments) Chris@0: { Chris@17: $args = []; Chris@0: foreach ($arguments as $k => $v) { Chris@17: if (\is_array($v)) { Chris@0: $args[$k] = $this->escape($v); Chris@17: } elseif (\is_string($v)) { Chris@0: $args[$k] = str_replace('%', '%%', $v); Chris@0: } else { Chris@0: $args[$k] = $v; Chris@0: } Chris@0: } Chris@0: Chris@0: return $args; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Converts php types to xml types. Chris@0: * Chris@0: * @param mixed $value Value to convert Chris@0: * Chris@0: * @return string Chris@0: * Chris@0: * @throws RuntimeException When trying to dump object or resource Chris@0: */ Chris@0: public static function phpToXml($value) Chris@0: { Chris@0: switch (true) { Chris@0: case null === $value: Chris@0: return 'null'; Chris@0: case true === $value: Chris@0: return 'true'; Chris@0: case false === $value: Chris@0: return 'false'; Chris@0: case $value instanceof Parameter: Chris@0: return '%'.$value.'%'; Chris@17: case \is_object($value) || \is_resource($value): Chris@0: throw new RuntimeException('Unable to dump a service container if a parameter is an object or a resource.'); Chris@0: default: Chris@0: return (string) $value; Chris@0: } Chris@0: } Chris@0: }