Вход Регистрация
Файл: symfony-2.7/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php
Строк: 752
<?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 SymfonyComponentDependencyInjectionLoader;

use 
SymfonyComponentConfigResourceFileResource;
use 
SymfonyComponentConfigUtilXmlUtils;
use 
SymfonyComponentDependencyInjectionDefinitionDecorator;
use 
SymfonyComponentDependencyInjectionContainerInterface;
use 
SymfonyComponentDependencyInjectionAlias;
use 
SymfonyComponentDependencyInjectionDefinition;
use 
SymfonyComponentDependencyInjectionReference;
use 
SymfonyComponentDependencyInjectionExceptionInvalidArgumentException;
use 
SymfonyComponentDependencyInjectionExceptionRuntimeException;
use 
SymfonyComponentExpressionLanguageExpression;

/**
 * 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);

        
$this->container->addResource(new FileResource($path));

        
// anonymous services
        
$this->processAnonymousServices($xml$path);

        
// imports
        
$this->parseImports($xml$path);

        
// parameters
        
$this->parseParameters($xml$path);

        
// extensions
        
$this->loadFromExtensions($xml);

        
// services
        
$this->parseDefinitions($xml$path);
    }

    
/**
     * {@inheritdoc}
     */
    
public function supports($resource$type null)
    {
        return 
is_string($resource) && 'xml' === pathinfo($resourcePATHINFO_EXTENSION);
    }

    
/**
     * Parses parameters.
     *
     * @param DOMDocument $xml
     * @param string       $file
     */
    
private function parseParameters(DOMDocument $xml$file)
    {
        if (
$parameters $this->getChildren($xml->documentElement'parameters')) {
            
$this->container->getParameterBag()->add($this->getArgumentsAsPhp($parameters[0], 'parameter'));
        }
    }

    
/**
     * 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;
        }

        foreach (
$imports as $import) {
            
$this->setCurrentDir(dirname($file));
            
$this->import($import->getAttribute('resource'), null, (bool) XmlUtils::phpize($import->getAttribute('ignore-errors')), $file);
        }
    }

    
/**
     * Parses multiple definitions.
     *
     * @param DOMDocument $xml
     * @param string       $file
     */
    
private function parseDefinitions(DOMDocument $xml$file)
    {
        
$xpath = new DOMXPath($xml);
        
$xpath->registerNamespace('container'self::NS);

        if (
false === $services $xpath->query('//container:services/container:service')) {
            return;
        }

        foreach (
$services as $service) {
            
$this->parseDefinition((string) $service->getAttribute('id'), $service$file);
        }
    }

    
/**
     * Parses an individual Definition.
     *
     * @param string      $id
     * @param DOMElement $service
     * @param string      $file
     */
    
private function parseDefinition($idDOMElement $service$file)
    {
        if (
$alias $service->getAttribute('alias')) {
            
$public true;
            if (
$publicAttr $service->getAttribute('public')) {
                
$public XmlUtils::phpize($publicAttr);
            }
            
$this->container->setAlias($id, new Alias($alias$public));

            return;
        }

        if (
$parent $service->getAttribute('parent')) {
            
$definition = new DefinitionDecorator($parent);
        } else {
            
$definition = new Definition();
        }

        foreach (array(
'class''scope''public''factory-class''factory-method''factory-service''synthetic''lazy''abstract') as $key) {
            if (
$value $service->getAttribute($key)) {
                
$method 'set'.str_replace('-'''$key);
                
$definition->$method(XmlUtils::phpize($value));
            }
        }

        if (
$value $service->getAttribute('synchronized')) {
            
$definition->setSynchronized(XmlUtils::phpize($value), 'request' !== $id);
        }

        if (
$files $this->getChildren($service'file')) {
            
$definition->setFile($files[0]->nodeValue);
        }

        
$definition->setArguments($this->getArgumentsAsPhp($service'argument'));
        
$definition->setProperties($this->getArgumentsAsPhp($service'property'));

        if (
$factories $this->getChildren($service'factory')) {
            
$factory $factories[0];
            if (
$function $factory->getAttribute('function')) {
                
$definition->setFactory($function);
            } else {
                if (
$childService $factory->getAttribute('service')) {
                    
$class = new Reference($childServiceContainerInterface::EXCEPTION_ON_INVALID_REFERENCEfalse);
                } else {
                    
$class $factory->getAttribute('class');
                }

                
$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 {
                if (
$childService $configurator->getAttribute('service')) {
                    
$class = new Reference($childServiceContainerInterface::EXCEPTION_ON_INVALID_REFERENCEfalse);
                } else {
                    
$class $configurator->getAttribute('class');
                }

                
$definition->setConfigurator(array($class$configurator->getAttribute('method')));
            }
        }

        foreach (
$this->getChildren($service'call') as $call) {
            
$definition->addMethodCall($call->getAttribute('method'), $this->getArgumentsAsPhp($call'argument'));
        }

        foreach (
$this->getChildren($service'tag') as $tag) {
            
$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);
                }
                
// keep not normalized key for BC too
                
$parameters[$name] = XmlUtils::phpize($node->nodeValue);
            }

            
$definition->addTag($tag->getAttribute('name'), $parameters);
        }

        if (
$value $service->getAttribute('decorates')) {
            
$renameId $service->hasAttribute('decoration-inner-name') ? $service->getAttribute('decoration-inner-name') : null;
            
$definition->setDecoratedService($value$renameId);
        }

        
$this->container->setDefinition($id$definition);
    }

    
/**
     * Parses a XML file to a DOMDocument
     *
     * @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
     */
    
private function processAnonymousServices(DOMDocument $xml$file)
    {
        
$definitions = array();
        
$count 0;

        
$xpath = new DOMXPath($xml);
        
$xpath->registerNamespace('container'self::NS);

        
// anonymous services as arguments/properties
        
if (false !== $nodes $xpath->query('//container:argument[@type="service"][not(@id)]|//container:property[@type="service"][not(@id)]')) {
            foreach (
$nodes as $node) {
                
// give it a unique name
                
$id sprintf('%s_%d'hash('sha256'$file), ++$count);
                
$node->setAttribute('id'$id);

                if (
$services $this->getChildren($node'service')) {
                    
$definitions[$id] = array($services[0], $filefalse);
                    
$services[0]->setAttribute('id'$id);
                }
            }
        }

        
// anonymous services "in the wild"
        
if (false !== $nodes $xpath->query('//container:services/container:service[not(@id)]')) {
            foreach (
$nodes as $node) {
                
// give it a unique name
                
$id sprintf('%s_%d'hash('sha256'$file), ++$count);
                
$node->setAttribute('id'$id);

                if (
$services $this->getChildren($node'service')) {
                    
$definitions[$id] = array($node$filetrue);
                    
$services[0]->setAttribute('id'$id);
                }
            }
        }

        
// resolve definitions
        
krsort($definitions);
        foreach (
$definitions as $id => $def) {
            list(
$domElement$file$wild) = $def;

            
// anonymous services are always private
            // we could not use the constant false here, because of XML parsing
            
$domElement->setAttribute('public''false');

            
$this->parseDefinition($id$domElement$file);

            if (
true === $wild) {
                
$tmpDomElement = new DOMElement('_services'nullself::NS);
                
$domElement->parentNode->replaceChild($tmpDomElement$domElement);
                
$tmpDomElement->setAttribute('id'$id);
            } else {
                
$domElement->parentNode->removeChild($domElement);
            }
        }
    }

    
/**
     * Returns arguments as valid php types.
     *
     * @param DOMElement $node
     * @param string      $name
     * @param bool        $lowercase
     *
     * @return mixed
     */
    
private function getArgumentsAsPhp(DOMElement $node$name$lowercase true)
    {
        
$arguments = array();
        foreach (
$this->getChildren($node$name) as $arg) {
            if (
$arg->hasAttribute('name')) {
                
$arg->setAttribute('key'$arg->getAttribute('name'));
            }

            if (!
$arg->hasAttribute('key')) {
                
$key = !$arguments max(array_keys($arguments)) + 1;
            } else {
                
$key $arg->getAttribute('key');
            }

            
// parameter keys are case insensitive
            
if ('parameter' == $name && $lowercase) {
                
$key strtolower($key);
            }

            
// this is used by DefinitionDecorator to overwrite a specific
            // argument of the parent definition
            
if ($arg->hasAttribute('index')) {
                
$key 'index_'.$arg->getAttribute('index');
            }

            switch (
$arg->getAttribute('type')) {
                case 
'service':
                    
$onInvalid $arg->getAttribute('on-invalid');
                    
$invalidBehavior ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE;
                    if (
'ignore' == $onInvalid) {
                        
$invalidBehavior ContainerInterface::IGNORE_ON_INVALID_REFERENCE;
                    } elseif (
'null' == $onInvalid) {
                        
$invalidBehavior ContainerInterface::NULL_ON_INVALID_REFERENCE;
                    }

                    if (
$strict $arg->getAttribute('strict')) {
                        
$strict XmlUtils::phpize($strict);
                    } else {
                        
$strict true;
                    }

                    
$arguments[$key] = new Reference($arg->getAttribute('id'), $invalidBehavior$strict);
                    break;
                case 
'expression':
                    
$arguments[$key] = new Expression($arg->nodeValue);
                    break;
                case 
'collection':
                    
$arguments[$key] = $this->getArgumentsAsPhp($arg$namefalse);
                    break;
                case 
'string':
                    
$arguments[$key] = $arg->nodeValue;
                    break;
                case 
'constant':
                    
$arguments[$key] = constant($arg->nodeValue);
                    break;
                default:
                    
$arguments[$key] = XmlUtils::phpize($arg->nodeValue);
            }
        }

        return 
$arguments;
    }

    
/**
     * Get child elements by name
     *
     * @param DOMNode $node
     * @param mixed    $name
     *
     * @return array
     */
    
private function getChildren(DOMNode $node$name)
    {
        
$children = array();
        foreach (
$node->childNodes as $child) {
            if (
$child instanceof DOMElement && $child->localName === $name && $child->namespaceURI === self::NS) {
                
$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);
            for (
$i 0$nb count($items); $i $nb$i += 2) {
                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)) {
                        throw new RuntimeException(sprintf('
Extension "%s" references a non-existent XSD file "%s"', get_class($extension), $path));
                    }

                    $schemaLocations[$items[$i]] = $path;
                }
            }
        }

        $tmpfiles = array();
        $imports = '';
        foreach ($schemaLocations as $namespace => $location) {
            $parts = explode('
/', $location);
            if (0 === stripos($location, '
phar://')) {
                
$tmpfile tempnam(sys_get_temp_dir(), 'sf2');
                if (
$tmpfile) {
                    
copy($location$tmpfile);
                    
$tmpfiles[] = $tmpfile;
                    
$parts explode('/'str_replace('\', '/', $tmpfile));
                }
            }
            $drive = '
\' === DIRECTORY_SEPARATOR ? array_shift($parts).'/' : '';
            $location = '
file:///'.$drive.implode('/', array_map('rawurlencode', $parts));

            
$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
        ;

        
$valid = @$dom->schemaValidateSource($source);

        foreach (
$tmpfiles as $tmpfile) {
            @
unlink($tmpfile);
        }

        return 
$valid;
    }

    
/**
     * 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()));
                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'
                
));
            }
        }
    }

    
/**
     * Loads from an extension.
     *
     * @param DOMDocument $xml
     */
    
private function loadFromExtensions(DOMDocument $xml)
    {
        foreach (
$xml->documentElement->childNodes as $node) {
            if (!
$node instanceof DOMElement || $node->namespaceURI === self::NS) {
                continue;
            }

            
$values = static::convertDomElementToArray($node);
            if (!
is_array($values)) {
                
$values = array();
            }

            
$this->container->loadFromExtension($node->namespaceURI$values);
        }
    }

    
/**
     * Converts a DomElement object to a PHP array.
     *
     * 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>)
     *
     * @param DomElement $element A DomElement instance
     *
     * @return array A PHP array
     */
    
public static function convertDomElementToArray(DomElement $element)
    {
        return 
XmlUtils::convertDomElementToArray($element);
    }
}
Онлайн: 2
Реклама