遍历嵌套数组并转换一些值

遍历嵌套数组并转换一些值

问题描述:

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:

  1. The classes in question already exist.
  2. Each class can be instantiated without passing any parameters to its constructor.
  3. All relevant properties of each class are public and can be set from outside of the class.