遍历嵌套数组并转换一些值
问题描述:
I'm looking for a clean solution to transform a nested array.
Here is what I'm trying to achieve...
Original array:
$map = array(
'name' => 'super test',
'machine_name' => 'super_test',
'class' => 'openlayers_map_map',
'options' => array(
'width' => 'auto',
'height' => '300px',
'contextualLinks' => 1,
'provideBlock' => 1,
'view' => array(
'center' => array(
'lat' => '0',
'lon' => '0',
),
'rotation' => '0',
'zoom' => '2',
),
'layers' => array(
'0' => array(
'name' => 'Ma super layer',
'machine_name' => 'plouf',
'class' => 'openlayers_layer_tile',
'options' => array(
'source' => array(
'name' => 'Ma super layer',
'machine_name' => 'plouf',
'class' => 'openlayers_source_osm'
),
'param1' => 'ca roule'
)
),
),
'controls' => array(
'control_mouseposition',
'0' => array(
'name' => 'Control attribution',
'machine_name' => 'openlayers_control_attribution',
'class' => 'openlayers_control_attribution',
'options' => array(
'collapsible' => 1
)
),
'control_rotate',
'control_zoom',
),
'interactions' => array(
'interaction_doubleclickzoom',
'interaction_dragpan',
'interaction_dragrotateandzoom',
'interaction_mousewheelzoom',
),
)
);
Final array:
$map = array(
'name' => 'super test',
'machine_name' => 'super_test',
'class' => 'openlayers_map_map',
'options' => array(
'width' => 'auto',
'height' => '300px',
'contextualLinks' => 1,
'provideBlock' => 1,
'view' => array(
'center' => array(
'lat' => '0',
'lon' => '0',
),
'rotation' => '0',
'zoom' => '2',
),
'layers' => array(
'0' => (object) openlayers_layer_tile
'name' => 'Ma super layer',
'machine_name' => 'plouf',
'class' => 'openlayers_layer_tile',
'options' => array(
'source' => (object) openlayers_source_osm
'name' => 'Ma super layer',
'machine_name' => 'plouf',
'class' => 'openlayers_source_osm'
),
'param1' => 'ca roule'
)
),
),
'controls' => array(
'control_mouseposition',
'0' => (object) openlayers_control_attribution
'name' => 'Control attribution',
'machine_name' => 'openlayers_control_attribution',
'class' => 'openlayers_control_attribution',
'options' => array(
'collapsible' => 1
)
),
'control_rotate',
'control_zoom',
),
'interactions' => array(
'interaction_doubleclickzoom',
'interaction_dragpan',
'interaction_dragrotateandzoom',
'interaction_mousewheelzoom',
),
)
);
Basically, I need to traverse the array, find all children with the 'class' key and transform them into objects of the same name.
答
If you don't have the classes ready to be instantiated, this code will create anonymous objects instead. (The class name still being present as a property.)
function class_to_object (&$arr) {
if (is_array($arr)) {
foreach ($arr as $key => &$val) {
class_to_object($val);
}
if (isset($arr['class'])) {
$arr = (object) $arr;
}
}
}
class_to_object($map);
Result :
(Notice that the first array is turned into an object, since it contains the field "class" too. I guess you can tweak the function easily enough if you don't want that behavior)
stdClass Object
(
[name] => super test
[machine_name] => super_test
[class] => openlayers_map_map
[options] => Array
(
[width] => auto
[height] => 300px
[contextualLinks] => 1
[provideBlock] => 1
[view] => Array
(
[center] => Array
(
[lat] => 0
[lon] => 0
)
[rotation] => 0
[zoom] => 2
)
[layers] => Array
(
[0] => stdClass Object
(
[name] => Ma super layer
[machine_name] => plouf
[class] => openlayers_layer_tile
[options] => Array
(
[source] => stdClass Object
(
[name] => Ma super layer
[machine_name] => plouf
[class] => openlayers_source_osm
)
[param1] => ca roule
)
)
)
[controls] => Array
(
[0] => stdClass Object
(
[name] => Control attribution
[machine_name] => openlayers_control_attribution
[class] => openlayers_control_attribution
[options] => Array
(
[collapsible] => 1
)
)
[1] => control_rotate
[2] => control_zoom
)
[interactions] => Array
(
[0] => interaction_doubleclickzoom
[1] => interaction_dragpan
[2] => interaction_dragrotateandzoom
[3] => interaction_mousewheelzoom
)
)
)
答
This totally untested recursive function may get you on the right track:
function recursive_hydrate_array($arr)
{
if(!is_array($arr) || !isset($arr["class"]))
{
throw new Exception("Argument is not an array or does not have a 'class' key.");
}
$klass = $arr["class"];
unset($arr["class"]);
$obj = new $klass();
foreach($arr as $k => $v)
{
if(is_array($arr[$k]) && isset($arr[$k]["class"]))
{
$obj->{$k} = recursive_hydrate_array($arr[$k]);
}
else
{
$obj->{$k} = $arr[$k];
}
}
return $obj;
}
Note that I am making three assumptions here:
- The classes in question already exist.
- Each class can be instantiated without passing any parameters to its constructor.
- All relevant properties of each class are public and can be set from outside of the class.