2015-08-17 17:00:26 -07:00
< ? php
/*
* This file is part of the Symfony package .
*
* ( c ) Fabien Potencier < fabien @ symfony . com >
*
* For the full copyright and license information , please view the LICENSE
* file that was distributed with this source code .
*/
namespace Symfony\Component\DependencyInjection\Loader ;
use Symfony\Component\Config\Util\XmlUtils ;
use Symfony\Component\DependencyInjection\Alias ;
2018-11-23 12:29:20 +00:00
use Symfony\Component\DependencyInjection\Argument\BoundArgument ;
use Symfony\Component\DependencyInjection\Argument\IteratorArgument ;
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument ;
use Symfony\Component\DependencyInjection\ChildDefinition ;
use Symfony\Component\DependencyInjection\ContainerBuilder ;
use Symfony\Component\DependencyInjection\ContainerInterface ;
2015-08-17 17:00:26 -07:00
use Symfony\Component\DependencyInjection\Definition ;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException ;
use Symfony\Component\DependencyInjection\Exception\RuntimeException ;
2018-11-23 12:29:20 +00:00
use Symfony\Component\DependencyInjection\Reference ;
2015-08-17 17:00:26 -07:00
use Symfony\Component\ExpressionLanguage\Expression ;
/**
* XmlFileLoader loads XML files service definitions .
*
* @ author Fabien Potencier < fabien @ symfony . com >
*/
class XmlFileLoader extends FileLoader
{
const NS = 'http://symfony.com/schema/dic/services' ;
/**
* { @ inheritdoc }
*/
public function load ( $resource , $type = null )
{
$path = $this -> locator -> locate ( $resource );
$xml = $this -> parseFileToDOM ( $path );
2018-11-23 12:29:20 +00:00
$this -> container -> fileExists ( $path );
$defaults = $this -> getServiceDefaults ( $xml , $path );
2015-08-17 17:00:26 -07:00
// anonymous services
2018-11-23 12:29:20 +00:00
$this -> processAnonymousServices ( $xml , $path , $defaults );
2015-08-17 17:00:26 -07:00
// imports
$this -> parseImports ( $xml , $path );
// parameters
2018-11-23 12:29:20 +00:00
$this -> parseParameters ( $xml , $path );
2015-08-17 17:00:26 -07:00
// extensions
$this -> loadFromExtensions ( $xml );
// services
2018-11-23 12:29:20 +00:00
try {
$this -> parseDefinitions ( $xml , $path , $defaults );
} finally {
$this -> instanceof = array ();
}
2015-08-17 17:00:26 -07:00
}
/**
* { @ inheritdoc }
*/
public function supports ( $resource , $type = null )
{
2018-11-23 12:29:20 +00:00
if ( ! \is_string ( $resource )) {
return false ;
}
if ( null === $type && 'xml' === pathinfo ( $resource , PATHINFO_EXTENSION )) {
return true ;
}
return 'xml' === $type ;
2015-08-17 17:00:26 -07:00
}
/**
* Parses parameters .
*
* @ param \DOMDocument $xml
2018-11-23 12:29:20 +00:00
* @ param string $file
2015-08-17 17:00:26 -07:00
*/
2018-11-23 12:29:20 +00:00
private function parseParameters ( \DOMDocument $xml , $file )
2015-08-17 17:00:26 -07:00
{
if ( $parameters = $this -> getChildren ( $xml -> documentElement , 'parameters' )) {
2018-11-23 12:29:20 +00:00
$this -> container -> getParameterBag () -> add ( $this -> getArgumentsAsPhp ( $parameters [ 0 ], 'parameter' , $file ));
2015-08-17 17:00:26 -07:00
}
}
/**
* Parses imports .
*
* @ param \DOMDocument $xml
* @ param string $file
*/
private function parseImports ( \DOMDocument $xml , $file )
{
$xpath = new \DOMXPath ( $xml );
$xpath -> registerNamespace ( 'container' , self :: NS );
if ( false === $imports = $xpath -> query ( '//container:imports/container:import' )) {
return ;
}
2018-11-23 12:29:20 +00:00
$defaultDirectory = \dirname ( $file );
2015-08-17 17:00:26 -07:00
foreach ( $imports as $import ) {
2016-04-20 09:56:34 -07:00
$this -> setCurrentDir ( $defaultDirectory );
2018-11-23 12:29:20 +00:00
$this -> import ( $import -> getAttribute ( 'resource' ), XmlUtils :: phpize ( $import -> getAttribute ( 'type' )) ? : null , ( bool ) XmlUtils :: phpize ( $import -> getAttribute ( 'ignore-errors' )), $file );
2015-08-17 17:00:26 -07:00
}
}
/**
* Parses multiple definitions .
*
* @ param \DOMDocument $xml
* @ param string $file
*/
2018-11-23 12:29:20 +00:00
private function parseDefinitions ( \DOMDocument $xml , $file , $defaults )
2015-08-17 17:00:26 -07:00
{
$xpath = new \DOMXPath ( $xml );
$xpath -> registerNamespace ( 'container' , self :: NS );
2018-11-23 12:29:20 +00:00
if ( false === $services = $xpath -> query ( '//container:services/container:service|//container:services/container:prototype' )) {
2015-08-17 17:00:26 -07:00
return ;
}
2018-11-23 12:29:20 +00:00
$this -> setCurrentDir ( \dirname ( $file ));
2015-08-17 17:00:26 -07:00
2018-11-23 12:29:20 +00:00
$this -> instanceof = array ();
$this -> isLoadingInstanceof = true ;
$instanceof = $xpath -> query ( '//container:services/container:instanceof' );
foreach ( $instanceof as $service ) {
$this -> setDefinition (( string ) $service -> getAttribute ( 'id' ), $this -> parseDefinition ( $service , $file , array ()));
}
$this -> isLoadingInstanceof = false ;
2015-08-17 17:00:26 -07:00
foreach ( $services as $service ) {
2018-11-23 12:29:20 +00:00
if ( null !== $definition = $this -> parseDefinition ( $service , $file , $defaults )) {
if ( 'prototype' === $service -> tagName ) {
$this -> registerClasses ( $definition , ( string ) $service -> getAttribute ( 'namespace' ), ( string ) $service -> getAttribute ( 'resource' ), ( string ) $service -> getAttribute ( 'exclude' ));
} else {
$this -> setDefinition (( string ) $service -> getAttribute ( 'id' ), $definition );
}
}
}
}
/**
* Get service defaults .
*
* @ return array
*/
private function getServiceDefaults ( \DOMDocument $xml , $file )
{
$xpath = new \DOMXPath ( $xml );
$xpath -> registerNamespace ( 'container' , self :: NS );
if ( null === $defaultsNode = $xpath -> query ( '//container:services/container:defaults' ) -> item ( 0 )) {
return array ();
}
$defaults = array (
'tags' => $this -> getChildren ( $defaultsNode , 'tag' ),
'bind' => array_map ( function ( $v ) { return new BoundArgument ( $v ); }, $this -> getArgumentsAsPhp ( $defaultsNode , 'bind' , $file )),
);
foreach ( $defaults [ 'tags' ] as $tag ) {
if ( '' === $tag -> getAttribute ( 'name' )) {
throw new InvalidArgumentException ( sprintf ( 'The tag name for tag "<defaults>" in %s must be a non-empty string.' , $file ));
2015-08-17 17:00:26 -07:00
}
}
2018-11-23 12:29:20 +00:00
if ( $defaultsNode -> hasAttribute ( 'autowire' )) {
$defaults [ 'autowire' ] = XmlUtils :: phpize ( $defaultsNode -> getAttribute ( 'autowire' ));
}
if ( $defaultsNode -> hasAttribute ( 'public' )) {
$defaults [ 'public' ] = XmlUtils :: phpize ( $defaultsNode -> getAttribute ( 'public' ));
}
if ( $defaultsNode -> hasAttribute ( 'autoconfigure' )) {
$defaults [ 'autoconfigure' ] = XmlUtils :: phpize ( $defaultsNode -> getAttribute ( 'autoconfigure' ));
}
return $defaults ;
2015-08-17 17:00:26 -07:00
}
/**
* Parses an individual Definition .
*
* @ param \DOMElement $service
2015-08-27 12:03:05 -07:00
* @ param string $file
2018-11-23 12:29:20 +00:00
* @ param array $defaults
2015-08-17 17:00:26 -07:00
*
* @ return Definition | null
*/
2018-11-23 12:29:20 +00:00
private function parseDefinition ( \DOMElement $service , $file , array $defaults )
2015-08-17 17:00:26 -07:00
{
if ( $alias = $service -> getAttribute ( 'alias' )) {
2018-11-23 12:29:20 +00:00
$this -> validateAlias ( $service , $file );
$this -> container -> setAlias (( string ) $service -> getAttribute ( 'id' ), $alias = new Alias ( $alias ));
2015-08-17 17:00:26 -07:00
if ( $publicAttr = $service -> getAttribute ( 'public' )) {
2018-11-23 12:29:20 +00:00
$alias -> setPublic ( XmlUtils :: phpize ( $publicAttr ));
} elseif ( isset ( $defaults [ 'public' ])) {
$alias -> setPublic ( $defaults [ 'public' ]);
2015-08-17 17:00:26 -07:00
}
return ;
}
2018-11-23 12:29:20 +00:00
if ( $this -> isLoadingInstanceof ) {
$definition = new ChildDefinition ( '' );
} elseif ( $parent = $service -> getAttribute ( 'parent' )) {
if ( ! empty ( $this -> instanceof )) {
throw new InvalidArgumentException ( sprintf ( 'The service "%s" cannot use the "parent" option in the same file where "instanceof" configuration is defined as using both is not supported. Move your child definitions to a separate file.' , $service -> getAttribute ( 'id' )));
}
foreach ( $defaults as $k => $v ) {
if ( 'tags' === $k ) {
// since tags are never inherited from parents, there is no confusion
// thus we can safely add them as defaults to ChildDefinition
continue ;
}
if ( 'bind' === $k ) {
if ( $defaults [ 'bind' ]) {
throw new InvalidArgumentException ( sprintf ( 'Bound values on service "%s" cannot be inherited from "defaults" when a "parent" is set. Move your child definitions to a separate file.' , $service -> getAttribute ( 'id' )));
}
continue ;
}
if ( ! $service -> hasAttribute ( $k )) {
throw new InvalidArgumentException ( sprintf ( 'Attribute "%s" on service "%s" cannot be inherited from "defaults" when a "parent" is set. Move your child definitions to a separate file or define this attribute explicitly.' , $k , $service -> getAttribute ( 'id' )));
}
}
$definition = new ChildDefinition ( $parent );
2015-08-17 17:00:26 -07:00
} else {
$definition = new Definition ();
2018-11-23 12:29:20 +00:00
if ( isset ( $defaults [ 'public' ])) {
$definition -> setPublic ( $defaults [ 'public' ]);
}
if ( isset ( $defaults [ 'autowire' ])) {
$definition -> setAutowired ( $defaults [ 'autowire' ]);
}
if ( isset ( $defaults [ 'autoconfigure' ])) {
$definition -> setAutoconfigured ( $defaults [ 'autoconfigure' ]);
}
$definition -> setChanges ( array ());
2015-08-17 17:00:26 -07:00
}
2018-11-23 12:29:20 +00:00
foreach ( array ( 'class' , 'public' , 'shared' , 'synthetic' , 'lazy' , 'abstract' ) as $key ) {
2015-08-17 17:00:26 -07:00
if ( $value = $service -> getAttribute ( $key )) {
2018-11-23 12:29:20 +00:00
$method = 'set' . $key ;
2015-08-17 17:00:26 -07:00
$definition -> $method ( XmlUtils :: phpize ( $value ));
}
}
2016-04-20 09:56:34 -07:00
if ( $value = $service -> getAttribute ( 'autowire' )) {
$definition -> setAutowired ( XmlUtils :: phpize ( $value ));
}
2018-11-23 12:29:20 +00:00
if ( $value = $service -> getAttribute ( 'autoconfigure' )) {
if ( ! $definition instanceof ChildDefinition ) {
$definition -> setAutoconfigured ( XmlUtils :: phpize ( $value ));
} elseif ( $value = XmlUtils :: phpize ( $value )) {
throw new InvalidArgumentException ( sprintf ( 'The service "%s" cannot have a "parent" and also have "autoconfigure". Try setting autoconfigure="false" for the service.' , $service -> getAttribute ( 'id' )));
2016-04-20 09:56:34 -07:00
}
2015-08-17 17:00:26 -07:00
}
if ( $files = $this -> getChildren ( $service , 'file' )) {
$definition -> setFile ( $files [ 0 ] -> nodeValue );
}
2016-04-20 09:56:34 -07:00
if ( $deprecated = $this -> getChildren ( $service , 'deprecated' )) {
2017-02-02 16:28:38 -08:00
$definition -> setDeprecated ( true , $deprecated [ 0 ] -> nodeValue ? : null );
2016-04-20 09:56:34 -07:00
}
2018-11-23 12:29:20 +00:00
$definition -> setArguments ( $this -> getArgumentsAsPhp ( $service , 'argument' , $file , false , $definition instanceof ChildDefinition ));
$definition -> setProperties ( $this -> getArgumentsAsPhp ( $service , 'property' , $file ));
2015-08-17 17:00:26 -07:00
if ( $factories = $this -> getChildren ( $service , 'factory' )) {
$factory = $factories [ 0 ];
if ( $function = $factory -> getAttribute ( 'function' )) {
$definition -> setFactory ( $function );
} else {
2018-11-23 12:29:20 +00:00
if ( $childService = $factory -> getAttribute ( 'service' )) {
$class = new Reference ( $childService , ContainerInterface :: EXCEPTION_ON_INVALID_REFERENCE );
2015-08-17 17:00:26 -07:00
} else {
2018-11-23 12:29:20 +00:00
$class = $factory -> hasAttribute ( 'class' ) ? $factory -> getAttribute ( 'class' ) : null ;
2015-08-17 17:00:26 -07:00
}
$definition -> setFactory ( array ( $class , $factory -> getAttribute ( 'method' )));
}
}
if ( $configurators = $this -> getChildren ( $service , 'configurator' )) {
$configurator = $configurators [ 0 ];
if ( $function = $configurator -> getAttribute ( 'function' )) {
$definition -> setConfigurator ( $function );
} else {
2018-11-23 12:29:20 +00:00
if ( $childService = $configurator -> getAttribute ( 'service' )) {
$class = new Reference ( $childService , ContainerInterface :: EXCEPTION_ON_INVALID_REFERENCE );
2015-08-17 17:00:26 -07:00
} else {
$class = $configurator -> getAttribute ( 'class' );
}
$definition -> setConfigurator ( array ( $class , $configurator -> getAttribute ( 'method' )));
}
}
foreach ( $this -> getChildren ( $service , 'call' ) as $call ) {
2018-11-23 12:29:20 +00:00
$definition -> addMethodCall ( $call -> getAttribute ( 'method' ), $this -> getArgumentsAsPhp ( $call , 'argument' , $file ));
2015-08-17 17:00:26 -07:00
}
2018-11-23 12:29:20 +00:00
$tags = $this -> getChildren ( $service , 'tag' );
if ( ! empty ( $defaults [ 'tags' ])) {
$tags = array_merge ( $tags , $defaults [ 'tags' ]);
}
foreach ( $tags as $tag ) {
2015-08-17 17:00:26 -07:00
$parameters = array ();
foreach ( $tag -> attributes as $name => $node ) {
if ( 'name' === $name ) {
continue ;
}
if ( false !== strpos ( $name , '-' ) && false === strpos ( $name , '_' ) && ! array_key_exists ( $normalizedName = str_replace ( '-' , '_' , $name ), $parameters )) {
$parameters [ $normalizedName ] = XmlUtils :: phpize ( $node -> nodeValue );
}
2018-11-23 12:29:20 +00:00
// keep not normalized key
2015-08-17 17:00:26 -07:00
$parameters [ $name ] = XmlUtils :: phpize ( $node -> nodeValue );
}
2016-04-20 09:56:34 -07:00
if ( '' === $tag -> getAttribute ( 'name' )) {
throw new InvalidArgumentException ( sprintf ( 'The tag name for service "%s" in %s must be a non-empty string.' , ( string ) $service -> getAttribute ( 'id' ), $file ));
}
2015-08-17 17:00:26 -07:00
$definition -> addTag ( $tag -> getAttribute ( 'name' ), $parameters );
}
2016-04-20 09:56:34 -07:00
foreach ( $this -> getChildren ( $service , 'autowiring-type' ) as $type ) {
$definition -> addAutowiringType ( $type -> textContent );
}
2018-11-23 12:29:20 +00:00
$bindings = $this -> getArgumentsAsPhp ( $service , 'bind' , $file );
if ( isset ( $defaults [ 'bind' ])) {
// deep clone, to avoid multiple process of the same instance in the passes
$bindings = array_merge ( unserialize ( serialize ( $defaults [ 'bind' ])), $bindings );
}
if ( $bindings ) {
$definition -> setBindings ( $bindings );
}
2015-08-17 17:00:26 -07:00
if ( $value = $service -> getAttribute ( 'decorates' )) {
$renameId = $service -> hasAttribute ( 'decoration-inner-name' ) ? $service -> getAttribute ( 'decoration-inner-name' ) : null ;
2016-04-20 09:56:34 -07:00
$priority = $service -> hasAttribute ( 'decoration-priority' ) ? $service -> getAttribute ( 'decoration-priority' ) : 0 ;
$definition -> setDecoratedService ( $value , $renameId , $priority );
2015-08-17 17:00:26 -07:00
}
return $definition ;
}
/**
2015-08-27 12:03:05 -07:00
* Parses a XML file to a \DOMDocument .
2015-08-17 17:00:26 -07:00
*
* @ param string $file Path to a file
*
* @ return \DOMDocument
*
* @ throws InvalidArgumentException When loading of XML file returns error
*/
private function parseFileToDOM ( $file )
{
try {
$dom = XmlUtils :: loadFile ( $file , array ( $this , 'validateSchema' ));
} catch ( \InvalidArgumentException $e ) {
throw new InvalidArgumentException ( sprintf ( 'Unable to parse file "%s".' , $file ), $e -> getCode (), $e );
}
$this -> validateExtensions ( $dom , $file );
return $dom ;
}
/**
* Processes anonymous services .
*
* @ param \DOMDocument $xml
* @ param string $file
2018-11-23 12:29:20 +00:00
* @ param array $defaults
2015-08-17 17:00:26 -07:00
*/
2018-11-23 12:29:20 +00:00
private function processAnonymousServices ( \DOMDocument $xml , $file , $defaults )
2015-08-17 17:00:26 -07:00
{
$definitions = array ();
$count = 0 ;
2018-11-23 12:29:20 +00:00
$suffix = '~' . ContainerBuilder :: hash ( $file );
2015-08-17 17:00:26 -07:00
$xpath = new \DOMXPath ( $xml );
$xpath -> registerNamespace ( 'container' , self :: NS );
// anonymous services as arguments/properties
2018-11-23 12:29:20 +00:00
if ( false !== $nodes = $xpath -> query ( '//container:argument[@type="service"][not(@id)]|//container:property[@type="service"][not(@id)]|//container:bind[not(@id)]|//container:factory[not(@service)]|//container:configurator[not(@service)]' )) {
2015-08-17 17:00:26 -07:00
foreach ( $nodes as $node ) {
if ( $services = $this -> getChildren ( $node , 'service' )) {
2018-11-23 12:29:20 +00:00
// give it a unique name
$id = sprintf ( '%d_%s' , ++ $count , preg_replace ( '/^.*\\\\/' , '' , $services [ 0 ] -> getAttribute ( 'class' )) . $suffix );
$node -> setAttribute ( 'id' , $id );
$node -> setAttribute ( 'service' , $id );
2015-08-17 17:00:26 -07:00
$definitions [ $id ] = array ( $services [ 0 ], $file , false );
$services [ 0 ] -> setAttribute ( 'id' , $id );
2017-02-02 16:28:38 -08:00
// anonymous services are always private
// we could not use the constant false here, because of XML parsing
$services [ 0 ] -> setAttribute ( 'public' , 'false' );
2015-08-17 17:00:26 -07:00
}
}
}
// anonymous services "in the wild"
if ( false !== $nodes = $xpath -> query ( '//container:services/container:service[not(@id)]' )) {
foreach ( $nodes as $node ) {
2018-11-23 12:29:20 +00:00
@ trigger_error ( sprintf ( 'Top-level anonymous services are deprecated since Symfony 3.4, the "id" attribute will be required in version 4.0 in %s at line %d.' , $file , $node -> getLineNo ()), E_USER_DEPRECATED );
2015-08-17 17:00:26 -07:00
// give it a unique name
2018-11-23 12:29:20 +00:00
$id = sprintf ( '%d_%s' , ++ $count , preg_replace ( '/^.*\\\\/' , '' , $node -> getAttribute ( 'class' )) . $suffix );
2015-08-17 17:00:26 -07:00
$node -> setAttribute ( 'id' , $id );
2017-02-02 16:28:38 -08:00
$definitions [ $id ] = array ( $node , $file , true );
2015-08-17 17:00:26 -07:00
}
}
// resolve definitions
2018-11-23 12:29:20 +00:00
uksort ( $definitions , 'strnatcmp' );
foreach ( array_reverse ( $definitions ) as $id => list ( $domElement , $file , $wild )) {
if ( null !== $definition = $this -> parseDefinition ( $domElement , $file , $wild ? $defaults : array ())) {
$this -> setDefinition ( $id , $definition );
2015-08-17 17:00:26 -07:00
}
if ( true === $wild ) {
$tmpDomElement = new \DOMElement ( '_services' , null , self :: NS );
$domElement -> parentNode -> replaceChild ( $tmpDomElement , $domElement );
$tmpDomElement -> setAttribute ( 'id' , $id );
}
}
}
/**
* Returns arguments as valid php types .
*
* @ param \DOMElement $node
* @ param string $name
2018-11-23 12:29:20 +00:00
* @ param string $file
2015-08-17 17:00:26 -07:00
* @ param bool $lowercase
*
* @ return mixed
*/
2018-11-23 12:29:20 +00:00
private function getArgumentsAsPhp ( \DOMElement $node , $name , $file , $lowercase = true , $isChildDefinition = false )
2015-08-17 17:00:26 -07:00
{
$arguments = array ();
foreach ( $this -> getChildren ( $node , $name ) as $arg ) {
if ( $arg -> hasAttribute ( 'name' )) {
$arg -> setAttribute ( 'key' , $arg -> getAttribute ( 'name' ));
}
2018-11-23 12:29:20 +00:00
// this is used by ChildDefinition to overwrite a specific
2015-08-17 17:00:26 -07:00
// argument of the parent definition
if ( $arg -> hasAttribute ( 'index' )) {
2018-11-23 12:29:20 +00:00
$key = ( $isChildDefinition ? 'index_' : '' ) . $arg -> getAttribute ( 'index' );
2017-02-02 16:28:38 -08:00
} elseif ( ! $arg -> hasAttribute ( 'key' )) {
// Append an empty argument, then fetch its key to overwrite it later
$arguments [] = null ;
$keys = array_keys ( $arguments );
$key = array_pop ( $keys );
} else {
$key = $arg -> getAttribute ( 'key' );
2018-11-23 12:29:20 +00:00
}
2017-02-02 16:28:38 -08:00
2018-11-23 12:29:20 +00:00
$onInvalid = $arg -> getAttribute ( 'on-invalid' );
$invalidBehavior = ContainerInterface :: EXCEPTION_ON_INVALID_REFERENCE ;
if ( 'ignore' == $onInvalid ) {
$invalidBehavior = ContainerInterface :: IGNORE_ON_INVALID_REFERENCE ;
} elseif ( 'ignore_uninitialized' == $onInvalid ) {
$invalidBehavior = ContainerInterface :: IGNORE_ON_UNINITIALIZED_REFERENCE ;
} elseif ( 'null' == $onInvalid ) {
$invalidBehavior = ContainerInterface :: NULL_ON_INVALID_REFERENCE ;
2015-08-17 17:00:26 -07:00
}
switch ( $arg -> getAttribute ( 'type' )) {
case 'service' :
2018-11-23 12:29:20 +00:00
if ( ! $arg -> getAttribute ( 'id' )) {
throw new InvalidArgumentException ( sprintf ( 'Tag "<%s>" with type="service" has no or empty "id" attribute in "%s".' , $name , $file ));
2015-08-17 17:00:26 -07:00
}
2018-11-23 12:29:20 +00:00
if ( $arg -> hasAttribute ( 'strict' )) {
@ trigger_error ( sprintf ( 'The "strict" attribute used when referencing the "%s" service is deprecated since Symfony 3.3 and will be removed in 4.0.' , $arg -> getAttribute ( 'id' )), E_USER_DEPRECATED );
2015-08-17 17:00:26 -07:00
}
2018-11-23 12:29:20 +00:00
$arguments [ $key ] = new Reference ( $arg -> getAttribute ( 'id' ), $invalidBehavior );
2015-08-17 17:00:26 -07:00
break ;
case 'expression' :
2018-11-23 12:29:20 +00:00
if ( ! class_exists ( Expression :: class )) {
throw new \LogicException ( sprintf ( 'The type="expression" attribute cannot be used without the ExpressionLanguage component. Try running "composer require symfony/expression-language".' ));
}
2015-08-17 17:00:26 -07:00
$arguments [ $key ] = new Expression ( $arg -> nodeValue );
break ;
case 'collection' :
2018-11-23 12:29:20 +00:00
$arguments [ $key ] = $this -> getArgumentsAsPhp ( $arg , $name , $file , false );
break ;
case 'iterator' :
$arg = $this -> getArgumentsAsPhp ( $arg , $name , $file , false );
try {
$arguments [ $key ] = new IteratorArgument ( $arg );
} catch ( InvalidArgumentException $e ) {
throw new InvalidArgumentException ( sprintf ( 'Tag "<%s>" with type="iterator" only accepts collections of type="service" references in "%s".' , $name , $file ));
}
break ;
case 'tagged' :
if ( ! $arg -> getAttribute ( 'tag' )) {
throw new InvalidArgumentException ( sprintf ( 'Tag "<%s>" with type="tagged" has no or empty "tag" attribute in "%s".' , $name , $file ));
}
$arguments [ $key ] = new TaggedIteratorArgument ( $arg -> getAttribute ( 'tag' ));
2015-08-17 17:00:26 -07:00
break ;
case 'string' :
$arguments [ $key ] = $arg -> nodeValue ;
break ;
case 'constant' :
2018-11-23 12:29:20 +00:00
$arguments [ $key ] = \constant ( trim ( $arg -> nodeValue ));
2015-08-17 17:00:26 -07:00
break ;
default :
$arguments [ $key ] = XmlUtils :: phpize ( $arg -> nodeValue );
}
}
return $arguments ;
}
/**
2015-08-27 12:03:05 -07:00
* Get child elements by name .
2015-08-17 17:00:26 -07:00
*
* @ param \DOMNode $node
* @ param mixed $name
*
* @ return array
*/
private function getChildren ( \DOMNode $node , $name )
{
$children = array ();
foreach ( $node -> childNodes as $child ) {
2018-11-23 12:29:20 +00:00
if ( $child instanceof \DOMElement && $child -> localName === $name && self :: NS === $child -> namespaceURI ) {
2015-08-17 17:00:26 -07:00
$children [] = $child ;
}
}
return $children ;
}
/**
* Validates a documents XML schema .
*
* @ param \DOMDocument $dom
*
* @ return bool
*
* @ throws RuntimeException When extension references a non - existent XSD file
*/
public function validateSchema ( \DOMDocument $dom )
{
$schemaLocations = array ( 'http://symfony.com/schema/dic/services' => str_replace ( '\\' , '/' , __DIR__ . '/schema/dic/services/services-1.0.xsd' ));
if ( $element = $dom -> documentElement -> getAttributeNS ( 'http://www.w3.org/2001/XMLSchema-instance' , 'schemaLocation' )) {
$items = preg_split ( '/\s+/' , $element );
2018-11-23 12:29:20 +00:00
for ( $i = 0 , $nb = \count ( $items ); $i < $nb ; $i += 2 ) {
2015-08-17 17:00:26 -07:00
if ( ! $this -> container -> hasExtension ( $items [ $i ])) {
continue ;
}
if (( $extension = $this -> container -> getExtension ( $items [ $i ])) && false !== $extension -> getXsdValidationBasePath ()) {
$path = str_replace ( $extension -> getNamespace (), str_replace ( '\\' , '/' , $extension -> getXsdValidationBasePath ()) . '/' , $items [ $i + 1 ]);
if ( ! is_file ( $path )) {
2018-11-23 12:29:20 +00:00
throw new RuntimeException ( sprintf ( 'Extension "%s" references a non-existent XSD file "%s"' , \get_class ( $extension ), $path ));
2015-08-17 17:00:26 -07:00
}
$schemaLocations [ $items [ $i ]] = $path ;
}
}
}
$tmpfiles = array ();
$imports = '' ;
foreach ( $schemaLocations as $namespace => $location ) {
$parts = explode ( '/' , $location );
2018-11-23 12:29:20 +00:00
$locationstart = 'file:///' ;
2015-08-17 17:00:26 -07:00
if ( 0 === stripos ( $location , 'phar://' )) {
2018-11-23 12:29:20 +00:00
$tmpfile = tempnam ( sys_get_temp_dir (), 'symfony' );
2015-08-17 17:00:26 -07:00
if ( $tmpfile ) {
copy ( $location , $tmpfile );
$tmpfiles [] = $tmpfile ;
$parts = explode ( '/' , str_replace ( '\\' , '/' , $tmpfile ));
2018-11-23 12:29:20 +00:00
} else {
array_shift ( $parts );
$locationstart = 'phar:///' ;
2015-08-17 17:00:26 -07:00
}
}
2018-11-23 12:29:20 +00:00
$drive = '\\' === \DIRECTORY_SEPARATOR ? array_shift ( $parts ) . '/' : '' ;
$location = $locationstart . $drive . implode ( '/' , array_map ( 'rawurlencode' , $parts ));
2015-08-17 17:00:26 -07:00
$imports .= sprintf ( ' <xsd:import namespace="%s" schemaLocation="%s" />' . " \n " , $namespace , $location );
}
$source = <<< EOF
< ? xml version = " 1.0 " encoding = " utf-8 " ?>
< xsd : schema xmlns = " http://symfony.com/schema "
xmlns : xsd = " http://www.w3.org/2001/XMLSchema "
targetNamespace = " http://symfony.com/schema "
elementFormDefault = " qualified " >
< xsd : import namespace = " http://www.w3.org/XML/1998/namespace " />
$imports
</ xsd : schema >
EOF
;
2017-02-02 16:28:38 -08:00
$disableEntities = libxml_disable_entity_loader ( false );
2015-08-17 17:00:26 -07:00
$valid = @ $dom -> schemaValidateSource ( $source );
2017-02-02 16:28:38 -08:00
libxml_disable_entity_loader ( $disableEntities );
2015-08-17 17:00:26 -07:00
foreach ( $tmpfiles as $tmpfile ) {
@ unlink ( $tmpfile );
}
return $valid ;
}
2018-11-23 12:29:20 +00:00
/**
* Validates an alias .
*
* @ param \DOMElement $alias
* @ param string $file
*/
private function validateAlias ( \DOMElement $alias , $file )
{
foreach ( $alias -> attributes as $name => $node ) {
if ( ! \in_array ( $name , array ( 'alias' , 'id' , 'public' ))) {
@ trigger_error ( sprintf ( 'Using the attribute "%s" is deprecated for the service "%s" which is defined as an alias in "%s". Allowed attributes for service aliases are "alias", "id" and "public". The XmlFileLoader will raise an exception in Symfony 4.0, instead of silently ignoring unsupported attributes.' , $name , $alias -> getAttribute ( 'id' ), $file ), E_USER_DEPRECATED );
}
}
foreach ( $alias -> childNodes as $child ) {
if ( $child instanceof \DOMElement && self :: NS === $child -> namespaceURI ) {
@ trigger_error ( sprintf ( 'Using the element "%s" is deprecated for the service "%s" which is defined as an alias in "%s". The XmlFileLoader will raise an exception in Symfony 4.0, instead of silently ignoring unsupported elements.' , $child -> localName , $alias -> getAttribute ( 'id' ), $file ), E_USER_DEPRECATED );
}
}
}
2015-08-17 17:00:26 -07:00
/**
* Validates an extension .
*
* @ param \DOMDocument $dom
* @ param string $file
*
* @ throws InvalidArgumentException When no extension is found corresponding to a tag
*/
private function validateExtensions ( \DOMDocument $dom , $file )
{
foreach ( $dom -> documentElement -> childNodes as $node ) {
if ( ! $node instanceof \DOMElement || 'http://symfony.com/schema/dic/services' === $node -> namespaceURI ) {
continue ;
}
// can it be handled by an extension?
if ( ! $this -> container -> hasExtension ( $node -> namespaceURI )) {
$extensionNamespaces = array_filter ( array_map ( function ( $ext ) { return $ext -> getNamespace (); }, $this -> container -> getExtensions ()));
2018-11-23 12:29:20 +00:00
throw new InvalidArgumentException ( sprintf ( 'There is no extension able to load the configuration for "%s" (in %s). Looked for namespace "%s", found %s' , $node -> tagName , $file , $node -> namespaceURI , $extensionNamespaces ? sprintf ( '"%s"' , implode ( '", "' , $extensionNamespaces )) : 'none' ));
2015-08-17 17:00:26 -07:00
}
}
}
/**
* Loads from an extension .
*
* @ param \DOMDocument $xml
*/
private function loadFromExtensions ( \DOMDocument $xml )
{
foreach ( $xml -> documentElement -> childNodes as $node ) {
2018-11-23 12:29:20 +00:00
if ( ! $node instanceof \DOMElement || self :: NS === $node -> namespaceURI ) {
2015-08-17 17:00:26 -07:00
continue ;
}
$values = static :: convertDomElementToArray ( $node );
2018-11-23 12:29:20 +00:00
if ( ! \is_array ( $values )) {
2015-08-17 17:00:26 -07:00
$values = array ();
}
$this -> container -> loadFromExtension ( $node -> namespaceURI , $values );
}
}
/**
2018-11-23 12:29:20 +00:00
* Converts a \DOMElement object to a PHP array .
2015-08-17 17:00:26 -07:00
*
* The following rules applies during the conversion :
*
* * Each tag is converted to a key value or an array
* if there is more than one " value "
*
* * The content of a tag is set under a " value " key ( < foo > bar </ foo > )
* if the tag also has some nested tags
*
* * The attributes are converted to keys ( < foo foo = " bar " /> )
*
* * The nested - tags are converted to keys ( < foo >< foo > bar </ foo ></ foo > )
*
2018-11-23 12:29:20 +00:00
* @ param \DOMElement $element A \DOMElement instance
2015-08-17 17:00:26 -07:00
*
* @ return array A PHP array
*/
2017-02-02 16:28:38 -08:00
public static function convertDomElementToArray ( \DOMElement $element )
2015-08-17 17:00:26 -07:00
{
return XmlUtils :: convertDomElementToArray ( $element );
}
}