Verzeichnisstruktur phpBB-3.1.0
- Veröffentlicht
- 27.10.2014
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. |
|
(Beispiel Datei-Icons)
|
Auf das Icon klicken um den Quellcode anzuzeigen |
PrototypedArrayNode.php
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\InvalidConfigurationException;
015 use Symfony\Component\Config\Definition\Exception\DuplicateKeyException;
016 use Symfony\Component\Config\Definition\Exception\UnsetKeyException;
017 use Symfony\Component\Config\Definition\Exception\Exception;
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;
029 protected $minNumberOfElements;
030 protected $defaultValue;
031 protected $defaultChildren;
032
033 /**
034 * Constructor.
035 *
036 * @param string $name The Node's name
037 * @param NodeInterface $parent The node parent
038 */
039 public function __construct($name, NodeInterface $parent = null)
040 {
041 parent::__construct($name, $parent);
042
043 $this->minNumberOfElements = 0;
044 $this->defaultValue = array();
045 }
046
047 /**
048 * Sets the minimum number of elements that a prototype based node must
049 * contain. By default this is zero, meaning no elements.
050 *
051 * @param int $number
052 */
053 public function setMinNumberOfElements($number)
054 {
055 $this->minNumberOfElements = $number;
056 }
057
058 /**
059 * Sets the attribute which value is to be used as key.
060 *
061 * This is useful when you have an indexed array that should be an
062 * associative array. You can select an item from within the array
063 * to be the key of the particular item. For example, if "id" is the
064 * "key", then:
065 *
066 * array(
067 * array('id' => 'my_name', 'foo' => 'bar'),
068 * );
069 *
070 * becomes
071 *
072 * array(
073 * 'my_name' => array('foo' => 'bar'),
074 * );
075 *
076 * If you'd like "'id' => 'my_name'" to still be present in the resulting
077 * array, then you can set the second argument of this method to false.
078 *
079 * @param string $attribute The name of the attribute which value is to be used as a key
080 * @param bool $remove Whether or not to remove the key
081 */
082 public function setKeyAttribute($attribute, $remove = true)
083 {
084 $this->keyAttribute = $attribute;
085 $this->removeKeyAttribute = $remove;
086 }
087
088 /**
089 * Retrieves the name of the attribute which value should be used as key.
090 *
091 * @return string The name of the attribute
092 */
093 public function getKeyAttribute()
094 {
095 return $this->keyAttribute;
096 }
097
098 /**
099 * Sets the default value of this node.
100 *
101 * @param string $value
102 *
103 * @throws \InvalidArgumentException if the default value is not an array
104 */
105 public function setDefaultValue($value)
106 {
107 if (!is_array($value)) {
108 throw new \InvalidArgumentException($this->getPath().': the default value of an array node has to be an array.');
109 }
110
111 $this->defaultValue = $value;
112 }
113
114 /**
115 * Checks if the node has a default value.
116 *
117 * @return bool
118 */
119 public function hasDefaultValue()
120 {
121 return true;
122 }
123
124 /**
125 * Adds default children when none are set.
126 *
127 * @param int|string|array|null $children The number of children|The child name|The children names to be added
128 */
129 public function setAddChildrenIfNoneSet($children = array('defaults'))
130 {
131 if (null === $children) {
132 $this->defaultChildren = array('defaults');
133 } else {
134 $this->defaultChildren = is_integer($children) && $children > 0 ? range(1, $children) : (array) $children;
135 }
136 }
137
138 /**
139 * Retrieves the default value.
140 *
141 * The default value could be either explicited or derived from the prototype
142 * default value.
143 *
144 * @return array The default value
145 */
146 public function getDefaultValue()
147 {
148 if (null !== $this->defaultChildren) {
149 $default = $this->prototype->hasDefaultValue() ? $this->prototype->getDefaultValue() : array();
150 $defaults = array();
151 foreach (array_values($this->defaultChildren) as $i => $name) {
152 $defaults[null === $this->keyAttribute ? $i : $name] = $default;
153 }
154
155 return $defaults;
156 }
157
158 return $this->defaultValue;
159 }
160
161 /**
162 * Sets the node prototype.
163 *
164 * @param PrototypeNodeInterface $node
165 */
166 public function setPrototype(PrototypeNodeInterface $node)
167 {
168 $this->prototype = $node;
169 }
170
171 /**
172 * Retrieves the prototype
173 *
174 * @return PrototypeNodeInterface The prototype
175 */
176 public function getPrototype()
177 {
178 return $this->prototype;
179 }
180
181 /**
182 * Disable adding concrete children for prototyped nodes.
183 *
184 * @param NodeInterface $node The child node to add
185 *
186 * @throws Exception
187 */
188 public function addChild(NodeInterface $node)
189 {
190 throw new Exception('A prototyped array node can not have concrete children.');
191 }
192
193 /**
194 * Finalizes the value of this node.
195 *
196 * @param mixed $value
197 *
198 * @return mixed The finalized value
199 *
200 * @throws UnsetKeyException
201 * @throws InvalidConfigurationException if the node doesn't have enough children
202 */
203 protected function finalizeValue($value)
204 {
205 if (false === $value) {
206 $msg = sprintf('Unsetting key for path "%s", value: %s', $this->getPath(), json_encode($value));
207 throw new UnsetKeyException($msg);
208 }
209
210 foreach ($value as $k => $v) {
211 $this->prototype->setName($k);
212 try {
213 $value[$k] = $this->prototype->finalize($v);
214 } catch (UnsetKeyException $unset) {
215 unset($value[$k]);
216 }
217 }
218
219 if (count($value) < $this->minNumberOfElements) {
220 $msg = sprintf('The path "%s" should have at least %d element(s) defined.', $this->getPath(), $this->minNumberOfElements);
221 $ex = new InvalidConfigurationException($msg);
222 $ex->setPath($this->getPath());
223
224 throw $ex;
225 }
226
227 return $value;
228 }
229
230 /**
231 * Normalizes the value.
232 *
233 * @param mixed $value The value to normalize
234 *
235 * @return mixed The normalized value
236 *
237 * @throws InvalidConfigurationException
238 * @throws DuplicateKeyException
239 */
240 protected function normalizeValue($value)
241 {
242 if (false === $value) {
243 return $value;
244 }
245
246 $value = $this->remapXml($value);
247
248 $isAssoc = array_keys($value) !== range(0, count($value) -1);
249 $normalized = array();
250 foreach ($value as $k => $v) {
251 if (null !== $this->keyAttribute && is_array($v)) {
252 if (!isset($v[$this->keyAttribute]) && is_int($k) && !$isAssoc) {
253 $msg = sprintf('The attribute "%s" must be set for path "%s".', $this->keyAttribute, $this->getPath());
254 $ex = new InvalidConfigurationException($msg);
255 $ex->setPath($this->getPath());
256
257 throw $ex;
258 } elseif (isset($v[$this->keyAttribute])) {
259 $k = $v[$this->keyAttribute];
260
261 // remove the key attribute when required
262 if ($this->removeKeyAttribute) {
263 unset($v[$this->keyAttribute]);
264 }
265
266 // if only "value" is left
267 if (1 == count($v) && isset($v['value'])) {
268 $v = $v['value'];
269 }
270 }
271
272 if (array_key_exists($k, $normalized)) {
273 $msg = sprintf('Duplicate key "%s" for path "%s".', $k, $this->getPath());
274 $ex = new DuplicateKeyException($msg);
275 $ex->setPath($this->getPath());
276
277 throw $ex;
278 }
279 }
280
281 $this->prototype->setName($k);
282 if (null !== $this->keyAttribute || $isAssoc) {
283 $normalized[$k] = $this->prototype->normalize($v);
284 } else {
285 $normalized[] = $this->prototype->normalize($v);
286 }
287 }
288
289 return $normalized;
290 }
291
292 /**
293 * Merges values together.
294 *
295 * @param mixed $leftSide The left side to merge.
296 * @param mixed $rightSide The right side to merge.
297 *
298 * @return mixed The merged values
299 *
300 * @throws InvalidConfigurationException
301 * @throws \RuntimeException
302 */
303 protected function mergeValues($leftSide, $rightSide)
304 {
305 if (false === $rightSide) {
306 // if this is still false after the last config has been merged the
307 // finalization pass will take care of removing this key entirely
308 return false;
309 }
310
311 if (false === $leftSide || !$this->performDeepMerging) {
312 return $rightSide;
313 }
314
315 foreach ($rightSide as $k => $v) {
316 // prototype, and key is irrelevant, so simply append the element
317 if (null === $this->keyAttribute) {
318 $leftSide[] = $v;
319 continue;
320 }
321
322 // no conflict
323 if (!array_key_exists($k, $leftSide)) {
324 if (!$this->allowNewKeys) {
325 $ex = new InvalidConfigurationException(sprintf(
326 'You are not allowed to define new elements for path "%s". '.
327 'Please define all elements for this path in one config file.',
328 $this->getPath()
329 ));
330 $ex->setPath($this->getPath());
331
332 throw $ex;
333 }
334
335 $leftSide[$k] = $v;
336 continue;
337 }
338
339 $this->prototype->setName($k);
340 $leftSide[$k] = $this->prototype->merge($leftSide[$k], $v);
341 }
342
343 return $leftSide;
344 }
345 }
346