@@ -29,6 +29,10 @@ class PrototypedArrayNode extends ArrayNode
2929 protected $ minNumberOfElements = 0 ;
3030 protected $ defaultValue = array ();
3131 protected $ defaultChildren ;
32+ /**
33+ * @var NodeInterface[] An array of the prototypes of the simplified value children
34+ */
35+ private $ valuePrototypes = array ();
3236
3337 /**
3438 * Sets the minimum number of elements that a prototype based node must
@@ -194,9 +198,9 @@ protected function finalizeValue($value)
194198 }
195199
196200 foreach ($ value as $ k => $ v ) {
197- $ this ->prototype -> setName ($ k );
201+ $ prototype = $ this ->getPrototypeForChild ($ k );
198202 try {
199- $ value [$ k ] = $ this -> prototype ->finalize ($ v );
203+ $ value [$ k ] = $ prototype ->finalize ($ v );
200204 } catch (UnsetKeyException $ e ) {
201205 unset($ value [$ k ]);
202206 }
@@ -250,8 +254,18 @@ protected function normalizeValue($value)
250254 }
251255
252256 // if only "value" is left
253- if (1 == count ($ v ) && isset ( $ v [ 'value ' ] )) {
257+ if (array_keys ($ v ) === array ( 'value ' )) {
254258 $ v = $ v ['value ' ];
259+ if ($ this ->prototype instanceof ArrayNode && ($ children = $ this ->prototype ->getChildren ()) && array_key_exists ('value ' , $ children )) {
260+ $ valuePrototype = current ($ this ->valuePrototypes ) ?: clone $ children ['value ' ];
261+ $ valuePrototype ->parent = $ this ;
262+ $ originalClosures = $ this ->prototype ->normalizationClosures ;
263+ if (is_array ($ originalClosures )) {
264+ $ valuePrototypeClosures = $ valuePrototype ->normalizationClosures ;
265+ $ valuePrototype ->normalizationClosures = is_array ($ valuePrototypeClosures ) ? array_merge ($ originalClosures , $ valuePrototypeClosures ) : $ originalClosures ;
266+ }
267+ $ this ->valuePrototypes [$ k ] = $ valuePrototype ;
268+ }
255269 }
256270 }
257271
@@ -264,11 +278,11 @@ protected function normalizeValue($value)
264278 }
265279 }
266280
267- $ this ->prototype -> setName ($ k );
281+ $ prototype = $ this ->getPrototypeForChild ($ k );
268282 if (null !== $ this ->keyAttribute || $ isAssoc ) {
269- $ normalized [$ k ] = $ this -> prototype ->normalize ($ v );
283+ $ normalized [$ k ] = $ prototype ->normalize ($ v );
270284 } else {
271- $ normalized [] = $ this -> prototype ->normalize ($ v );
285+ $ normalized [] = $ prototype ->normalize ($ v );
272286 }
273287 }
274288
@@ -322,10 +336,54 @@ protected function mergeValues($leftSide, $rightSide)
322336 continue ;
323337 }
324338
325- $ this ->prototype -> setName ($ k );
326- $ leftSide [$ k ] = $ this -> prototype ->merge ($ leftSide [$ k ], $ v );
339+ $ prototype = $ this ->getPrototypeForChild ($ k );
340+ $ leftSide [$ k ] = $ prototype ->merge ($ leftSide [$ k ], $ v );
327341 }
328342
329343 return $ leftSide ;
330344 }
345+
346+ /**
347+ * Returns a prototype for the child node that is associated to $key in the value array.
348+ * For general child nodes, this will be $this->prototype.
349+ * But if $this->removeKeyAttribute is true and there are only two keys in the child node:
350+ * one is same as this->keyAttribute and the other is 'value', then the prototype will be different.
351+ *
352+ * For example, assume $this->keyAttribute is 'name' and the value array is as follows:
353+ * array(
354+ * array(
355+ * 'name' => 'name001',
356+ * 'value' => 'value001'
357+ * )
358+ * )
359+ *
360+ * Now, the key is 0 and the child node is:
361+ * array(
362+ * 'name' => 'name001',
363+ * 'value' => 'value001'
364+ * )
365+ *
366+ * When normalizing the value array, the 'name' element will removed from the child node
367+ * and its value becomes the new key of the child node:
368+ * array(
369+ * 'name001' => array('value' => 'value001')
370+ * )
371+ *
372+ * Now only 'value' element is left in the child node which can be further simplified into a string:
373+ * array('name001' => 'value001')
374+ *
375+ * Now, the key becomes 'name001' and the child node becomes 'value001' and
376+ * the prototype of child node 'name001' should be a ScalarNode instead of an ArrayNode instance.
377+ *
378+ * @param string $key The key of the child node
379+ *
380+ * @return mixed The prototype instance
381+ */
382+ private function getPrototypeForChild ($ key )
383+ {
384+ $ prototype = isset ($ this ->valuePrototypes [$ key ]) ? $ this ->valuePrototypes [$ key ] : $ this ->prototype ;
385+ $ prototype ->setName ($ key );
386+
387+ return $ prototype ;
388+ }
331389}
0 commit comments