PHPで動的n次元配列追加をしたいです。 例えば複数の階層を持つ配列です。


流行りライド
PHPで動的n次元配列追加をしたいです。 例え… - Qiitaをもうちょっと真面目に対応したもの

仕様

  • 最下層以外は全てキーとみなす
  • "("の前に任意の文字が無い場合は、配列の追加とみなす

実装


echo str_repeat('=', 36), PHP_EOL;
echo "お題:a((b((c))d((e)))) => ['a' => [['b'=>[['c']], 'd' => [['e']]]]]", PHP_EOL;
echo str_repeat('=', 36), PHP_EOL;
echo "['a' => [['b'=>[['c']], 'd' => [['e']]]]]を実際展開するとこうなる。", PHP_EOL;
var_dump(['a' => [['b'=>[['c']], 'd' => [['e']]]]]);
echo PHP_EOL;

$target = 'a((b((c))d((e))))';

//比較的真面目parser
$stacker = parser($target);

//一括DONE
$result = [];
foreach ($stacker as $stack) {
    $value = array_pop($stack);
    $result = set_lowest($result, $stack, $value);
}

echo PHP_EOL;
echo "ウチの実行結果。", PHP_EOL;
var_dump($result);
echo PHP_EOL;

echo str_repeat('=', 36), PHP_EOL;
echo "拡張お題:a((((c))((e)))) => ['a' => [[[['c']], [['e']]]]]", PHP_EOL;
echo str_repeat('=', 36), PHP_EOL;
echo "['a' => [[[['c']], [['e']]]]]を実際展開するとこうなる。", PHP_EOL;
var_dump(['a' => [[[['c']], [['e']]]]]);
echo PHP_EOL;

$target = 'a((((c))((e))))';

//比較的真面目parser
$stacker = parser($target);

//一括DONE
$result = [];
foreach ($stacker as $stack) {
    $value = array_pop($stack);
    $result = set_lowest($result, $stack, $value);
}

echo PHP_EOL;
echo "ウチの実行結果。", PHP_EOL;
var_dump($result);
echo PHP_EOL;

/**
 * 指定された文字列を解析し、チャンクとして返します。
 *
 * @param   string  $target 解析対象の文字列
 * @return  array   解析済みのチャンク 階層ツリーごとに1配列を持つ配列
 */
function parser ($target) {
    $key_buffer = [];
    $value_buffer = [];
    $stacker = [];

    for ($i = 0, $max = mb_strlen($target, ENCODING);$i < $max;$i++) {
        $char = mb_substr($target, $i, 1, ENCODING);
        switch ($char) {
            case '(' :
                $key_buffer[] = empty($value_buffer) ? count(get_lowest($stacker, array_keys($key_buffer))) : implode('', $value_buffer);
                $value_buffer = [];
                break;
            case ')':
                if (!empty($value_buffer)) {
                    $key_buffer[] = 0;
                    $key_buffer[] = implode('', $value_buffer);
                    $stacker[] = $key_buffer;
                }
                array_pop($key_buffer);
                array_pop($key_buffer);
                $value_buffer = [];
                break;
            default:
                $value_buffer[] = $char;
                break;
        }
    }

    return $stacker;
}

/**
 * 指定された階層にある値を設定します。
 *
 * @param   array   $array  配列
 * @param   mixed   $keys   階層
 * @return  array   設定後の配列
 */
function set_lowest ($array, $keys, $value) {
    $keys = (array) $keys;
    if (empty($array)) {
        $tmp =& $array;
    } else {
        $tmp =& $array[array_shift($keys)];
    }

    foreach ($keys as $key) {
        if (!isset($tmp[$key])) {
            $tmp[$key] = null;
        }
        $tmp =& $tmp[$key];
    }
    $tmp = $value;
    return $array;
}

/**
 * 指定された階層にある値を取得します。
 *
 * @param   array   $array  配列
 * @param   mixed   $keys   階層
 * @return  mixed   指定された改造にある値
 */
function get_lowest ($array, $keys) {
    foreach ((array) $keys as $key) {
        if (isset($array[$key])) {
            $array = $array[$key];
        } else {
            return null;
        }
    }
    return $array;
}

実行結果

====================================
お題:a((b((c))d((e)))) => ['a' => [['b'=>[['c']], 'd' => [['e']]]]]
====================================
['a' => [['b'=>[['c']], 'd' => [['e']]]]]を実際展開するとこうなる。
array(1) {
  ["a"]=>
  array(1) {
    [0]=>
    array(2) {
      ["b"]=>
      array(1) {
        [0]=>
        array(1) {
          [0]=>
          string(1) "c"
        }
      }
      ["d"]=>
      array(1) {
        [0]=>
        array(1) {
          [0]=>
          string(1) "e"
        }
      }
    }
  }
}


ウチの実行結果。
array(1) {
  ["a"]=>
  array(1) {
    [0]=>
    array(2) {
      ["b"]=>
      array(1) {
        [0]=>
        array(1) {
          [0]=>
          string(1) "c"
        }
      }
      ["d"]=>
      array(1) {
        [0]=>
        array(1) {
          [0]=>
          string(1) "e"
        }
      }
    }
  }
}

====================================
拡張お題:a((((c))((e)))) => ['a' => [[[['c']], [['e']]]]]
====================================
['a' => [[[['c']], [['e']]]]]を実際展開するとこうなる。
array(1) {
  ["a"]=>
  array(1) {
    [0]=>
    array(2) {
      [0]=>
      array(1) {
        [0]=>
        array(1) {
          [0]=>
          string(1) "c"
        }
      }
      [1]=>
      array(1) {
        [0]=>
        array(1) {
          [0]=>
          string(1) "e"
        }
      }
    }
  }
}


ウチの実行結果。
array(1) {
  ["a"]=>
  array(1) {
    [0]=>
    array(2) {
      [0]=>
      array(1) {
        [0]=>
        array(1) {
          [0]=>
          string(1) "c"
        }
      }
      [1]=>
      array(1) {
        [0]=>
        array(1) {
          [0]=>
          string(1) "e"
        }
      }
    }
  }
}