47

今日までこれについて考えたことはありませんでしたが、ウェブを検索しても何も見つかりませんでした。たぶん、検索でそれを正しく表現していなかったのでしょう。

与えられた配列 (多次元かどうかに関係なく):

$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 はここでは解決策ではありません。また、それらは最良の答えでもありません。

4

7 に答える 7

70

var_exportまたはserializeあなたが探しているものです。 var_exportPHP の解析可能な配列構文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
  }
}
于 2010-08-20T14:36:43.033 に答える
16

タイプによっては、手動で解析する以外に方法はありません。オブジェクトのサポートは追加しませんでしたが、配列のサポートと非常によく似ています。パブリック プロパティだけでなく、コンストラクターをトリガーしないようにするために、リフレクション マジックを実行する必要があります。

編集:オブジェクトのサポートを追加しました...反射魔法...

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。ほとんどは、キーワードの文字列の長さといくつかの括弧などです。)

于 2014-05-12T12:13:16.493 に答える
6

このような配列をエンコード/デコードする場合はvar_export()、PHP の配列に出力を生成する を使用する必要があります。たとえば、次のようになります。

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

その結果かもしれません。eval()ただし、配列を戻すには を使用する必要があり、これは潜在的に危険な方法です (特に、eval()実際には PHP コードを実行するため、単純なコード インジェクションによって、ハッカーが PHP スクリプトを制御できるようになる可能性があります)。

さらに優れた解決策としてserialize()、任意の配列またはオブジェクトのシリアル化されたバージョンを作成する があります。およびjson_encode()、任意の配列またはオブジェクトをJSON形式でエンコードします (これは、異なる言語間のデータ交換に適しています)。

于 2010-08-20T14:39:40.023 に答える
5

秘訣は、コードのチャンクと で一致さ"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 は文字列の内容をエスケープしないように見えるため、確実に解決する方法はありません。(一致する可能\["[^"]*"\]性がありますが、文字列にも含ま"]れる場合があります)

于 2010-08-20T15:14:37.417 に答える
1

regexp を使用して array(.) { (.*) } を array($1) に変更し、コードを eval します。これは、書かれているほど簡単ではありません。対応するブラケットなどを処理する必要があるためです。解決策を見つける方法の手がかりにすぎません。 ;)

  • これは、var_dump を var_export に変更したり、シリアル化したりできない場合に役立ちます。
于 2010-08-20T14:36:39.127 に答える
0

関数を探していると思いますserialize

serialize — 値の格納可能な表現を生成します

配列の内容を読み取り可能な形式で保存し、後で関数を使用して配列を読み取ることができますunserialize

これらの関数を使用すると、データベースだけでなく、テキスト/フラット ファイルでも配列を格納/取得できます。

于 2010-08-20T14:36:41.097 に答える