今日まで考えたことはありませんでしたが、ウェブを検索しても何も見つかりませんでした。たぶん私は検索でそれを正しく表現していませんでした。
与えられた配列(多次元かどうか):
_$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()
関数のように。データがすべてブラウザーの出力として1行にあるのか、それとも端末への出力として改行が含まれるのか。
それは単に正規表現の問題ですか?それとも他の方法がありますか?私は創造性を探しています。
更新:注。私は人々のシリアライズとシリアライズ解除に精通しています。私は代替ソリューションを探していません。これは、最適化されたクリエイティブな方法で実行できるかどうかを確認するためのコードの課題です。したがってserializeとvar_exportはソリューションではありませんここにあります。また、それらは最良の答えでもありません。
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);
}
次のような複雑な構造でテストしました。
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
}
}
タイプに応じて手動で解析する以外に方法はありません。オブジェクトのサポートは追加しませんでしたが、配列のサポートと非常に似ています。パブリックプロパティだけでなく、コンストラクターもトリガーしないようにするために、リフレクションマジックを実行する必要があるだけです。
編集:オブジェクトのサポートを追加...リフレクションマジック...
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 codeを実行するため)なので、単純なコードインジェクションにより、ハッカーが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
関数で実行できる少し余分なアカウンティングが必要です。)
ps。これは"
を含む文字列の問題を解決しませんが、var_dumpは文字列の内容をエスケープしないと思われるため、これを確実に解決する方法はありません。 (\["[^"]*"\]
と一致させることもできますが、文字列には"]
も含まれる場合があります)
Regexpを使用してarray(。){(。*)}をarray($ 1)に変更し、コードを評価します。これは、対応する大括弧などを処理する必要があるため、記述を見つけるのは簡単ではありません。 ;)
serialize
関数を探していると思います。
serialize —値の保存可能な表現を生成します
配列の内容を読み取り可能な形式で保存し、後で unserialize
関数を使用して配列を読み取ることができます。
これらの関数を使用して、データベースだけでなくテキスト/フラットファイルにも配列を格納/取得できます。
PHP 7.2.0以降廃止されたため、create_functionを使用しないように更新されました。代わりに、匿名関数を使用するように置き換えられます。
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));