Commit bfd7420c authored by catch's avatar catch
Browse files

Issue #2269385 by sun: Fixed DependencyInjection YamlFileLoader is out of date.

parent 8327533e
Loading
Loading
Loading
Loading
+121 −70
Changes for core/lib/Drupal/Core/DependencyInjection/YamlFileLoader.php: 121 added lines, 70 removed lines.
Original line number Diff line number Diff line
@@ -13,20 +13,30 @@
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;

/**
 * YamlFileLoader loads YAML files service definitions.
 *
 * Drupal does not use Symfony's Config component, so this is a partial copy
 * of \Symfony\Component\DependencyInjection\Loader\YamlFileLoader class not
 * depending on the Config component.
 * Drupal does not use Symfony's Config component, and Symfony's dependency on
 * it cannot be removed easily. Therefore, this is a partial but mostly literal
 * copy of upstream, which does not depend on the Config component.
 *
 * @see \Symfony\Component\DependencyInjection\Loader\YamlFileLoader
 * @see https://github.com/symfony/symfony/pull/10920
 *
 * NOTE: 98% of this code is a literal copy of Symfony's YamlFileLoader.
 *
 * This file does NOT follow Drupal coding standards, so as to simplify future
 * synchronizations.
 */
class YamlFileLoader {
class YamlFileLoader
{

    /**
     * Statically cached yaml files.
     *
   * Especially during tests, yaml files are re-parsed often.
     * Especially during tests, YAML files are re-parsed often.
     *
     * @var array
     */
@@ -37,61 +47,92 @@ class YamlFileLoader {
     */
    protected $container;

  public function __construct(ContainerBuilder $container) {
    public function __construct(ContainerBuilder $container)
    {
      $this->container = $container;
    }

    /**
   * Load a YAML file containing service definitions and kernel parameters.
     * Loads a Yaml file.
     *
   * string $filename
   *   The name of the file to load.
     * @param mixed  $file The resource
     */
  public function load($filename) {
    if (!isset(static::$yaml[$filename])) {
      static::$yaml[$filename] = $this->loadFile($filename);
    public function load($file)
    {
        if (!isset(static::$yaml[$file])) {
          static::$yaml[$file] = $this->loadFile($file);
        }
    $content = static::$yaml[$filename];
    $content += array('parameters' => array(), 'services' => array());
        $content = static::$yaml[$file];

        // Not supported.
        //$this->container->addResource(new FileResource($path));

        // empty file
        if (null === $content) {
            return;
        }

        // imports
        // Not supported.
        //$this->parseImports($content, $file);

        // parameters
        if (isset($content['parameters'])) {
            foreach ($content['parameters'] as $key => $value) {
                $this->container->setParameter($key, $this->resolveServices($value));
            }
        }

        // extensions
        // Not supported.
        //$this->loadFromExtensions($content);

        // services
        $this->parseDefinitions($content, $file);
    }

    /**
     * Parses definitions
     *
     * @param array  $content
     * @param string $file
     */
    private function parseDefinitions($content, $file)
    {
        if (!isset($content['services'])) {
            return;
        }

        foreach ($content['services'] as $id => $service) {
      $this->parseDefinition($id, $service, $filename);
            $this->parseDefinition($id, $service, $file);
        }
    }

    /**
     * Parses a definition.
     *
   * Copied from \Symfony\Component\DependencyInjection\Loader\YamlFileLoader::parseDefinition().
   *
     * @param string $id
   *   The id of the service.
   * @param string|array $service
   *   Either a string starting with a @ meaning this service is an alias or
   *   the array defining the service.
   * @param string $filename
   *   The name of the file, only used in error messages.
     * @param array  $service
     * @param string $file
     *
   * @throws \InvalidArgumentException When tags are invalid
     * @throws InvalidArgumentException When tags are invalid
     */
  protected function parseDefinition($id, $service, $filename) {
    private function parseDefinition($id, $service, $file)
    {
        if (is_string($service) && 0 === strpos($service, '@')) {
            $this->container->setAlias($id, substr($service, 1));

            return;
    }
    elseif (isset($service['alias'])) {
        } elseif (isset($service['alias'])) {
            $public = !array_key_exists('public', $service) || (Boolean) $service['public'];
            $this->container->setAlias($id, new Alias($service['alias'], $public));

            return;
        }

        if (isset($service['parent'])) {
            $definition = new DefinitionDecorator($service['parent']);
    }
    else {
        } else {
            $definition = new Definition();
        }

@@ -111,6 +152,10 @@ protected function parseDefinition($id, $service, $filename) {
            $definition->setSynchronized($service['synchronized']);
        }

        if (isset($service['lazy'])) {
            $definition->setLazy($service['lazy']);
        }

        if (isset($service['public'])) {
            $definition->setPublic($service['public']);
        }
@@ -146,8 +191,7 @@ protected function parseDefinition($id, $service, $filename) {
        if (isset($service['configurator'])) {
            if (is_string($service['configurator'])) {
                $definition->setConfigurator($service['configurator']);
      }
      else {
            } else {
                $definition->setConfigurator(array($this->resolveServices($service['configurator'][0]), $service['configurator'][1]));
            }
        }
@@ -161,20 +205,20 @@ protected function parseDefinition($id, $service, $filename) {

        if (isset($service['tags'])) {
            if (!is_array($service['tags'])) {
        throw new \InvalidArgumentException(sprintf('Parameter "tags" must be an array for service "%s" in %s.', $id, $filename));
                throw new InvalidArgumentException(sprintf('Parameter "tags" must be an array for service "%s" in %s.', $id, $file));
            }

            foreach ($service['tags'] as $tag) {
                if (!isset($tag['name'])) {
          throw new \InvalidArgumentException(sprintf('A "tags" entry is missing a "name" key for service "%s" in %s.', $id, $filename));
                    throw new InvalidArgumentException(sprintf('A "tags" entry is missing a "name" key for service "%s" in %s.', $id, $file));
                }

                $name = $tag['name'];
                unset($tag['name']);

        foreach ($tag as $value) {
          if (!is_scalar($value)) {
            throw new \InvalidArgumentException(sprintf('A "tags" attribute must be of a scalar-type for service "%s", tag "%s" in %s.', $id, $name, $filename));
                foreach ($tag as $attribute => $value) {
                    if (!is_scalar($value) && null !== $value) {
                        throw new InvalidArgumentException(sprintf('A "tags" attribute must be of a scalar-type for service "%s", tag "%s", attribute "%s" in %s.', $id, $name, $attribute, $file));
                    }
                }

@@ -188,39 +232,45 @@ protected function parseDefinition($id, $service, $filename) {
    /**
     * Loads a YAML file.
     *
   * @param string $filename
     * @param string $file
     *
   * @return array
   *   The file content.
     * @return array The file content
     */
  protected function loadFile($filename) {
    return $this->validate(Yaml::decode(file_get_contents($filename)), $filename);
    protected function loadFile($file)
    {
        if (!stream_is_local($file)) {
            throw new InvalidArgumentException(sprintf('This is not a local file "%s".', $file));
        }

        if (!file_exists($file)) {
            throw new InvalidArgumentException(sprintf('The service file "%s" is not valid.', $file));
        }

        return $this->validate(Yaml::decode(file_get_contents($file)), $file);
    }

    /**
     * Validates a YAML file.
     *
     * @param mixed  $content
   *   The parsed YAML file.
   * @param string $filename
   *   The name of the file, only used for error messages.
     * @param string $file
     *
     * @return array
   *   The $content unchanged returned to allow for chaining this method.
     *
   * @throws \InvalidArgumentException When service file is not valid
     * @throws InvalidArgumentException When service file is not valid
     */
  protected function validate($content, $filename) {
    if (NULL === $content) {
    private function validate($content, $file)
    {
        if (null === $content) {
            return $content;
        }

        if (!is_array($content)) {
      throw new \InvalidArgumentException(sprintf('The service file "%s" is not valid: it is not an array.', $filename));
            throw new InvalidArgumentException(sprintf('The service file "%s" is not valid: it is not an array.', $file));
        }
    if ($keys = array_diff_key($content, array('parameters' => TRUE, 'services' => TRUE))) {
      $invalid_keys = htmlspecialchars(implode(', ', $keys), ENT_QUOTES, 'UTF-8');
      throw new \InvalidArgumentException(sprintf('The service file "%s" is not valid: it contains invalid keys %s. Services have to be added under "services" and Parameters under "parameters".', $filename, $invalid_keys));

        if ($invalid_keys = array_diff_key($content, array('parameters' => 1, 'services' => 1))) {
            throw new InvalidArgumentException(sprintf('The service file "%s" is not valid: it contains invalid keys %s. Services have to be added under "services" and Parameters under "parameters".', $file, $invalid_keys));
        }

        return $content;
@@ -229,40 +279,41 @@ protected function validate($content, $filename) {
    /**
     * Resolves services.
     *
   * Copied from \Symfony\Component\DependencyInjection\Loader\YamlFileLoader::parseDefinition().
   *
   * @param mixed $value
   *   If a string, then it is either a plain string (for example a class
   *   name) or a reference to a service. If it's an array then it's a list of
   *   such strings.
     * @param string $value
     *
   * @return string|\Symfony\Component\DependencyInjection\Reference
   *   Either the string unchanged or the Reference object.
     * @return Reference
     */
  protected function resolveServices($value) {
    private function resolveServices($value)
    {
        if (is_array($value)) {
            $value = array_map(array($this, 'resolveServices'), $value);
    }
    elseif (is_string($value) && 0 === strpos($value, '@')) {
      if (0 === strpos($value, '@?')) {
        } elseif (is_string($value) &&  0 === strpos($value, '@=')) {
            // Not supported.
            //return new Expression(substr($value, 2));
            throw new InvalidArgumentException(sprintf("'%s' is an Expression, but expressions are not supported.", $value));
        } elseif (is_string($value) &&  0 === strpos($value, '@')) {
            if (0 === strpos($value, '@@')) {
                $value = substr($value, 1);
                $invalidBehavior = null;
            } elseif (0 === strpos($value, '@?')) {
                $value = substr($value, 2);
                $invalidBehavior = ContainerInterface::IGNORE_ON_INVALID_REFERENCE;
      }
      else {
            } else {
                $value = substr($value, 1);
                $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE;
            }

            if ('=' === substr($value, -1)) {
                $value = substr($value, 0, -1);
        $strict = FALSE;
      }
      else {
        $strict = TRUE;
                $strict = false;
            } else {
                $strict = true;
            }

            if (null !== $invalidBehavior) {
                $value = new Reference($value, $invalidBehavior, $strict);
            }
        }

        return $value;
    }