aboutsummaryrefslogtreecommitdiff
path: root/srcs/phpmyadmin/vendor/symfony/config/Definition
diff options
context:
space:
mode:
Diffstat (limited to 'srcs/phpmyadmin/vendor/symfony/config/Definition')
-rw-r--r--srcs/phpmyadmin/vendor/symfony/config/Definition/ArrayNode.php416
-rw-r--r--srcs/phpmyadmin/vendor/symfony/config/Definition/BaseNode.php559
-rw-r--r--srcs/phpmyadmin/vendor/symfony/config/Definition/BooleanNode.php55
-rw-r--r--srcs/phpmyadmin/vendor/symfony/config/Definition/Builder/ArrayNodeDefinition.php548
-rw-r--r--srcs/phpmyadmin/vendor/symfony/config/Definition/Builder/BooleanNodeDefinition.php53
-rw-r--r--srcs/phpmyadmin/vendor/symfony/config/Definition/Builder/BuilderAwareInterface.php25
-rw-r--r--srcs/phpmyadmin/vendor/symfony/config/Definition/Builder/EnumNodeDefinition.php56
-rw-r--r--srcs/phpmyadmin/vendor/symfony/config/Definition/Builder/ExprBuilder.php248
-rw-r--r--srcs/phpmyadmin/vendor/symfony/config/Definition/Builder/FloatNodeDefinition.php32
-rw-r--r--srcs/phpmyadmin/vendor/symfony/config/Definition/Builder/IntegerNodeDefinition.php32
-rw-r--r--srcs/phpmyadmin/vendor/symfony/config/Definition/Builder/MergeBuilder.php67
-rw-r--r--srcs/phpmyadmin/vendor/symfony/config/Definition/Builder/NodeBuilder.php238
-rw-r--r--srcs/phpmyadmin/vendor/symfony/config/Definition/Builder/NodeDefinition.php373
-rw-r--r--srcs/phpmyadmin/vendor/symfony/config/Definition/Builder/NodeParentInterface.php21
-rw-r--r--srcs/phpmyadmin/vendor/symfony/config/Definition/Builder/NormalizationBuilder.php60
-rw-r--r--srcs/phpmyadmin/vendor/symfony/config/Definition/Builder/NumericNodeDefinition.php73
-rw-r--r--srcs/phpmyadmin/vendor/symfony/config/Definition/Builder/ParentNodeDefinitionInterface.php46
-rw-r--r--srcs/phpmyadmin/vendor/symfony/config/Definition/Builder/ScalarNodeDefinition.php32
-rw-r--r--srcs/phpmyadmin/vendor/symfony/config/Definition/Builder/TreeBuilder.php106
-rw-r--r--srcs/phpmyadmin/vendor/symfony/config/Definition/Builder/ValidationBuilder.php44
-rw-r--r--srcs/phpmyadmin/vendor/symfony/config/Definition/Builder/VariableNodeDefinition.php65
-rw-r--r--srcs/phpmyadmin/vendor/symfony/config/Definition/ConfigurationInterface.php27
-rw-r--r--srcs/phpmyadmin/vendor/symfony/config/Definition/Dumper/XmlReferenceDumper.php302
-rw-r--r--srcs/phpmyadmin/vendor/symfony/config/Definition/Dumper/YamlReferenceDumper.php242
-rw-r--r--srcs/phpmyadmin/vendor/symfony/config/Definition/EnumNode.php62
-rw-r--r--srcs/phpmyadmin/vendor/symfony/config/Definition/Exception/DuplicateKeyException.php22
-rw-r--r--srcs/phpmyadmin/vendor/symfony/config/Definition/Exception/Exception.php21
-rw-r--r--srcs/phpmyadmin/vendor/symfony/config/Definition/Exception/ForbiddenOverwriteException.php22
-rw-r--r--srcs/phpmyadmin/vendor/symfony/config/Definition/Exception/InvalidConfigurationException.php49
-rw-r--r--srcs/phpmyadmin/vendor/symfony/config/Definition/Exception/InvalidDefinitionException.php21
-rw-r--r--srcs/phpmyadmin/vendor/symfony/config/Definition/Exception/InvalidTypeException.php21
-rw-r--r--srcs/phpmyadmin/vendor/symfony/config/Definition/Exception/TreeWithoutRootNodeException.php21
-rw-r--r--srcs/phpmyadmin/vendor/symfony/config/Definition/Exception/UnsetKeyException.php22
-rw-r--r--srcs/phpmyadmin/vendor/symfony/config/Definition/FloatNode.php51
-rw-r--r--srcs/phpmyadmin/vendor/symfony/config/Definition/IntegerNode.php46
-rw-r--r--srcs/phpmyadmin/vendor/symfony/config/Definition/NodeInterface.php100
-rw-r--r--srcs/phpmyadmin/vendor/symfony/config/Definition/NumericNode.php64
-rw-r--r--srcs/phpmyadmin/vendor/symfony/config/Definition/Processor.php97
-rw-r--r--srcs/phpmyadmin/vendor/symfony/config/Definition/PrototypeNodeInterface.php27
-rw-r--r--srcs/phpmyadmin/vendor/symfony/config/Definition/PrototypedArrayNode.php375
-rw-r--r--srcs/phpmyadmin/vendor/symfony/config/Definition/ScalarNode.php67
-rw-r--r--srcs/phpmyadmin/vendor/symfony/config/Definition/VariableNode.php143
42 files changed, 4951 insertions, 0 deletions
diff --git a/srcs/phpmyadmin/vendor/symfony/config/Definition/ArrayNode.php b/srcs/phpmyadmin/vendor/symfony/config/Definition/ArrayNode.php
new file mode 100644
index 0000000..3b2ace7
--- /dev/null
+++ b/srcs/phpmyadmin/vendor/symfony/config/Definition/ArrayNode.php
@@ -0,0 +1,416 @@
+<?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($normalizeKeys)
+ {
+ $this->normalizeKeys = (bool) $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 (false !== strpos($k, '-') && false === strpos($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 The children
+ */
+ 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.
+ *
+ * @param bool $boolean
+ */
+ public function setAddIfNotSet($boolean)
+ {
+ $this->addIfNotSet = (bool) $boolean;
+ }
+
+ /**
+ * Sets whether false is allowed as value indicating that the array should be unset.
+ *
+ * @param bool $allow
+ */
+ public function setAllowFalse($allow)
+ {
+ $this->allowFalse = (bool) $allow;
+ }
+
+ /**
+ * Sets whether new keys can be defined in subsequent configurations.
+ *
+ * @param bool $allow
+ */
+ public function setAllowNewKeys($allow)
+ {
+ $this->allowNewKeys = (bool) $allow;
+ }
+
+ /**
+ * Sets if deep merging should occur.
+ *
+ * @param bool $boolean
+ */
+ public function setPerformDeepMerging($boolean)
+ {
+ $this->performDeepMerging = (bool) $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($boolean, $remove = true)
+ {
+ $this->ignoreExtraKeys = (bool) $boolean;
+ $this->removeExtraKeys = $this->ignoreExtraKeys && $remove;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setName($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 (!\strlen($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;
+ }
+
+ /**
+ * Finalizes the value of this node.
+ *
+ * @param mixed $value
+ *
+ * @return mixed The finalised value
+ *
+ * @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()) {
+ $ex = new InvalidConfigurationException(sprintf('The child node "%s" at path "%s" must be configured.', $name, $this->getPath()));
+ $ex->setPath($this->getPath());
+
+ throw $ex;
+ }
+
+ if ($child->hasDefaultValue()) {
+ $value[$name] = $child->getDefaultValue();
+ }
+
+ continue;
+ }
+
+ if ($child->isDeprecated()) {
+ @trigger_error($child->getDeprecationMessage($name, $this->getPath()), E_USER_DEPRECATED);
+ }
+
+ try {
+ $value[$name] = $child->finalize($value[$name]);
+ } catch (UnsetKeyException $e) {
+ unset($value[$name]);
+ }
+ }
+
+ return $value;
+ }
+
+ /**
+ * Validates the type of the value.
+ *
+ * @param mixed $value
+ *
+ * @throws InvalidTypeException
+ */
+ 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(), \gettype($value)));
+ if ($hint = $this->getInfo()) {
+ $ex->addHint($hint);
+ }
+ $ex->setPath($this->getPath());
+
+ throw $ex;
+ }
+ }
+
+ /**
+ * Normalizes the value.
+ *
+ * @param mixed $value The value to normalize
+ *
+ * @return mixed The normalized value
+ *
+ * @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.
+ *
+ * @param array $value The source values
+ *
+ * @return array The remapped values
+ */
+ protected function remapXml($value)
+ {
+ foreach ($this->xmlRemappings as list($singular, $plural)) {
+ if (!isset($value[$singular])) {
+ continue;
+ }
+
+ $value[$plural] = Processor::normalizeConfig($value, $singular, $plural);
+ unset($value[$singular]);
+ }
+
+ return $value;
+ }
+
+ /**
+ * Merges values together.
+ *
+ * @param mixed $leftSide The left side to merge
+ * @param mixed $rightSide The right side to merge
+ *
+ * @return mixed The merged values
+ *
+ * @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;
+ }
+}
diff --git a/srcs/phpmyadmin/vendor/symfony/config/Definition/BaseNode.php b/srcs/phpmyadmin/vendor/symfony/config/Definition/BaseNode.php
new file mode 100644
index 0000000..076d175
--- /dev/null
+++ b/srcs/phpmyadmin/vendor/symfony/config/Definition/BaseNode.php
@@ -0,0 +1,559 @@
+<?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\Exception;
+use Symfony\Component\Config\Definition\Exception\ForbiddenOverwriteException;
+use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
+use Symfony\Component\Config\Definition\Exception\InvalidTypeException;
+use Symfony\Component\Config\Definition\Exception\UnsetKeyException;
+
+/**
+ * The base node class.
+ *
+ * @author Johannes M. Schmitt <schmittjoh@gmail.com>
+ */
+abstract class BaseNode implements NodeInterface
+{
+ const DEFAULT_PATH_SEPARATOR = '.';
+
+ private static $placeholderUniquePrefix;
+ private static $placeholders = [];
+
+ protected $name;
+ protected $parent;
+ protected $normalizationClosures = [];
+ protected $finalValidationClosures = [];
+ protected $allowOverwrite = true;
+ protected $required = false;
+ protected $deprecationMessage = null;
+ protected $equivalentValues = [];
+ protected $attributes = [];
+ protected $pathSeparator;
+
+ private $handlingPlaceholder;
+
+ /**
+ * @throws \InvalidArgumentException if the name contains a period
+ */
+ public function __construct(?string $name, NodeInterface $parent = null, string $pathSeparator = self::DEFAULT_PATH_SEPARATOR)
+ {
+ if (false !== strpos($name = (string) $name, $pathSeparator)) {
+ throw new \InvalidArgumentException('The name must not contain "'.$pathSeparator.'".');
+ }
+
+ $this->name = $name;
+ $this->parent = $parent;
+ $this->pathSeparator = $pathSeparator;
+ }
+
+ /**
+ * Register possible (dummy) values for a dynamic placeholder value.
+ *
+ * Matching configuration values will be processed with a provided value, one by one. After a provided value is
+ * successfully processed the configuration value is returned as is, thus preserving the placeholder.
+ *
+ * @internal
+ */
+ public static function setPlaceholder(string $placeholder, array $values): void
+ {
+ if (!$values) {
+ throw new \InvalidArgumentException('At least one value must be provided.');
+ }
+
+ self::$placeholders[$placeholder] = $values;
+ }
+
+ /**
+ * Sets a common prefix for dynamic placeholder values.
+ *
+ * Matching configuration values will be skipped from being processed and are returned as is, thus preserving the
+ * placeholder. An exact match provided by {@see setPlaceholder()} might take precedence.
+ *
+ * @internal
+ */
+ public static function setPlaceholderUniquePrefix(string $prefix): void
+ {
+ self::$placeholderUniquePrefix = $prefix;
+ }
+
+ /**
+ * Resets all current placeholders available.
+ *
+ * @internal
+ */
+ public static function resetPlaceholders(): void
+ {
+ self::$placeholderUniquePrefix = null;
+ self::$placeholders = [];
+ }
+
+ /**
+ * @param string $key
+ */
+ public function setAttribute($key, $value)
+ {
+ $this->attributes[$key] = $value;
+ }
+
+ /**
+ * @param string $key
+ *
+ * @return mixed
+ */
+ public function getAttribute($key, $default = null)
+ {
+ return isset($this->attributes[$key]) ? $this->attributes[$key] : $default;
+ }
+
+ /**
+ * @param string $key
+ *
+ * @return bool
+ */
+ public function hasAttribute($key)
+ {
+ return isset($this->attributes[$key]);
+ }
+
+ /**
+ * @return array
+ */
+ public function getAttributes()
+ {
+ return $this->attributes;
+ }
+
+ public function setAttributes(array $attributes)
+ {
+ $this->attributes = $attributes;
+ }
+
+ /**
+ * @param string $key
+ */
+ public function removeAttribute($key)
+ {
+ unset($this->attributes[$key]);
+ }
+
+ /**
+ * Sets an info message.
+ *
+ * @param string $info
+ */
+ public function setInfo($info)
+ {
+ $this->setAttribute('info', $info);
+ }
+
+ /**
+ * Returns info message.
+ *
+ * @return string|null The info text
+ */
+ public function getInfo()
+ {
+ return $this->getAttribute('info');
+ }
+
+ /**
+ * Sets the example configuration for this node.
+ *
+ * @param string|array $example
+ */
+ public function setExample($example)
+ {
+ $this->setAttribute('example', $example);
+ }
+
+ /**
+ * Retrieves the example configuration for this node.
+ *
+ * @return string|array|null The example
+ */
+ public function getExample()
+ {
+ return $this->getAttribute('example');
+ }
+
+ /**
+ * Adds an equivalent value.
+ *
+ * @param mixed $originalValue
+ * @param mixed $equivalentValue
+ */
+ public function addEquivalentValue($originalValue, $equivalentValue)
+ {
+ $this->equivalentValues[] = [$originalValue, $equivalentValue];
+ }
+
+ /**
+ * Set this node as required.
+ *
+ * @param bool $boolean Required node
+ */
+ public function setRequired($boolean)
+ {
+ $this->required = (bool) $boolean;
+ }
+
+ /**
+ * Sets this node as deprecated.
+ *
+ * You can use %node% and %path% placeholders in your message to display,
+ * respectively, the node name and its complete path.
+ *
+ * @param string|null $message Deprecated message
+ */
+ public function setDeprecated($message)
+ {
+ $this->deprecationMessage = $message;
+ }
+
+ /**
+ * Sets if this node can be overridden.
+ *
+ * @param bool $allow
+ */
+ public function setAllowOverwrite($allow)
+ {
+ $this->allowOverwrite = (bool) $allow;
+ }
+
+ /**
+ * Sets the closures used for normalization.
+ *
+ * @param \Closure[] $closures An array of Closures used for normalization
+ */
+ public function setNormalizationClosures(array $closures)
+ {
+ $this->normalizationClosures = $closures;
+ }
+
+ /**
+ * Sets the closures used for final validation.
+ *
+ * @param \Closure[] $closures An array of Closures used for final validation
+ */
+ public function setFinalValidationClosures(array $closures)
+ {
+ $this->finalValidationClosures = $closures;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isRequired()
+ {
+ return $this->required;
+ }
+
+ /**
+ * Checks if this node is deprecated.
+ *
+ * @return bool
+ */
+ public function isDeprecated()
+ {
+ return null !== $this->deprecationMessage;
+ }
+
+ /**
+ * Returns the deprecated message.
+ *
+ * @param string $node the configuration node name
+ * @param string $path the path of the node
+ *
+ * @return string
+ */
+ public function getDeprecationMessage($node, $path)
+ {
+ return strtr($this->deprecationMessage, ['%node%' => $node, '%path%' => $path]);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getPath()
+ {
+ if (null !== $this->parent) {
+ return $this->parent->getPath().$this->pathSeparator.$this->name;
+ }
+
+ return $this->name;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ final public function merge($leftSide, $rightSide)
+ {
+ if (!$this->allowOverwrite) {
+ throw new ForbiddenOverwriteException(sprintf('Configuration path "%s" cannot be overwritten. You have to define all options for this path, and any of its sub-paths in one configuration section.', $this->getPath()));
+ }
+
+ if ($leftSide !== $leftPlaceholders = self::resolvePlaceholderValue($leftSide)) {
+ foreach ($leftPlaceholders as $leftPlaceholder) {
+ $this->handlingPlaceholder = $leftSide;
+ try {
+ $this->merge($leftPlaceholder, $rightSide);
+ } finally {
+ $this->handlingPlaceholder = null;
+ }