代码之家  ›  专栏  ›  技术社区  ›  Chuck Burgess

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

  •  41
  • Chuck Burgess  · 技术社区  · 14 年前

    直到今天,我才真正想到这个问题,但是在搜索了网络之后,我没有真正找到任何东西。也许我在搜索中用词不当。

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

    $data = array('this' => array('is' => 'the'), 'challenge' => array('for' => array('you')));
    

    当var_转储时:

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

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

    这只是一个regex的问题吗?还是有别的办法?我在寻找创造力。

    更新:注意事项。我熟悉序列化和非序列化的人。我不想寻找其他的解决方案。这是一个代码挑战,看看是否可以以优化和创造性的方式完成。所以 序列化和var_导出不是解决方案 在这里。它们也不是最好的答案。

    7 回复  |  直到 6 年前
        1
  •  58
  •   Pang firemonkey    10 年前

    var_export serialize 就是你要找的。 瓦尔出口 将呈现php可分析数组语法,以及 串行化 将呈现非人类可读但可逆的“数组到字符串”转换…

    编辑 好吧,对于挑战:

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

    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);
    }
    

    我在一个复杂的结构上进行了测试,比如:

    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
      }
    }
    
        2
  •  13
  •   bwoebi    9 年前

    除了根据类型手动解析之外,没有其他方法。 我没有添加对对象的支持,但它与数组1非常相似;您只需要执行一些反射魔法,以不仅填充公共属性,而且不触发构造函数。

    编辑:添加了对对象的支持…反射魔法…

    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");
    }
    

    (在增加字符串位置计数器时,这里有很多“神奇”的数字 $i ,主要是关键字的字符串长度和一些括号等。)

        3
  •  5
  •   Frxstrem    14 年前

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

    array(
      1 => 'foo',
      2 => 'bar'
    )
    

    可能是它的结果。你必须使用 eval() 但是,要想取回阵列,这是一种潜在的危险方法(尤其是在 表达式() 真正执行PHP代码,所以简单的代码注入可以使黑客能够控制您的PHP脚本)。

    一些更好的解决方案是 serialize() 创建任何数组或对象的序列化版本;以及 json_encode() ,它用 JSON 格式(对于不同语言之间的数据交换更为首选)。

        4
  •  4
  •   mvds    14 年前

    诀窍是按代码块匹配 "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],"{}[]","(), ")
                )
            );
    }
    

    输出:

    array('this'=>array('is'=>'the'),'challenge'=>array('for'=>array(0=>'you')))
    

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

    PS。 这并不能解决字符串包含 " 但是,由于var_dump似乎没有转义字符串内容,因此无法可靠地解决这个问题。(你可以匹配 \["[^"]*"\] 但字符串可以包含 "] 同样)

        5
  •  1
  •   canni    14 年前

    使用regexp将array(.)(.*)更改为array($1)并计算代码,这并不像编写那样容易,因为您必须处理匹配的括号等,这只是如何找到解决方案的线索;)

    • 如果无法将var_dump更改为var_export或serialize,这将很有帮助。
        6
  •  0
  •   Sarfraz    14 年前

    我想你在找 serialize 功能:

    serialize生成可存储的 值的表示

    它允许您以可读的格式保存数组的内容,稍后您可以使用 unserialize 功能。

    使用这些函数,您甚至可以在文本/平面文件以及数据库中存储/检索数组。

        7
  •  -1
  •   Aurimas RekÅ¡tys    6 年前

    更新为不使用create_函数,因为从php 7.2.0开始就不推荐使用该函数。相反,它被替换为使用匿名函数:

    
    
        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 = function($match) {
                return 's:'.strlen($match[1]).':"'.$match[1].'"';
            };
            $serialized = preg_replace_callback(
                '#\s*\["(.*?)"\]\s*=>#', 
                $func,
                $serialized
            );
            $func = 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);
        }
    
        $test = 'array(10) {
          ["status"]=>
          string(1) "1"
          ["transactionID"]=>
          string(14) "1532xxx"
          ["orderID"]=>
          string(10) "1532xxx"
          ["value"]=>
          string(8) "0.73xxx"
          ["address"]=>
          string(1) "-"
          ["confirmations"]=>
          string(3) "999"
          ["transaction_hash"]=>
          string(64) "internxxx"
          ["notes"]=>
          string(0) ""
          ["txCost"]=>
          string(1) "0"
          ["txTimestamp"]=>
          string(10) "1532078165"
        }';
        var_export(unvar_dump($test));