【问题标题】:Convert var_dump of array back to array variable将数组的 var_dump 转换回数组变量
【发布时间】:2011-04-01 16:54:39
【问题描述】:

直到今天我才真正考虑过这个问题,但在网上搜索后,我并没有真正找到任何东西。也许我在搜索中的措辞不正确。

给定一个数组(是否有多个维度):

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

当 var_dumped 时:

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

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

这只是正则表达式的问题吗?还是有其他方法?我正在寻找创意。

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

【问题讨论】:

  • 是的,可以通过解析来实现。不,这不是你通常想要打扰的事情,因为如果你真的需要这个,你就做错了。或许可以以此为基础提出一个社区 Wiki Code Golf 问题,然后就可以了。
  • 这绝对是可能的,但它不会是微不足道的,因为语法并不是机器可解析的。当您遇到string(8) "Foo"bar" 和其他奇怪的边缘情况时,在可靠的庄园中实施会变得相对混乱......如果有优雅的解决方案,我很乐意看到它们。但是要意识到,大多数完全有效的解决方案可能会相当冗长,并且内部有相当多的逻辑......
  • var_export() 有什么问题?
  • 没什么...除了这个问题不是关于使用 var_dump 的替代品。这是关于获取一个已经 var_dumped 的字符串并将其返回到 var_dumped 之前的状态。
  • 只是我还是“When var_dumped:”示例实际上不是要转储的内容?

标签: php var-dump


【解决方案1】:

var_exportserialize 是您正在寻找的内容。 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);
}

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

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

【讨论】:

  • @Gordon 你打败了我。我正要回去编辑这些链接。谢谢!
  • 我认为您误解了这个问题。挑战是将 var_dump 反转为数组。我熟悉 serialize() 和 unserialize()... 是的,它们是迄今为止更好的选择。这是一个代码挑战。也许不值得付出努力,但我想看看是否可以以优化和创造性的方式完成。我不是在寻找替代解决方案。
  • @cdburgess:很奇怪,你到底想做什么?
  • @cdburgess:所以你的问题的标题应该是Code Challenge - Convert var_dump back to array/variable
  • 您好,您的方法不适用于 Flickr var_dump 数组。警告:strpos() 期望参数 1 是字符串,数组在第 28 行的 /opt/lampp/htdocs/phpflickr/example.php 中给出 注意:数组到字符串的转换在 /opt/lampp/htdocs/phpflickr/example.php 上第 59 行警告:unserialize() 期望参数 1 是字符串,数组在第 84 行的 /opt/lampp/htdocs/phpflickr/example.php 中给出
【解决方案2】:

根据类型,除了手动解析之外别无他法。 我没有添加对对象的支持,但它与数组非常相似;你只需要做一些反射魔法来填充公共属性并且不触发构造函数。

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

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时的“神奇”数字,主要是关键字的字符串长度和一些括号等)

【讨论】:

  • 谢谢!我喜欢你的方法,但有些字符串无法正确解析,例如:'string(6) "ab};cd"' 返回d"
  • @georg 哦,那是一个愚蠢的错误,在错误的地方写了太多strlen()。更好的? — 我只是没有注意到它,因为我总是用长度为 1 的字符串进行测试...
  • Mod Note:此答案已合并到from this other question,经过提问者和回答者的验证,我标记为欺骗并合并。
  • @AndrewBarber *crying* 我现在没有得到我的赏金 :-( &lt;/joking&gt; 谢谢你合并它,现在很好:-)
  • 呵呵...我确实让@georg 知道,如果他愿意,他可以在这里添加赏金! ;) 但是,是的;谢谢!
【解决方案3】:

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

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

可能是它的结果。但是,您必须使用eval() 来取回数组,这是一种潜在的危险方式(尤其是因为eval() 确实执行PHP 代码,因此简单的代码注入可以使黑客能够控制您的PHP 脚本)。

一些更好的解决方案是serialize(),它可以创建任何数组或对象的序列化版本;和json_encode(),它使用JSON 格式对任何数组或对象进行编码(更适合不同语言之间的数据交换)。

【讨论】:

    【解决方案4】:

    诀窍是通过代码块和"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 不会转义字符串内容,因此无法可靠地解决该问题。 (您可以匹配\["[^"]*"\],但字符串也可能包含"]

    【讨论】:

    • 这太棒了!您是少数真正阅读并理解该问题的人之一。感谢您接受挑战并提供有效的解决方案。现在如果有一个 INT(5) 作为值怎么办? (即array('you',2))它将显示为 int(5) 但应从您的函数返回为 5。
    • 我只是以你的例子来使它工作。用数字替换int\(\d+\) 听起来并不是什么大问题。查看更新的答案。
    • 太棒了!做得很好,并且在小的优化代码中!仅供参考:“\\2”之后缺少逗号。
    【解决方案5】:

    使用正则表达式将 array(.) { (.*) } 更改为 array($1) 并评估代码,这并不像写的那么容易,因为你必须处理匹配的括号等,只是一个关于如何的线索寻找解决方案;)

    • 如果您无法将 var_dump 更改为 var_export 或序列化,这将很有帮助

    【讨论】:

    • 正则表达式解决方案将非常困难,因为您可以使用嵌套的大括号...所以它更可能涉及字符串解析器而不是正则表达式(考虑到由于嵌套而需要担心状态)...
    • no 你不必处理字符串解析器,正则表达式有一些极好的功能,如非贪婪/全局标志等,它可以用一个带有正确设置标志的正则表达式来完成 :)
    • BBcode 解析器建立在正则表达式之上,并且在没有状态机的情况下也能很好地工作;) 只需将 'array(.) {' 和 '}' 视为关闭/打开标签 :)
    • 然后给我看一个正则表达式,它将所有有效的 var_dumped 数据转换回本机可解析的 php...如果你能给我看一个可以处理的正则表达式的例子,我承认我错了与:array(1) { ["foo}[bar]"] =&gt; string(4) "baz{" }
    • 你可能是对的,它不能只用一个正则表达式来完成,但是,你可以为每个“标签”使用一个正则表达式,其中标签是以下之一: array(.) ;细绳(。) ; integer(.) 等并以正确的顺序解析输出(简单类型 -> 数组),但仍然无法“重新解析”var_dumped 对象和其他非标准结构,为此我们有序列化和其他内容
    【解决方案6】:

    我认为您正在寻找serialize 函数:

    serialize — 生成一个可存储的 值的表示

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

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

    【讨论】:

      【解决方案7】:

      更新为不使用 create_function,因为它在 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));

      【讨论】:

        猜你喜欢
        • 2015-04-20
        • 1970-01-01
        • 1970-01-01
        • 2012-02-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多