- <?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\Config\Definition;
- use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
- use Symfony\Component\Config\Definition\Exception\InvalidTypeException;
- use Symfony\Component\Config\Definition\Exception\UnsetKeyException;
- /**
-  * Represents an Array node in the config tree.
-  *
-  * @author Johannes M. Schmitt <schmittjoh@gmail.com>
-  */
- class ArrayNode extends BaseNode implements PrototypeNodeInterface
- {
-     protected $xmlRemappings = [];
-     protected $children = [];
-     protected $allowFalse = false;
-     protected $allowNewKeys = true;
-     protected $addIfNotSet = false;
-     protected $performDeepMerging = true;
-     protected $ignoreExtraKeys = false;
-     protected $removeExtraKeys = true;
-     protected $normalizeKeys = true;
-     public function setNormalizeKeys(bool $normalizeKeys)
-     {
-         $this->normalizeKeys = $normalizeKeys;
-     }
-     /**
-      * {@inheritdoc}
-      *
-      * Namely, you mostly have foo_bar in YAML while you have foo-bar in XML.
-      * After running this method, all keys are normalized to foo_bar.
-      *
-      * If you have a mixed key like foo-bar_moo, it will not be altered.
-      * The key will also not be altered if the target key already exists.
-      */
-     protected function preNormalize($value)
-     {
-         if (!$this->normalizeKeys || !\is_array($value)) {
-             return $value;
-         }
-         $normalized = [];
-         foreach ($value as $k => $v) {
-             if (str_contains($k, '-') && !str_contains($k, '_') && !\array_key_exists($normalizedKey = str_replace('-', '_', $k), $value)) {
-                 $normalized[$normalizedKey] = $v;
-             } else {
-                 $normalized[$k] = $v;
-             }
-         }
-         return $normalized;
-     }
-     /**
-      * Retrieves the children of this node.
-      *
-      * @return array<string, NodeInterface>
-      */
-     public function getChildren()
-     {
-         return $this->children;
-     }
-     /**
-      * Sets the xml remappings that should be performed.
-      *
-      * @param array $remappings An array of the form [[string, string]]
-      */
-     public function setXmlRemappings(array $remappings)
-     {
-         $this->xmlRemappings = $remappings;
-     }
-     /**
-      * Gets the xml remappings that should be performed.
-      *
-      * @return array an array of the form [[string, string]]
-      */
-     public function getXmlRemappings()
-     {
-         return $this->xmlRemappings;
-     }
-     /**
-      * Sets whether to add default values for this array if it has not been
-      * defined in any of the configuration files.
-      */
-     public function setAddIfNotSet(bool $boolean)
-     {
-         $this->addIfNotSet = $boolean;
-     }
-     /**
-      * Sets whether false is allowed as value indicating that the array should be unset.
-      */
-     public function setAllowFalse(bool $allow)
-     {
-         $this->allowFalse = $allow;
-     }
-     /**
-      * Sets whether new keys can be defined in subsequent configurations.
-      */
-     public function setAllowNewKeys(bool $allow)
-     {
-         $this->allowNewKeys = $allow;
-     }
-     /**
-      * Sets if deep merging should occur.
-      */
-     public function setPerformDeepMerging(bool $boolean)
-     {
-         $this->performDeepMerging = $boolean;
-     }
-     /**
-      * Whether extra keys should just be ignored without an exception.
-      *
-      * @param bool $boolean To allow extra keys
-      * @param bool $remove  To remove extra keys
-      */
-     public function setIgnoreExtraKeys(bool $boolean, bool $remove = true)
-     {
-         $this->ignoreExtraKeys = $boolean;
-         $this->removeExtraKeys = $this->ignoreExtraKeys && $remove;
-     }
-     /**
-      * Returns true when extra keys should be ignored without an exception.
-      */
-     public function shouldIgnoreExtraKeys(): bool
-     {
-         return $this->ignoreExtraKeys;
-     }
-     /**
-      * {@inheritdoc}
-      */
-     public function setName(string $name)
-     {
-         $this->name = $name;
-     }
-     /**
-      * {@inheritdoc}
-      */
-     public function hasDefaultValue()
-     {
-         return $this->addIfNotSet;
-     }
-     /**
-      * {@inheritdoc}
-      */
-     public function getDefaultValue()
-     {
-         if (!$this->hasDefaultValue()) {
-             throw new \RuntimeException(sprintf('The node at path "%s" has no default value.', $this->getPath()));
-         }
-         $defaults = [];
-         foreach ($this->children as $name => $child) {
-             if ($child->hasDefaultValue()) {
-                 $defaults[$name] = $child->getDefaultValue();
-             }
-         }
-         return $defaults;
-     }
-     /**
-      * Adds a child node.
-      *
-      * @throws \InvalidArgumentException when the child node has no name
-      * @throws \InvalidArgumentException when the child node's name is not unique
-      */
-     public function addChild(NodeInterface $node)
-     {
-         $name = $node->getName();
-         if ('' === $name) {
-             throw new \InvalidArgumentException('Child nodes must be named.');
-         }
-         if (isset($this->children[$name])) {
-             throw new \InvalidArgumentException(sprintf('A child node named "%s" already exists.', $name));
-         }
-         $this->children[$name] = $node;
-     }
-     /**
-      * {@inheritdoc}
-      *
-      * @throws UnsetKeyException
-      * @throws InvalidConfigurationException if the node doesn't have enough children
-      */
-     protected function finalizeValue($value)
-     {
-         if (false === $value) {
-             throw new UnsetKeyException(sprintf('Unsetting key for path "%s", value: %s.', $this->getPath(), json_encode($value)));
-         }
-         foreach ($this->children as $name => $child) {
-             if (!\array_key_exists($name, $value)) {
-                 if ($child->isRequired()) {
-                     $message = sprintf('The child config "%s" under "%s" must be configured', $name, $this->getPath());
-                     if ($child->getInfo()) {
-                         $message .= sprintf(': %s', $child->getInfo());
-                     } else {
-                         $message .= '.';
-                     }
-                     $ex = new InvalidConfigurationException($message);
-                     $ex->setPath($this->getPath());
-                     throw $ex;
-                 }
-                 if ($child->hasDefaultValue()) {
-                     $value[$name] = $child->getDefaultValue();
-                 }
-                 continue;
-             }
-             if ($child->isDeprecated()) {
-                 $deprecation = $child->getDeprecation($name, $this->getPath());
-                 trigger_deprecation($deprecation['package'], $deprecation['version'], $deprecation['message']);
-             }
-             try {
-                 $value[$name] = $child->finalize($value[$name]);
-             } catch (UnsetKeyException $e) {
-                 unset($value[$name]);
-             }
-         }
-         return $value;
-     }
-     /**
-      * {@inheritdoc}
-      */
-     protected function validateType($value)
-     {
-         if (!\is_array($value) && (!$this->allowFalse || false !== $value)) {
-             $ex = new InvalidTypeException(sprintf('Invalid type for path "%s". Expected "array", but got "%s"', $this->getPath(), get_debug_type($value)));
-             if ($hint = $this->getInfo()) {
-                 $ex->addHint($hint);
-             }
-             $ex->setPath($this->getPath());
-             throw $ex;
-         }
-     }
-     /**
-      * {@inheritdoc}
-      *
-      * @throws InvalidConfigurationException
-      */
-     protected function normalizeValue($value)
-     {
-         if (false === $value) {
-             return $value;
-         }
-         $value = $this->remapXml($value);
-         $normalized = [];
-         foreach ($value as $name => $val) {
-             if (isset($this->children[$name])) {
-                 try {
-                     $normalized[$name] = $this->children[$name]->normalize($val);
-                 } catch (UnsetKeyException $e) {
-                 }
-                 unset($value[$name]);
-             } elseif (!$this->removeExtraKeys) {
-                 $normalized[$name] = $val;
-             }
-         }
-         // if extra fields are present, throw exception
-         if (\count($value) && !$this->ignoreExtraKeys) {
-             $proposals = array_keys($this->children);
-             sort($proposals);
-             $guesses = [];
-             foreach (array_keys($value) as $subject) {
-                 $minScore = \INF;
-                 foreach ($proposals as $proposal) {
-                     $distance = levenshtein($subject, $proposal);
-                     if ($distance <= $minScore && $distance < 3) {
-                         $guesses[$proposal] = $distance;
-                         $minScore = $distance;
-                     }
-                 }
-             }
-             $msg = sprintf('Unrecognized option%s "%s" under "%s"', 1 === \count($value) ? '' : 's', implode(', ', array_keys($value)), $this->getPath());
-             if (\count($guesses)) {
-                 asort($guesses);
-                 $msg .= sprintf('. Did you mean "%s"?', implode('", "', array_keys($guesses)));
-             } else {
-                 $msg .= sprintf('. Available option%s %s "%s".', 1 === \count($proposals) ? '' : 's', 1 === \count($proposals) ? 'is' : 'are', implode('", "', $proposals));
-             }
-             $ex = new InvalidConfigurationException($msg);
-             $ex->setPath($this->getPath());
-             throw $ex;
-         }
-         return $normalized;
-     }
-     /**
-      * Remaps multiple singular values to a single plural value.
-      *
-      * @return array
-      */
-     protected function remapXml(array $value)
-     {
-         foreach ($this->xmlRemappings as [$singular, $plural]) {
-             if (!isset($value[$singular])) {
-                 continue;
-             }
-             $value[$plural] = Processor::normalizeConfig($value, $singular, $plural);
-             unset($value[$singular]);
-         }
-         return $value;
-     }
-     /**
-      * {@inheritdoc}
-      *
-      * @throws InvalidConfigurationException
-      * @throws \RuntimeException
-      */
-     protected function mergeValues($leftSide, $rightSide)
-     {
-         if (false === $rightSide) {
-             // if this is still false after the last config has been merged the
-             // finalization pass will take care of removing this key entirely
-             return false;
-         }
-         if (false === $leftSide || !$this->performDeepMerging) {
-             return $rightSide;
-         }
-         foreach ($rightSide as $k => $v) {
-             // no conflict
-             if (!\array_key_exists($k, $leftSide)) {
-                 if (!$this->allowNewKeys) {
-                     $ex = new InvalidConfigurationException(sprintf('You are not allowed to define new elements for path "%s". Please define all elements for this path in one config file. If you are trying to overwrite an element, make sure you redefine it with the same name.', $this->getPath()));
-                     $ex->setPath($this->getPath());
-                     throw $ex;
-                 }
-                 $leftSide[$k] = $v;
-                 continue;
-             }
-             if (!isset($this->children[$k])) {
-                 if (!$this->ignoreExtraKeys || $this->removeExtraKeys) {
-                     throw new \RuntimeException('merge() expects a normalized config array.');
-                 }
-                 $leftSide[$k] = $v;
-                 continue;
-             }
-             $leftSide[$k] = $this->children[$k]->merge($leftSide[$k], $v);
-         }
-         return $leftSide;
-     }
-     /**
-      * {@inheritdoc}
-      */
-     protected function allowPlaceholders(): bool
-     {
-         return false;
-     }
- }
-