web-dev-qa-db-fra.com

Reconvertir var_dump du tableau en variable de tableau

Je n'ai jamais vraiment pensé à cela jusqu'à aujourd'hui, mais après avoir cherché sur le Web, je n'ai rien trouvé. Peut-être que je ne le formulais pas directement dans la recherche.

Étant donné un tableau (de dimensions multiples ou non):

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

Quand var_dumped:

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

Le défi est le suivant: quelle est la meilleure méthode optimisée pour recompiler le tableau en un tableau utilisable pour PHP? Comme une fonction undump_var(). Si les données sont toutes sur une ligne en sortie dans un navigateur ou si elles contiennent les sauts de ligne en sortie vers le terminal.

Est-ce juste une question de regex? Ou y a-t-il un autre moyen? Je cherche de la créativité.

UPDATE: Note. Je connais les personnes sérialiser et désérialiser. Je ne cherche pas de solutions alternatives. C'est un défi de code pour voir si cela peut être fait d'une manière optimisée et créative. So serialize et var_export ne sont pas des solutions ici. Ce ne sont pas non plus les meilleures réponses.

40
Chuck Burgess

var_export ou serialize est ce que vous cherchez. var_export restituera une syntaxe de tableau analysable PHP, et serialize rendra une conversion non-humain-lisible mais réversible "tableau en chaîne" ...

Edit Bon, pour le challenge:

Fondamentalement, je convertis la sortie en une chaîne sérialisée (puis la désérialiser). Je ne prétends pas que ce soit parfait, mais cela semble fonctionner sur des structures assez complexes que j'ai essayées ...

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

Je l'ai testé sur une structure complexe telle que:

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

Il n'y a pas d'autre moyen que d'analyser manuellement en fonction du type ..__ Je n'ai pas ajouté de support pour les objets, mais c'est très similaire à celui des tableaux; vous avez juste besoin de faire de la magie de réflexion pour renseigner non seulement les propriétés publiques et pour ne pas déclencher le constructeur.

EDIT: Ajout du support pour les objets ... Réflexion magique ...

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");
}

(Voici beaucoup de nombres "magiques" lors de l'incrémentation du compteur de position de chaîne $i, principalement des longueurs de chaîne des mots-clés et des parenthèses, etc.)

14
bwoebi

Si vous voulez encoder/décoder un tableau comme ceci, vous devez soit utiliser var_export() , qui génère une sortie dans le tableau de PHP pour, par exemple:

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

pourrait en être le résultat. Cependant, vous devrez utiliser eval() pour récupérer le tableau. C’est une méthode potentiellement dangereuse (d’autant plus que eval() exécute vraiment PHP) code, de sorte qu’une simple injection de code pourrait permettre aux pirates de contrôler PHP script).

Certaines solutions encore meilleures sont serialize() , qui crée une version sérialisée de tout tableau ou objet; et json_encode() , qui code n'importe quel tableau ou objet au format JSON (qui est préférable pour l'échange de données entre différentes langues).

5
Frxstrem

L'astuce consiste à faire correspondre des morceaux de code et "strings", et sur les chaînes, ne faites rien, sinon faites les remplacements:

$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],"{}[]","(), ")
            )
        );
}

les sorties:

array('this'=>array('is'=>'the'),'challenge'=>array('for'=>array(0=>'you')))

(La suppression des touches numériques croissantes commençant à 0 nécessite un peu plus de comptabilité, ce qui peut être fait dans la fonction repl.)

ps. cela ne résout pas le problème des chaînes contenant ", mais comme il semble que var_dump n'échappe pas au contenu de la chaîne, il n'existe aucun moyen de résoudre ce problème de manière fiable. (vous pouvez correspondre à \["[^"]*"\] mais une chaîne peut aussi contenir "])

4
mvds

Utilisez regexp pour changer array (.) {(. *)} En array ($ 1) et évaluez le code, ce n'est pas si facile à écrire car vous devez gérer les parenthèses correspondantes, etc. ;)

  • cela sera utile si vous ne pouvez pas changer var_dump en var_export, ou sérialiser
1
canni

Je pense que vous recherchez la fonction serialize :

sérialiser - Génère un stockable représentation d'une valeur

Il vous permet de sauvegarder le contenu du tableau dans un format lisible et plus tard, vous pourrez le lire avec la fonction unserialize .

En utilisant ces fonctions, vous pouvez stocker/récupérer les tableaux même dans des fichiers texte/plats ainsi que dans des bases de données.

0
Sarfraz