将数组的var_dump转换回数组变量

Chu*_*ess 43 php var-dump

直到今天我才真正想过这个,但在搜索网络后我并没有真正找到任何东西.也许我没有在搜索中对其进行措辞.

给定一个数组(多维或不多):

$data = array('this' => array('is' => 'the'), 'challenge' => array('for' => array('you')));
Run Code Online (Sandbox Code Playgroud)

var_dumped时:

array(2) { ["this"]=> array(1) { ["is"]=> string(3) "the" } ["challenge"]=> array(1) { ["for"]=> array(1) { [0]=> string(3) "you" } } }
Run Code Online (Sandbox Code Playgroud)

挑战在于:将数组重新编译为PHP的可用数组的最佳优化方法是什么?就像一个undump_var()功能.数据是否全部作为浏览器输出的一行,或者是否包含换行符作为输出到终端.

这只是正则表达式的问题吗?或者还有其他方式吗?我在寻找创造力.

更新:注意.我熟悉序列化和反序列化的人.我不是在寻找替代解决方案.这是一个代码挑战,看看它是否可以以优化和创造性的方式完成.所以serialize和var_export不是这里的解决方案.它们也不是最好的答案.

irc*_*ell 64

var_export或者serialize是你在寻找什么. var_export将呈现一个PHP可解析的数组语法,serialize并将呈现一个非人类可读但可逆的"数组到字符串"转换...

编辑好的,挑战:

基本上,我将输出转换为序列化字符串(然后反序列化).我并不认为这是完美的,但它似乎适用于我试过的一些非常复杂的结构......

function unvar_dump($str) {
    if (strpos($str, "\n") === false) {
        //Add new lines:
        $regex = array(
            '#(\\[.*?\\]=>)#',
            '#(string\\(|int\\(|float\\(|array\\(|NULL|object\\(|})#',
        );
        $str = preg_replace($regex, "\n\\1", $str);
        $str = trim($str);
    }
    $regex = array(
        '#^\\040*NULL\\040*$#m',
        '#^\\s*array\\((.*?)\\)\\s*{\\s*$#m',
        '#^\\s*string\\((.*?)\\)\\s*(.*?)$#m',
        '#^\\s*int\\((.*?)\\)\\s*$#m',
        '#^\\s*bool\\(true\\)\\s*$#m',
        '#^\\s*bool\\(false\\)\\s*$#m',
        '#^\\s*float\\((.*?)\\)\\s*$#m',
        '#^\\s*\[(\\d+)\\]\\s*=>\\s*$#m',
        '#\\s*?\\r?\\n\\s*#m',
    );
    $replace = array(
        'N',
        'a:\\1:{',
        's:\\1:\\2',
        'i:\\1',
        'b:1',
        'b:0',
        'd:\\1',
        'i:\\1',
        ';'
    );
    $serialized = preg_replace($regex, $replace, $str);
    $func = create_function(
        '$match', 
        'return "s:".strlen($match[1]).":\\"".$match[1]."\\"";'
    );
    $serialized = preg_replace_callback(
        '#\\s*\\["(.*?)"\\]\\s*=>#', 
        $func,
        $serialized
    );
    $func = create_function(
        '$match', 
        'return "O:".strlen($match[1]).":\\"".$match[1]."\\":".$match[2].":{";'
    );
    $serialized = preg_replace_callback(
        '#object\\((.*?)\\).*?\\((\\d+)\\)\\s*{\\s*;#', 
        $func, 
        $serialized
    );
    $serialized = preg_replace(
        array('#};#', '#{;#'), 
        array('}', '{'), 
        $serialized
    );

    return unserialize($serialized);
}
Run Code Online (Sandbox Code Playgroud)

我在一个复杂的结构上测试它,例如:

array(4) {
  ["foo"]=>
  string(8) "Foo"bar""
  [0]=>
  int(4)
  [5]=>
  float(43.2)
  ["af"]=>
  array(3) {
    [0]=>
    string(3) "123"
    [1]=>
    object(stdClass)#2 (2) {
      ["bar"]=>
      string(4) "bart"
      ["foo"]=>
      array(1) {
        [0]=>
        string(2) "re"
      }
    }
    [2]=>
    NULL
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 我想你误解了这个问题.挑战是将var_dump反转为数组.我熟悉serialize()和unserialize()......是的,它们是更好的选择.这是一个代码挑战.也许这不值得努力,但我想看看它是否可以以优化和创造性的方式完成.我不是在寻找替代解决方案. (2认同)
  • @cdburgess:所以你的问题的标题应该是 **Code Challenge - Convert var_dump back to array/variable** (2认同)

bwo*_*ebi 14

除了手动解析之外别无选择,具体取决于类型.我没有添加对象的支持,但它与数组非常相似; 你只需要做一些反射魔法不仅可以填充公共属性而且不会触发构造函数.

编辑:添加对象的支持......反思魔法......

function unserializeDump($str, &$i = 0) {
    $strtok = substr($str, $i);
    switch ($type = strtok($strtok, "(")) { // get type, before first parenthesis
         case "bool":
             return strtok(")") === "true"?(bool) $i += 10:!$i += 11;
         case "int":
             $int = (int)substr($str, $i + 4);
             $i += strlen($int) + 5;
             return $int;
         case "string":
             $i += 11 + ($len = (int)substr($str, $i + 7)) + strlen($len);
             return substr($str, $i - $len - 1, $len);
         case "float":
             return (float)($float = strtok(")")) + !$i += strlen($float) + 7;
         case "NULL":
             return NULL;
         case "array":
             $array = array();
             $len = (int)substr($str, $i + 6);
             $i = strpos($str, "\n", $i) - 1;
             for ($entries = 0; $entries < $len; $entries++) {
                 $i = strpos($str, "\n", $i);
                 $indent = -1 - (int)$i + $i = strpos($str, "[", $i);
                 // get key int/string
                 if ($str[$i + 1] == '"') {
                     // use longest possible sequence to avoid key and dump structure collisions
                     $key = substr($str, $i + 2, - 2 - $i + $i = strpos($str, "\"]=>\n  ", $i));
                 } else {
                     $key = (int)substr($str, $i + 1);
                     $i += strlen($key);
                 }
                 $i += $indent + 5; // jump line
                 $array[$key] = unserializeDump($str, $i);
             }
             $i = strpos($str, "}", $i) + 1;
             return $array;
         case "object":
             $reflection = new ReflectionClass(strtok(")"));
             $object = $reflection->newInstanceWithoutConstructor();
             $len = !strtok("(") + strtok(")");
             $i = strpos($str, "\n", $i) - 1;
             for ($entries = 0; $entries < $len; $entries++) {
                 $i = strpos($str, "\n", $i);
                 $indent = -1 - (int)$i + $i = strpos($str, "[", $i);
                 // use longest possible sequence to avoid key and dump structure collisions
                 $key = substr($str, $i + 2, - 2 - $i + $i = min(strpos($str, "\"]=>\n  ", $i)?:INF, strpos($str, "\":protected]=>\n  ", $i)?:INF, $priv = strpos($str, "\":\"", $i)?:INF));
                 if ($priv == $i) {
                     $ref = new ReflectionClass(substr($str, $i + 3, - 3 - $i + $i = strpos($str, "\":private]=>\n  ", $i)));
                     $i += $indent + 13; // jump line
                 } else {
                     $i += $indent + ($str[$i+1] == ":"?15:5); // jump line
                     $ref = $reflection;
                 }
                 $prop = $ref->getProperty($key);
                 $prop->setAccessible(true);
                 $prop->setValue($object, unserializeDump($str, $i));
             }
             $i = strpos($str, "}", $i) + 1;
             return $object;

    }
    throw new Exception("Type not recognized...: $type");
}
Run Code Online (Sandbox Code Playgroud)

(在增加字符串位置计数器时$i,这里有很多"魔术"数字,大多数只是关键字的字符串长度和一些括号等)


Frx*_*rem 5

如果你想对这样的数组进行编码/解码,你应该使用var_export(),它在PHP的数组中生成输出,例如:

array(
  1 => 'foo',
  2 => 'bar'
)
Run Code Online (Sandbox Code Playgroud)

可能是它的结果.但是,你必须使用它eval()来获取数组,这是一种潜在的危险方式(特别是因为eval()真正执行PHP代码,因此简单的代码注入可以使黑客能够控制你的PHP脚本).

一些甚至更好的解决方案是serialize(),它创建任何数组或对象的序列化版本; 并且json_encode(),它使用JSON格式编码任何数组或对象(对于不同语言之间的数据交换,它更受欢迎).


mvd*_*vds 5

诀窍是通过代码块 和 进行匹配"strings",并且在字符串上不执行任何操作,但进行替换:

$out = preg_replace_callback('/"[^"]*"|[^"]+/','repl',$in);

function repl($m)
{
    return $m[0][0]=='"'?
        str_replace('"',"'",$m[0])
    :
        str_replace("(,","(",
            preg_replace("/(int\((\d+)\)|\s*|(string|)\(\d+\))/","\\2",
                strtr($m[0],"{}[]","(), ")
            )
        );
}
Run Code Online (Sandbox Code Playgroud)

输出:

array('this'=>array('is'=>'the'),'challenge'=>array('for'=>array(0=>'you')))
Run Code Online (Sandbox Code Playgroud)

(删除从 0 开始的升序数字键需要一些额外的计算,这可以在repl函数中完成。)

附:这并不能解决包含 的字符串的问题",但由于 var_dump 似乎没有转义字符串内容,因此无法可靠地解决该问题。(您可以匹配,但也\["[^"]*"\]可以包含字符串)"]