将数组的var_dump转换回数组variables
我从来没有真正想过这个,直到今天,但searchnetworking后,我什么都没有find。 也许我不是在search中正确的用词。
给定一个数组(多维或不):
$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()
函数。 数据是否全部在浏览器中输出,或者是否包含换行输出到terminal。
这只是一个正则expression式的问题吗? 或者有其他的方法吗? 我正在寻找创造力。
更新:注意。 我熟悉序列化和反序列化的人。 我不在寻找替代解决scheme。 这是一个代码挑战,看看它是否能够以优化和创造性的方式完成。 所以serialize和var_export在这里不是解决scheme 。 他们也不是最好的答案。
var_export
或serialize
是你在找什么。 var_export
将呈现PHP可分析的数组语法,而serialize
将呈现非人类可读但可逆的“数组到string”的转换…
编辑好吧,挑战:
基本上,我把输出转换成一个序列化的string(然后反序列化)。 我不认为这是完美的,但它似乎工作在一些非常复杂的结构,我试过了…
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); }
我在一个复杂的结构上testing它,例如:
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 } }
除了手动parsing外,没有别的方法取决于types。 我没有添加对象的支持,但它非常类似于数组; 你只需要做一些reflection魔术来填充不仅公共属性和不触发构造函数。
编辑:添加对象的支持…反思魔术…
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"); }
(当递增string位置计数器$i
,这里有很多“魔术”数字,主要是关键字的string长度和一些括号等)
如果你想这样编码/解码一个数组,你应该使用var_export()
,它会在PHP的数组中生成输出,例如:
array( 1 => 'foo', 2 => 'bar' )
可能是它的结果。 但是,你必须使用eval()
来返回数组,这是一个潜在的危险的方法(尤其是因为eval()
真的在执行PHP代码,所以简单的代码注入可以使黑客能够控制你的PHP脚本)。
一些甚至更好的解决scheme是serialize()
,它创build任何数组或对象的序列化版本; 和json_encode()
,它使用JSON格式对数组或对象进行编码(这对于不同语言之间的数据交换更为优选)。
诀窍是通过大量的代码和"strings"
进行匹配,而对string不做任何事情,除此之外做replace:
$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。 这并不能解决包含"
的string问题,但是因为var_dump似乎不能转义string内容,所以没有办法可靠地解决这个问题(你可以匹配\["[^"]*"\]
但是一个string也可以包含"]
)
使用regexp将数组(。){(。*)}更改为数组($ 1)并对代码进行评估,因为您不得不处理匹配的括号等,只是寻找解决scheme的线索;)
- 如果您不能将var_dump更改为var_export或序列化,这将会很有帮助
我想你正在寻找serialize
函数:
序列化 – 生成值的可存储表示
它允许您以可读的格式保存数组内容,之后您可以使用反unserialize
函数读取数组。
使用这些函数,您甚至可以在文本/平面文件和数据库中存储/检索数组。