Преобразовать дамп var массива обратно в переменную массива
Я никогда по-настоящему не задумывался об этом до сегодняшнего дня, но после поиска в Интернете я действительно ничего не нашел. Может быть, я не совсем правильно выразился при поиске.
Заданный массив (с несколькими измерениями или без):
$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 здесь не являются решениями. И это не самые лучшие ответы.
7 ответов:
var_export
илиserialize
это то, что ты ищешь.var_export
отобразит синтаксис разбираемого массива PHP, аserialize
-нечеловеческое, но обратимое преобразование "массив в строку"...Edit хорошо, для вызова:
В основном, я преобразую выходные данные в сериализованную строку (а затем несериализую ее). Я не утверждаю, что это идеально, но, похоже, это работает на некоторых довольно сложных структурах, которые я пробовал...
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); }
I испытал его на сложной структуре, такой как:
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 } }
Нет другого способа, кроме ручного разбора в зависимости от типа. Я не добавил поддержку объектов, но она очень похожа на массивы; вам просто нужно сделать некоторую магию отражения, чтобы заполнить не только открытые свойства и не запускать конструктор.
EDIT: добавлена поддержка объектов... Магия отражения...
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
, в основном просто длины строк ключевых слов и некоторые скобки и т.д.)
Если вы хотите закодировать/декодировать массив, подобный этому, вы должны либо использовать
var_export()
, который генерирует выходные данные в массиве PHP для, например:array( 1 => 'foo', 2 => 'bar' )
Может быть результатом этого. Однако вам придется использовать
eval()
, чтобы получить массив обратно, и это потенциально опасный способ (тем более, чтоeval()
действительно выполняет PHP-код, поэтому простая инъекция кода может сделать хакеров способными получить контроль над вашим PHP-скриптом).Некоторые даже лучшие решения являются
serialize()
, который создает сериализованную версию любого массива или объекта; иjson_encode()
, который кодирует любой массив или объект в формате JSON (что более предпочтительно для обмена данными между различными языками).
Хитрость заключается в том, чтобы сопоставлять куски кода и
"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
.)ПС. это не решает проблему строк, содержащих
"
, но поскольку кажется, что var_dump не экранирует содержимое строки, нет никакого способа решить это надежно. (вы могли бы соответствовать\["[^"]*"\]
но строка также может содержать"]
)
Используйте регулярное выражение для изменения массива (.) { (.*) } в массив($1) и eval код, это не так просто, как написано, потому что вам придется иметь дело с соответствующими кронштейнами и т. д., просто подсказка о том, как найти решение ;)
- это будет полезно, если вы не можете изменить var_dump на var_export или сериализовать
Я думаю, что вы ищете
serialize
функция:Serialize-создает хранимый объект представление значения
Это позволяет сохранить содержимое массива в удобочитаемом формате, а затем вы можете прочитать массив обратно с помощью
unserialize
функция.Используя эти функции, вы можете хранить/извлекать массивы даже в текстовых / плоских файлах, а также в базе данных.
Обновлено, чтобы не использовать 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));