Verzeichnisstruktur phpBB-3.3.15


Veröffentlicht
28.08.2024

So funktioniert es


Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück

Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis.
Auf den Verzeichnisnamen klicken, dies zeigt nur das Verzeichnis mit Inhalt an

(Beispiel Datei-Icons)

Auf das Icon klicken um den Quellcode anzuzeigen

PrototypedArrayNode.php

Zuletzt modifiziert: 02.04.2025, 15:03 - Dateigröße: 11.98 KiB


001  <?php
002   
003  /*
004   * This file is part of the Symfony package.
005   *
006   * (c) Fabien Potencier <fabien@symfony.com>
007   *
008   * For the full copyright and license information, please view the LICENSE
009   * file that was distributed with this source code.
010   */
011   
012  namespace Symfony\Component\Config\Definition;
013   
014  use Symfony\Component\Config\Definition\Exception\DuplicateKeyException;
015  use Symfony\Component\Config\Definition\Exception\Exception;
016  use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
017  use Symfony\Component\Config\Definition\Exception\UnsetKeyException;
018   
019  /**
020   * Represents a prototyped Array node in the config tree.
021   *
022   * @author Johannes M. Schmitt <schmittjoh@gmail.com>
023   */
024  class PrototypedArrayNode extends ArrayNode
025  {
026      protected $prototype;
027      protected $keyAttribute;
028      protected $removeKeyAttribute = false;
029      protected $minNumberOfElements = 0;
030      protected $defaultValue = [];
031      protected $defaultChildren;
032      /**
033       * @var NodeInterface[] An array of the prototypes of the simplified value children
034       */
035      private $valuePrototypes = [];
036   
037      /**
038       * Sets the minimum number of elements that a prototype based node must
039       * contain. By default this is zero, meaning no elements.
040       *
041       * @param int $number
042       */
043      public function setMinNumberOfElements($number)
044      {
045          $this->minNumberOfElements = $number;
046      }
047   
048      /**
049       * Sets the attribute which value is to be used as key.
050       *
051       * This is useful when you have an indexed array that should be an
052       * associative array. You can select an item from within the array
053       * to be the key of the particular item. For example, if "id" is the
054       * "key", then:
055       *
056       *     [
057       *         ['id' => 'my_name', 'foo' => 'bar'],
058       *     ];
059       *
060       *  becomes
061       *
062       *      [
063       *          'my_name' => ['foo' => 'bar'],
064       *      ];
065       *
066       * If you'd like "'id' => 'my_name'" to still be present in the resulting
067       * array, then you can set the second argument of this method to false.
068       *
069       * @param string $attribute The name of the attribute which value is to be used as a key
070       * @param bool   $remove    Whether or not to remove the key
071       */
072      public function setKeyAttribute($attribute, $remove = true)
073      {
074          $this->keyAttribute = $attribute;
075          $this->removeKeyAttribute = $remove;
076      }
077   
078      /**
079       * Retrieves the name of the attribute which value should be used as key.
080       *
081       * @return string|null The name of the attribute
082       */
083      public function getKeyAttribute()
084      {
085          return $this->keyAttribute;
086      }
087   
088      /**
089       * Sets the default value of this node.
090       *
091       * @param string $value
092       *
093       * @throws \InvalidArgumentException if the default value is not an array
094       */
095      public function setDefaultValue($value)
096      {
097          if (!\is_array($value)) {
098              throw new \InvalidArgumentException($this->getPath().': the default value of an array node has to be an array.');
099          }
100   
101          $this->defaultValue = $value;
102      }
103   
104      /**
105       * {@inheritdoc}
106       */
107      public function hasDefaultValue()
108      {
109          return true;
110      }
111   
112      /**
113       * Adds default children when none are set.
114       *
115       * @param int|string|array|null $children The number of children|The child name|The children names to be added
116       */
117      public function setAddChildrenIfNoneSet($children = ['defaults'])
118      {
119          if (null === $children) {
120              $this->defaultChildren = ['defaults'];
121          } else {
122              $this->defaultChildren = \is_int($children) && $children > 0 ? range(1, $children) : (array) $children;
123          }
124      }
125   
126      /**
127       * {@inheritdoc}
128       *
129       * The default value could be either explicited or derived from the prototype
130       * default value.
131       */
132      public function getDefaultValue()
133      {
134          if (null !== $this->defaultChildren) {
135              $default = $this->prototype->hasDefaultValue() ? $this->prototype->getDefaultValue() : [];
136              $defaults = [];
137              foreach (array_values($this->defaultChildren) as $i => $name) {
138                  $defaults[null === $this->keyAttribute ? $i : $name] = $default;
139              }
140   
141              return $defaults;
142          }
143   
144          return $this->defaultValue;
145      }
146   
147      /**
148       * Sets the node prototype.
149       */
150      public function setPrototype(PrototypeNodeInterface $node)
151      {
152          $this->prototype = $node;
153      }
154   
155      /**
156       * Retrieves the prototype.
157       *
158       * @return PrototypeNodeInterface The prototype
159       */
160      public function getPrototype()
161      {
162          return $this->prototype;
163      }
164   
165      /**
166       * Disable adding concrete children for prototyped nodes.
167       *
168       * @throws Exception
169       */
170      public function addChild(NodeInterface $node)
171      {
172          throw new Exception('A prototyped array node can not have concrete children.');
173      }
174   
175      /**
176       * Finalizes the value of this node.
177       *
178       * @param mixed $value
179       *
180       * @return mixed The finalized value
181       *
182       * @throws UnsetKeyException
183       * @throws InvalidConfigurationException if the node doesn't have enough children
184       */
185      protected function finalizeValue($value)
186      {
187          if (false === $value) {
188              throw new UnsetKeyException(sprintf('Unsetting key for path "%s", value: "%s".', $this->getPath(), json_encode($value)));
189          }
190   
191          foreach ($value as $k => $v) {
192              $prototype = $this->getPrototypeForChild($k);
193              try {
194                  $value[$k] = $prototype->finalize($v);
195              } catch (UnsetKeyException $e) {
196                  unset($value[$k]);
197              }
198          }
199   
200          if (\count($value) < $this->minNumberOfElements) {
201              $ex = new InvalidConfigurationException(sprintf('The path "%s" should have at least %d element(s) defined.', $this->getPath(), $this->minNumberOfElements));
202              $ex->setPath($this->getPath());
203   
204              throw $ex;
205          }
206   
207          return $value;
208      }
209   
210      /**
211       * Normalizes the value.
212       *
213       * @param mixed $value The value to normalize
214       *
215       * @return mixed The normalized value
216       *
217       * @throws InvalidConfigurationException
218       * @throws DuplicateKeyException
219       */
220      protected function normalizeValue($value)
221      {
222          if (false === $value) {
223              return $value;
224          }
225   
226          $value = $this->remapXml($value);
227   
228          $isAssoc = array_keys($value) !== range(0, \count($value) - 1);
229          $normalized = [];
230          foreach ($value as $k => $v) {
231              if (null !== $this->keyAttribute && \is_array($v)) {
232                  if (!isset($v[$this->keyAttribute]) && \is_int($k) && !$isAssoc) {
233                      $ex = new InvalidConfigurationException(sprintf('The attribute "%s" must be set for path "%s".', $this->keyAttribute, $this->getPath()));
234                      $ex->setPath($this->getPath());
235   
236                      throw $ex;
237                  } elseif (isset($v[$this->keyAttribute])) {
238                      $k = $v[$this->keyAttribute];
239   
240                      // remove the key attribute when required
241                      if ($this->removeKeyAttribute) {
242                          unset($v[$this->keyAttribute]);
243                      }
244   
245                      // if only "value" is left
246                      if (array_keys($v) === ['value']) {
247                          $v = $v['value'];
248                          if ($this->prototype instanceof ArrayNode && ($children = $this->prototype->getChildren()) && \array_key_exists('value', $children)) {
249                              $valuePrototype = current($this->valuePrototypes) ?: clone $children['value'];
250                              $valuePrototype->parent = $this;
251                              $originalClosures = $this->prototype->normalizationClosures;
252                              if (\is_array($originalClosures)) {
253                                  $valuePrototypeClosures = $valuePrototype->normalizationClosures;
254                                  $valuePrototype->normalizationClosures = \is_array($valuePrototypeClosures) ? array_merge($originalClosures, $valuePrototypeClosures) : $originalClosures;
255                              }
256                              $this->valuePrototypes[$k] = $valuePrototype;
257                          }
258                      }
259                  }
260   
261                  if (\array_key_exists($k, $normalized)) {
262                      $ex = new DuplicateKeyException(sprintf('Duplicate key "%s" for path "%s".', $k, $this->getPath()));
263                      $ex->setPath($this->getPath());
264   
265                      throw $ex;
266                  }
267              }
268   
269              $prototype = $this->getPrototypeForChild($k);
270              if (null !== $this->keyAttribute || $isAssoc) {
271                  $normalized[$k] = $prototype->normalize($v);
272              } else {
273                  $normalized[] = $prototype->normalize($v);
274              }
275          }
276   
277          return $normalized;
278      }
279   
280      /**
281       * Merges values together.
282       *
283       * @param mixed $leftSide  The left side to merge
284       * @param mixed $rightSide The right side to merge
285       *
286       * @return mixed The merged values
287       *
288       * @throws InvalidConfigurationException
289       * @throws \RuntimeException
290       */
291      protected function mergeValues($leftSide, $rightSide)
292      {
293          if (false === $rightSide) {
294              // if this is still false after the last config has been merged the
295              // finalization pass will take care of removing this key entirely
296              return false;
297          }
298   
299          if (false === $leftSide || !$this->performDeepMerging) {
300              return $rightSide;
301          }
302   
303          foreach ($rightSide as $k => $v) {
304              // prototype, and key is irrelevant, append the element
305              if (null === $this->keyAttribute) {
306                  $leftSide[] = $v;
307                  continue;
308              }
309   
310              // no conflict
311              if (!\array_key_exists($k, $leftSide)) {
312                  if (!$this->allowNewKeys) {
313                      $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.', $this->getPath()));
314                      $ex->setPath($this->getPath());
315   
316                      throw $ex;
317                  }
318   
319                  $leftSide[$k] = $v;
320                  continue;
321              }
322   
323              $prototype = $this->getPrototypeForChild($k);
324              $leftSide[$k] = $prototype->merge($leftSide[$k], $v);
325          }
326   
327          return $leftSide;
328      }
329   
330      /**
331       * Returns a prototype for the child node that is associated to $key in the value array.
332       * For general child nodes, this will be $this->prototype.
333       * But if $this->removeKeyAttribute is true and there are only two keys in the child node:
334       * one is same as this->keyAttribute and the other is 'value', then the prototype will be different.
335       *
336       * For example, assume $this->keyAttribute is 'name' and the value array is as follows:
337       *
338       *     [
339       *         [
340       *             'name' => 'name001',
341       *             'value' => 'value001'
342       *         ]
343       *     ]
344       *
345       * Now, the key is 0 and the child node is:
346       *
347       *     [
348       *        'name' => 'name001',
349       *        'value' => 'value001'
350       *     ]
351       *
352       * When normalizing the value array, the 'name' element will removed from the child node
353       * and its value becomes the new key of the child node:
354       *
355       *     [
356       *         'name001' => ['value' => 'value001']
357       *     ]
358       *
359       * Now only 'value' element is left in the child node which can be further simplified into a string:
360       *
361       *     ['name001' => 'value001']
362       *
363       * Now, the key becomes 'name001' and the child node becomes 'value001' and
364       * the prototype of child node 'name001' should be a ScalarNode instead of an ArrayNode instance.
365       *
366       * @param string $key The key of the child node
367       *
368       * @return mixed The prototype instance
369       */
370      private function getPrototypeForChild($key)
371      {
372          $prototype = isset($this->valuePrototypes[$key]) ? $this->valuePrototypes[$key] : $this->prototype;
373          $prototype->setName($key);
374   
375          return $prototype;
376      }
377  }
378