Overview

Namespaces

  • Contrib
    • Bundle
      • CoverallsBundle
        • Console
        • Entity
      • CoverallsV1Bundle
        • Api
        • Collector
        • Command
        • Config
        • Entity
          • Git
    • Component
      • File
      • Log
      • System
        • Git
  • Guzzle
    • Batch
      • Exception
    • Cache
    • Common
      • Exception
    • Http
      • Curl
      • Exception
      • Message
      • QueryAggregator
    • Inflection
    • Iterator
    • Log
    • Parser
      • Cookie
      • Message
      • UriTemplate
      • Url
    • Plugin
      • Async
      • Backoff
      • Cache
      • Cookie
        • CookieJar
        • Exception
      • CurlAuth
      • ErrorResponse
        • Exception
      • History
      • Log
      • Md5
      • Mock
      • Oauth
    • Service
      • Builder
      • Command
        • Factory
        • LocationVisitor
          • Request
          • Response
      • Description
      • Exception
      • Resource
    • Stream
  • PHP
  • Psr
    • Log
  • Symfony
    • Component
      • Config
        • Definition
          • Builder
          • Exception
        • Exception
        • Loader
        • Resource
        • Util
      • Console
        • Command
        • Formatter
        • Helper
        • Input
        • Output
        • Tester
      • EventDispatcher
        • Debug
      • Finder
        • Adapter
        • Comparator
        • Exception
        • Expression
        • Iterator
        • Shell
      • Stopwatch
      • Yaml
        • Exception

Classes

  • ArrayNode
  • BaseNode
  • BooleanNode
  • EnumNode
  • FloatNode
  • IntegerNode
  • NumericNode
  • Processor
  • PrototypedArrayNode
  • ReferenceDumper
  • ScalarNode
  • VariableNode

Interfaces

  • ConfigurationInterface
  • NodeInterface
  • PrototypeNodeInterface
  • Overview
  • Namespace
  • Class
  • Tree
  • Todo
  1: <?php
  2: 
  3: /*
  4:  * This file is part of the Symfony package.
  5:  *
  6:  * (c) Fabien Potencier <fabien@symfony.com>
  7:  *
  8:  * For the full copyright and license information, please view the LICENSE
  9:  * file that was distributed with this source code.
 10:  */
 11: 
 12: namespace Symfony\Component\Config\Definition;
 13: 
 14: use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
 15: use Symfony\Component\Config\Definition\Exception\InvalidTypeException;
 16: use Symfony\Component\Config\Definition\Exception\UnsetKeyException;
 17: 
 18: /**
 19:  * Represents an Array node in the config tree.
 20:  *
 21:  * @author Johannes M. Schmitt <schmittjoh@gmail.com>
 22:  */
 23: class ArrayNode extends BaseNode implements PrototypeNodeInterface
 24: {
 25:     protected $xmlRemappings;
 26:     protected $children;
 27:     protected $allowFalse;
 28:     protected $allowNewKeys;
 29:     protected $addIfNotSet;
 30:     protected $performDeepMerging;
 31:     protected $ignoreExtraKeys;
 32:     protected $normalizeKeys;
 33: 
 34:     /**
 35:      * Constructor.
 36:      *
 37:      * @param string        $name   The Node's name
 38:      * @param NodeInterface $parent The node parent
 39:      */
 40:     public function __construct($name, NodeInterface $parent = null)
 41:     {
 42:         parent::__construct($name, $parent);
 43: 
 44:         $this->children = array();
 45:         $this->xmlRemappings = array();
 46:         $this->removeKeyAttribute = true;
 47:         $this->allowFalse = false;
 48:         $this->addIfNotSet = false;
 49:         $this->allowNewKeys = true;
 50:         $this->performDeepMerging = true;
 51:         $this->normalizeKeys = true;
 52:     }
 53: 
 54:     public function setNormalizeKeys($normalizeKeys)
 55:     {
 56:         $this->normalizeKeys = (Boolean) $normalizeKeys;
 57:     }
 58: 
 59:     /**
 60:      * Normalizes keys between the different configuration formats.
 61:      *
 62:      * Namely, you mostly have foo_bar in YAML while you have foo-bar in XML.
 63:      * After running this method, all keys are normalized to foo_bar.
 64:      *
 65:      * If you have a mixed key like foo-bar_moo, it will not be altered.
 66:      * The key will also not be altered if the target key already exists.
 67:      *
 68:      * @param mixed $value
 69:      *
 70:      * @return array The value with normalized keys
 71:      */
 72:     protected function preNormalize($value)
 73:     {
 74:         if (!$this->normalizeKeys || !is_array($value)) {
 75:             return $value;
 76:         }
 77: 
 78:         foreach ($value as $k => $v) {
 79:             if (false !== strpos($k, '-') && false === strpos($k, '_') && !array_key_exists($normalizedKey = str_replace('-', '_', $k), $value)) {
 80:                 $value[$normalizedKey] = $v;
 81:                 unset($value[$k]);
 82:             }
 83:         }
 84: 
 85:         return $value;
 86:     }
 87: 
 88:     /**
 89:      * Retrieves the children of this node.
 90:      *
 91:      * @return array The children
 92:      */
 93:     public function getChildren()
 94:     {
 95:         return $this->children;
 96:     }
 97: 
 98:     /**
 99:      * Sets the xml remappings that should be performed.
100:      *
101:      * @param array $remappings an array of the form array(array(string, string))
102:      */
103:     public function setXmlRemappings(array $remappings)
104:     {
105:         $this->xmlRemappings = $remappings;
106:     }
107: 
108:     /**
109:      * Sets whether to add default values for this array if it has not been
110:      * defined in any of the configuration files.
111:      *
112:      * @param Boolean $boolean
113:      */
114:     public function setAddIfNotSet($boolean)
115:     {
116:         $this->addIfNotSet = (Boolean) $boolean;
117:     }
118: 
119:     /**
120:      * Sets whether false is allowed as value indicating that the array should be unset.
121:      *
122:      * @param Boolean $allow
123:      */
124:     public function setAllowFalse($allow)
125:     {
126:         $this->allowFalse = (Boolean) $allow;
127:     }
128: 
129:     /**
130:      * Sets whether new keys can be defined in subsequent configurations.
131:      *
132:      * @param Boolean $allow
133:      */
134:     public function setAllowNewKeys($allow)
135:     {
136:         $this->allowNewKeys = (Boolean) $allow;
137:     }
138: 
139:     /**
140:      * Sets if deep merging should occur.
141:      *
142:      * @param Boolean $boolean
143:      */
144:     public function setPerformDeepMerging($boolean)
145:     {
146:         $this->performDeepMerging = (Boolean) $boolean;
147:     }
148: 
149:     /**
150:      * Whether extra keys should just be ignore without an exception.
151:      *
152:      * @param Boolean $boolean To allow extra keys
153:      */
154:     public function setIgnoreExtraKeys($boolean)
155:     {
156:         $this->ignoreExtraKeys = (Boolean) $boolean;
157:     }
158: 
159:     /**
160:      * Sets the node Name.
161:      *
162:      * @param string $name The node's name
163:      */
164:     public function setName($name)
165:     {
166:         $this->name = $name;
167:     }
168: 
169:     /**
170:      * Checks if the node has a default value.
171:      *
172:      * @return Boolean
173:      */
174:     public function hasDefaultValue()
175:     {
176:         return $this->addIfNotSet;
177:     }
178: 
179:     /**
180:      * Retrieves the default value.
181:      *
182:      * @return array The default value
183:      *
184:      * @throws \RuntimeException if the node has no default value
185:      */
186:     public function getDefaultValue()
187:     {
188:         if (!$this->hasDefaultValue()) {
189:             throw new \RuntimeException(sprintf('The node at path "%s" has no default value.', $this->getPath()));
190:         }
191: 
192:         $defaults = array();
193:         foreach ($this->children as $name => $child) {
194:             if ($child->hasDefaultValue()) {
195:                 $defaults[$name] = $child->getDefaultValue();
196:             }
197:         }
198: 
199:         return $defaults;
200:     }
201: 
202:     /**
203:      * Adds a child node.
204:      *
205:      * @param NodeInterface $node The child node to add
206:      *
207:      * @throws \InvalidArgumentException when the child node has no name
208:      * @throws \InvalidArgumentException when the child node's name is not unique
209:      */
210:     public function addChild(NodeInterface $node)
211:     {
212:         $name = $node->getName();
213:         if (empty($name)) {
214:             throw new \InvalidArgumentException('Child nodes must be named.');
215:         }
216:         if (isset($this->children[$name])) {
217:             throw new \InvalidArgumentException(sprintf('A child node named "%s" already exists.', $name));
218:         }
219: 
220:         $this->children[$name] = $node;
221:     }
222: 
223:     /**
224:      * Finalizes the value of this node.
225:      *
226:      * @param mixed $value
227:      *
228:      * @return mixed The finalised value
229:      *
230:      * @throws UnsetKeyException
231:      * @throws InvalidConfigurationException if the node doesn't have enough children
232:      */
233:     protected function finalizeValue($value)
234:     {
235:         if (false === $value) {
236:             $msg = sprintf('Unsetting key for path "%s", value: %s', $this->getPath(), json_encode($value));
237:             throw new UnsetKeyException($msg);
238:         }
239: 
240:         foreach ($this->children as $name => $child) {
241:             if (!array_key_exists($name, $value)) {
242:                 if ($child->isRequired()) {
243:                     $msg = sprintf('The child node "%s" at path "%s" must be configured.', $name, $this->getPath());
244:                     $ex = new InvalidConfigurationException($msg);
245:                     $ex->setPath($this->getPath());
246: 
247:                     throw $ex;
248:                 }
249: 
250:                 if ($child->hasDefaultValue()) {
251:                     $value[$name] = $child->getDefaultValue();
252:                 }
253: 
254:                 continue;
255:             }
256: 
257:             try {
258:                 $value[$name] = $child->finalize($value[$name]);
259:             } catch (UnsetKeyException $unset) {
260:                 unset($value[$name]);
261:             }
262:         }
263: 
264:         return $value;
265:     }
266: 
267:     /**
268:      * Validates the type of the value.
269:      *
270:      * @param mixed $value
271:      *
272:      * @throws InvalidTypeException
273:      */
274:     protected function validateType($value)
275:     {
276:         if (!is_array($value) && (!$this->allowFalse || false !== $value)) {
277:             $ex = new InvalidTypeException(sprintf(
278:                 'Invalid type for path "%s". Expected array, but got %s',
279:                 $this->getPath(),
280:                 gettype($value)
281:             ));
282:             $ex->setPath($this->getPath());
283: 
284:             throw $ex;
285:         }
286:     }
287: 
288:     /**
289:      * Normalizes the value.
290:      *
291:      * @param mixed $value The value to normalize
292:      *
293:      * @return mixed The normalized value
294:      *
295:      * @throws InvalidConfigurationException
296:      */
297:     protected function normalizeValue($value)
298:     {
299:         if (false === $value) {
300:             return $value;
301:         }
302: 
303:         $value = $this->remapXml($value);
304: 
305:         $normalized = array();
306:         foreach ($this->children as $name => $child) {
307:             if (array_key_exists($name, $value)) {
308:                 $normalized[$name] = $child->normalize($value[$name]);
309:                 unset($value[$name]);
310:             }
311:         }
312: 
313:         // if extra fields are present, throw exception
314:         if (count($value) && !$this->ignoreExtraKeys) {
315:             $msg = sprintf('Unrecognized options "%s" under "%s"', implode(', ', array_keys($value)), $this->getPath());
316:             $ex = new InvalidConfigurationException($msg);
317:             $ex->setPath($this->getPath());
318: 
319:             throw $ex;
320:         }
321: 
322:         return $normalized;
323:     }
324: 
325:     /**
326:      * Remaps multiple singular values to a single plural value.
327:      *
328:      * @param array $value The source values
329:      *
330:      * @return array The remapped values
331:      */
332:     protected function remapXml($value)
333:     {
334:         foreach ($this->xmlRemappings as $transformation) {
335:             list($singular, $plural) = $transformation;
336: 
337:             if (!isset($value[$singular])) {
338:                 continue;
339:             }
340: 
341:             $value[$plural] = Processor::normalizeConfig($value, $singular, $plural);
342:             unset($value[$singular]);
343:         }
344: 
345:         return $value;
346:     }
347: 
348:     /**
349:      * Merges values together.
350:      *
351:      * @param mixed $leftSide  The left side to merge.
352:      * @param mixed $rightSide The right side to merge.
353:      *
354:      * @return mixed The merged values
355:      *
356:      * @throws InvalidConfigurationException
357:      * @throws \RuntimeException
358:      */
359:     protected function mergeValues($leftSide, $rightSide)
360:     {
361:         if (false === $rightSide) {
362:             // if this is still false after the last config has been merged the
363:             // finalization pass will take care of removing this key entirely
364:             return false;
365:         }
366: 
367:         if (false === $leftSide || !$this->performDeepMerging) {
368:             return $rightSide;
369:         }
370: 
371:         foreach ($rightSide as $k => $v) {
372:             // no conflict
373:             if (!array_key_exists($k, $leftSide)) {
374:                 if (!$this->allowNewKeys) {
375:                     $ex = new InvalidConfigurationException(sprintf(
376:                         'You are not allowed to define new elements for path "%s". '
377:                        .'Please define all elements for this path in one config file. '
378:                        .'If you are trying to overwrite an element, make sure you redefine it '
379:                        .'with the same name.',
380:                         $this->getPath()
381:                     ));
382:                     $ex->setPath($this->getPath());
383: 
384:                     throw $ex;
385:                 }
386: 
387:                 $leftSide[$k] = $v;
388:                 continue;
389:             }
390: 
391:             if (!isset($this->children[$k])) {
392:                 throw new \RuntimeException('merge() expects a normalized config array.');
393:             }
394: 
395:             $leftSide[$k] = $this->children[$k]->merge($leftSide[$k], $v);
396:         }
397: 
398:         return $leftSide;
399:     }
400: }
401: 
php-coveralls API documentation generated by ApiGen 2.8.0