@@ -29,6 +29,10 @@ class PrototypedArrayNode extends ArrayNode
29
29
protected $ minNumberOfElements = 0 ;
30
30
protected $ defaultValue = array ();
31
31
protected $ defaultChildren ;
32
+ /**
33
+ * @var NodeInterface[] An array of the prototypes of the simplified value children
34
+ */
35
+ private $ valuePrototypes = array ();
32
36
33
37
/**
34
38
* Sets the minimum number of elements that a prototype based node must
@@ -194,9 +198,9 @@ protected function finalizeValue($value)
194
198
}
195
199
196
200
foreach ($ value as $ k => $ v ) {
197
- $ this ->prototype -> setName ($ k );
201
+ $ prototype = $ this ->getPrototypeForChild ($ k );
198
202
try {
199
- $ value [$ k ] = $ this -> prototype ->finalize ($ v );
203
+ $ value [$ k ] = $ prototype ->finalize ($ v );
200
204
} catch (UnsetKeyException $ e ) {
201
205
unset($ value [$ k ]);
202
206
}
@@ -250,8 +254,18 @@ protected function normalizeValue($value)
250
254
}
251
255
252
256
// if only "value" is left
253
- if (1 == count ($ v ) && isset ( $ v [ 'value ' ] )) {
257
+ if (array_keys ($ v ) === array ( 'value ' )) {
254
258
$ 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
+ }
255
269
}
256
270
}
257
271
@@ -264,11 +278,11 @@ protected function normalizeValue($value)
264
278
}
265
279
}
266
280
267
- $ this ->prototype -> setName ($ k );
281
+ $ prototype = $ this ->getPrototypeForChild ($ k );
268
282
if (null !== $ this ->keyAttribute || $ isAssoc ) {
269
- $ normalized [$ k ] = $ this -> prototype ->normalize ($ v );
283
+ $ normalized [$ k ] = $ prototype ->normalize ($ v );
270
284
} else {
271
- $ normalized [] = $ this -> prototype ->normalize ($ v );
285
+ $ normalized [] = $ prototype ->normalize ($ v );
272
286
}
273
287
}
274
288
@@ -322,10 +336,54 @@ protected function mergeValues($leftSide, $rightSide)
322
336
continue ;
323
337
}
324
338
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 );
327
341
}
328
342
329
343
return $ leftSide ;
330
344
}
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
+ }
331
389
}
0 commit comments