web-dev-qa-db-fra.com

unserialize () [function.unserialize]: erreur au décalage

J'utilise Hotaru CMS avec le plugin Image Upload, je reçois cette erreur si j'essaie de joindre une image à un message, sinon il n'y a pas d'erreur.

Le code incriminé (l’erreur pointe sur la ligne avec **):

/**
     * Retrieve submission step data
     *
     * @param $key - empty when setting
     * @return bool
     */
    public function loadSubmitData($h, $key = '')
    {
        // delete everything in this table older than 30 minutes:
        $this->deleteTempData($h->db);

        if (!$key) { return false; }

        $cleanKey = preg_replace('/[^a-z0-9]+/','',$key);
        if (strcmp($key,$cleanKey) != 0) {
            return false;
        } else {
            $sql = "SELECT tempdata_value FROM " . TABLE_TEMPDATA . " WHERE tempdata_key = %s ORDER BY tempdata_updatedts DESC LIMIT 1";
            $submitted_data = $h->db->get_var($h->db->prepare($sql, $key));
            **if ($submitted_data) { return unserialize($submitted_data); } else { return false; }** 
        }
    }

Les données de la table, notez que le bit de fin contient les informations sur l'image, je ne suis pas un expert en PHP, donc je me demandais ce que vous en penseriez?

tempdata_value:

a:10:{s:16:"submit_editorial";b:0;s:15:"submit_orig_url";s:13:"www.bbc.co.uk";s:12:"submit_title";s:14:"No title found";s:14:"submit_content";s:12:"dnfsdkfjdfdf";s:15:"submit_category";i:2;s:11:"submit_tags";s:3:"bbc";s:9:"submit_id";b:0;s:16:"submit_subscribe";i:0;s:15:"submit_comments";s:4:"open";s:5:"image";s:19:"C:fakepath100.jpg";}

Edit: Je pense avoir trouvé le bit de sérialisation ...

/**
     * Save submission step data
     *
     * @return bool
     */
    public function saveSubmitData($h)
    {
        // delete everything in this table older than 30 minutes:
        $this->deleteTempData($h->db);

        $sid = preg_replace('/[^a-z0-9]+/i', '', session_id());
        $key = md5(microtime() . $sid . Rand());
        $sql = "INSERT INTO " . TABLE_TEMPDATA . " (tempdata_key, tempdata_value, tempdata_updateby) VALUES (%s,%s, %d)";
        $h->db->query($h->db->prepare($sql, $key, serialize($h->vars['submitted_data']), $h->currentUser->id));
        return $key;
    }
81
user576820

unserialize() [function.unserialize]: Error at offset était dû à invalid serialization data en raison d'une longueur non valide

Solution rapide  

Ce que vous pouvez faire, c'est recalculating the length des éléments du tableau sérialisé

Vous avez actuellement des données sérialisées  

$data = 'a:10:{s:16:"submit_editorial";b:0;s:15:"submit_orig_url";s:13:"www.bbc.co.uk";s:12:"submit_title";s:14:"No title found";s:14:"submit_content";s:12:"dnfsdkfjdfdf";s:15:"submit_category";i:2;s:11:"submit_tags";s:3:"bbc";s:9:"submit_id";b:0;s:16:"submit_subscribe";i:0;s:15:"submit_comments";s:4:"open";s:5:"image";s:19:"C:fakepath100.jpg";}';

Exemple sans recalcul  

var_dump(unserialize($data));

Sortie 

Notice: unserialize() [function.unserialize]: Error at offset 337 of 338 bytes

Recalculer  

$data = preg_replace('!s:(\d+):"(.*?)";!e', "'s:'.strlen('$2').':\"$2\";'", $data);
var_dump(unserialize($data));

Sortie

array
  'submit_editorial' => boolean false
  'submit_orig_url' => string 'www.bbc.co.uk' (length=13)
  'submit_title' => string 'No title found' (length=14)
  'submit_content' => string 'dnfsdkfjdfdf' (length=12)
  'submit_category' => int 2
  'submit_tags' => string 'bbc' (length=3)
  'submit_id' => boolean false
  'submit_subscribe' => int 0
  'submit_comments' => string 'open' (length=4)
  'image' => string 'C:fakepath100.jpg' (length=17)

Recommandation .. I

Au lieu d’utiliser ce genre de solution rapide ... je vous conseille de mettre à jour la question avec 

  • Comment vous sérialisez vos données

  • Comment tu le sauves ..

=============================== EDIT 1 ============== =================  

L'erreur  

L'erreur a été générée à cause de l'utilisation du guillemet double " au lieu d'un guillemet simple ', c'est pourquoi C:\fakepath\100.png a été converti en C:fakepath100.jpg 

Pour corriger l'erreur  

Vous devez changer $h->vars['submitted_data'] De (notez bien le ')

Remplacer

 $h->vars['submitted_data']['image'] = "C:\fakepath\100.png" ;

Avec

 $h->vars['submitted_data']['image'] = 'C:\fakepath\100.png' ;

Filtre supplémentaire  

Vous pouvez également ajouter ce filtre simple avant d'appeler sérialiser 

function satitize(&$value, $key)
{
    $value = addslashes($value);
}

array_walk($h->vars['submitted_data'], "satitize");

Si vous avez des caractères UTF, vous pouvez également exécuter 

 $h->vars['submitted_data'] = array_map("utf8_encode",$h->vars['submitted_data']);

Comment détecter le problème dans de futures données sérialisées  

  findSerializeError ( $data1 ) ;

Sortie

Diffrence 9 != 7
    -> ORD number 57 != 55
    -> Line Number = 315
    -> Section Data1  = pen";s:5:"image";s:19:"C:fakepath100.jpg
    -> Section Data2  = pen";s:5:"image";s:17:"C:fakepath100.jpg
                                            ^------- The Error (Element Length)

findSerializeError Fonction 

function findSerializeError($data1) {
    echo "<pre>";
    $data2 = preg_replace ( '!s:(\d+):"(.*?)";!e', "'s:'.strlen('$2').':\"$2\";'",$data1 );
    $max = (strlen ( $data1 ) > strlen ( $data2 )) ? strlen ( $data1 ) : strlen ( $data2 );

    echo $data1 . PHP_EOL;
    echo $data2 . PHP_EOL;

    for($i = 0; $i < $max; $i ++) {

        if (@$data1 {$i} !== @$data2 {$i}) {

            echo "Diffrence ", @$data1 {$i}, " != ", @$data2 {$i}, PHP_EOL;
            echo "\t-> ORD number ", ord ( @$data1 {$i} ), " != ", ord ( @$data2 {$i} ), PHP_EOL;
            echo "\t-> Line Number = $i" . PHP_EOL;

            $start = ($i - 20);
            $start = ($start < 0) ? 0 : $start;
            $length = 40;

            $point = $max - $i;
            if ($point < 20) {
                $rlength = 1;
                $rpoint = - $point;
            } else {
                $rpoint = $length - 20;
                $rlength = 1;
            }

            echo "\t-> Section Data1  = ", substr_replace ( substr ( $data1, $start, $length ), "<b style=\"color:green\">{$data1 {$i}}</b>", $rpoint, $rlength ), PHP_EOL;
            echo "\t-> Section Data2  = ", substr_replace ( substr ( $data2, $start, $length ), "<b style=\"color:red\">{$data2 {$i}}</b>", $rpoint, $rlength ), PHP_EOL;
        }

    }

}

Un meilleur moyen de sauvegarder dans la base de données  

$toDatabse = base64_encode(serialize($data));  // Save to database
$fromDatabase = unserialize(base64_decode($data)); //Getting Save Format 
200
Baba

Je n'ai pas assez de réputation pour commenter, alors j'espère que cela sera vu par les personnes utilisant la réponse "correcte" ci-dessus:

Depuis php 5.5, le modificateur/e dans preg_replace () est totalement obsolète et le preg_match ci-dessus affichera une erreur. La documentation php recommande d'utiliser preg_match_callback à sa place.

Veuillez trouver la solution suivante en guise d'alternative à la proposition preg_match proposée ci-dessus.

$fixed_data = preg_replace_callback ( '!s:(\d+):"(.*?)";!', function($match) {      
    return ($match[1] == strlen($match[2])) ? $match[0] : 's:' . strlen($match[2]) . ':"' . $match[2] . '";';
},$bad_data );
53
r00tAcc3ss

Il y a une autre raison pour laquelle unserialize() a échoué parce que vous avez mal introduit des données sérialisées dans la base de données, voir Official Explanation here. Puisque serialize() renvoie des données binaires et que les variables php ne s’intéressent pas aux méthodes de codage, le fait de le mettre dans TEXT, VARCHAR () provoquera cette erreur.

Solution: stockez les données sérialisées dans BLOB dans votre table. 

12
Ge Rong

Solution rapide

Recalculer la longueur des éléments dans un tableau sérialisé - mais ne pas utiliser (preg_replace), il est déconseillé - mieux utiliser

$data = preg_replace_callback('!s:(\d+):"(.*?)";!', function($m) { return 's:'.mb_strlen($m[2]).':"'.$m[2].'";'; }, $data);
8
adilbo

fonction publique unserializeKeySkills ($ string) {

    $output = array();
    $string = trim(preg_replace('/\s\s+/', ' ',$string));
    $string = preg_replace_callback('!s:(\d+):"(.*?)";!', function($m) { return 's:'.strlen($m[2]).':"'.$m[2].'";'; }, utf8_encode( trim(preg_replace('/\s\s+/', ' ',$string)) ));
    try {
        $output =  unserialize($string);
    } catch (\Exception $e) {
        \Log::error("unserialize Data : " .print_r($string,true));
    }
    return $output;
}
4
Pardeep Goyal

Cette erreur est due au fait que votre jeu de caractères est incorrect.

Définir le jeu de caractères après la balise ouverte: 

header('Content-Type: text/html; charset=utf-8');

Et définissez charset utf8 dans votre base de données: 

mysql_query("SET NAMES 'utf8'");
4
Will

Vous pouvez réparer une chaîne sérialisée cassée en utilisant la fonction suivante, avec caractère multi-octets traitement.

function repairSerializeString($value)
{

    $regex = '/s:([0-9]+):"(.*?)"/';

    return preg_replace_callback(
        $regex, function($match) {
            return "s:".mb_strlen($match[2]).":\"".$match[2]."\""; 
        },
        $value
    );
}
3
Rajesh Meniya
$badData = 'a:2:{i:0;s:16:"as:45:"d";
Is \n";i:1;s:19:"as:45:"d";
Is \r\n";}';

Vous ne pouvez pas réparer une chaîne sérialisée cassée en utilisant les expressions rationnelles proposées:

$data = preg_replace('!s:(\d+):"(.*?)";!e', "'s:'.strlen('$2').':\"$2\";'", $badData);
var_dump(@unserialize($data)); // Output: bool(false)

// or

$data = preg_replace_callback(
    '/s:(\d+):"(.*?)";/',
    function($m){
        return 's:' . mb_strlen($m[2]) . ':"' . $m[2] . '";';
    },
    $badData
);
var_dump(@unserialize($data)); // Output: bool(false)

Vous pouvez réparer une chaîne sérialisée cassée en utilisant les expressions rationnelles suivantes:

$data = preg_replace_callback(
    '/(?<=^|\{|;)s:(\d+):\"(.*?)\";(?=[asbdiO]\:\d|N;|\}|$)/s',
    function($m){
        return 's:' . mb_strlen($m[2]) . ':"' . $m[2] . '";';
    },
    $badData
);

var_dump(@unserialize($data));

Sortie

array(2) {
  [0] =>
  string(17) "as:45:"d";
Is \n"
  [1] =>
  string(19) "as:45:"d";
Is \r\n"
}

ou

array(2) {
  [0] =>
  string(16) "as:45:"d";
Is \n"
  [1] =>
  string(18) "as:45:"d";
Is \r\n"
}

les documents officiels indiquent qu'il doit renvoyer false et définir E_NOTICE

mais puisque vous avez une erreur, le rapport d'erreur est configuré pour être déclenché par E_NOTICE

voici un correctif pour vous permettre de détecter les faux retournés par unserialize

$old_err=error_reporting(); 
error_reporting($old_err & ~E_NOTICE);
$object = unserialize($serialized_data);
error_reporting($old_err);

vous voudrez peut-être envisager d'utiliser le codage/décodage en base64

$string=base64_encode(serialize($obj));
unserialize(base64_decode($string));
2
Muayyad Alsadi

Après avoir essayé certaines choses sur cette page sans succès, j'ai jeté un coup d'œil dans la source de la page et remarqué que toutes les citations de la chaîne sérialisée ont été remplacées par des entités html . Le décodage de ces entités permet d'éviter beaucoup de maux de tête:

$myVar = html_entity_decode($myVar);
1
David

Dans mon cas, je stockais des données sérialisées dans le champ BLOB de la base de données MySQL, qui n'était apparemment pas assez grande pour contenir la valeur entière et la tronquer. Une telle chaîne ne pourrait évidemment pas être non sérialisée.
Une fois ce champ converti en MEDIUMBLOB, le problème s'est dissipé . Il peut également être nécessaire de basculer dans les options de table ROW_FORMAT en DYNAMIC ou COMPRESSED

1
Adam Bubela

Voici un outil en ligne pour réparer une chaîne sérialisée endommagée.

Je voudrais ajouter que cela se produit principalement à cause d'une recherche et d'un remplacement effectués sur la base de données et les données de sérialisation ( spécialement le key length ) ne soit pas mis à jour selon le remplacement et cela cause la "corruption".

Néanmoins, l'outil ci-dessus utilise la logique suivante pour corriger les données de sérialisation ( Copied From Here ).

function error_correction_serialise($string){
    // at first, check if "fixing" is really needed at all. After that, security checkup.
    if ( @unserialize($string) !== true &&  preg_match('/^[aOs]:/', $string) ) {
         $string = preg_replace_callback( '/s\:(\d+)\:\"(.*?)\";/s',    function($matches){return 's:'.strlen($matches[2]).':"'.$matches[2].'";'; },   $string );
    }
    return $string;
} 
0
Mohd Abdul Mujib

La corruption dans cette question est isolée sur une seule sous-chaîne à la fin de la chaîne sérialisée. Elle a probablement été remplacée manuellement par une personne qui souhaitait mettre à jour le nom de fichier image. Ce fait apparaît clairement dans le lien de démonstration ci-dessous, qui utilise les données publiées du PO. En bref, C:fakepath100.jpg n'a pas une longueur de 19, mais plutôt de 17.

Etant donné que la corruption de chaîne sérialisée est limitée à un nombre d'octets/de caractères incorrect, la procédure suivante permet de mettre à jour la chaîne endommagée avec la valeur de nombre d'octets correcte.

Le remplacement suivant basé sur la regex ne sera efficace que pour remédier au nombre d’octets, rien de plus.

Il semble que bon nombre des précédents articles ne font que copier-coller un motif regex de quelqu'un d'autre. Il n'y a aucune raison de capturer le nombre d'octets potentiellement corrompu s'il ne doit pas être utilisé dans le remplacement. De plus, l'ajout du modificateur de motif s est une inclusion raisonnable dans le cas où une valeur de chaîne contient des retours à la ligne /.

* Pour ceux qui ne connaissent pas le traitement des caractères multi-octets lors de la sérialisation, vous ne devez pas utiliser mb_strlen() dans le rappel personnalisé car c'est le nombre d'octets qui est stocké et non le nombre de caractères , voir ma sortie. .

Code: ( Démo avec les données de l'OP ) ( Démo avec des données d'échantillonnage arbitraires ) ( Démo avec remplacement de la condition )

$corrupted = <<<STRING
a:4:{i:0;s:3:"three";i:1;s:5:"five";i:2;s:2:"newline1
newline2";i:3;s:6:"garçon";}
STRING;

$repaired = preg_replace_callback(
        '/s:\d+:"(.*?)";/s',
        //  ^^^- matched/consumed but not captured because not used in replacement
        function ($m) {
            return "s:" . strlen($m[1]) . ":\"{$m[1]}\";";
        },
        $corrupted
    );

echo $corrupted , "\n" , $repaired;
echo "\n---\n";
var_export(unserialize($repaired));

Sortie:

a:4:{i:0;s:3:"three";i:1;s:5:"five";i:2;s:2:"newline1
Newline2";i:3;s:6:"garçon";}
a:4:{i:0;s:5:"three";i:1;s:4:"five";i:2;s:17:"newline1
Newline2";i:3;s:7:"garçon";}
---
array (
  0 => 'three',
  1 => 'five',
  2 => 'newline1
Newline2',
  3 => 'garçon',
)

Une jambe dans le trou du lapin ... Ce qui précède fonctionne bien même si des guillemets doubles apparaissent dans une valeur de chaîne, mais si une valeur de chaîne contient "; ou un autre outil monkeywrenching, vous devrez vous y rendre un peu plus loin et mettre en œuvre des "regards". Mon nouveau modèle

vérifie que la s principale est:

  • le début de toute la chaîne d'entrée ou
  • précédé de ;

et vérifie que le "; est:

  • à la fin de toute la chaîne d'entrée ou
  • suivi de } ou
  • suivi d'une déclaration de chaîne ou d'entier s: ou i:

Je n'ai pas testé chaque possibilité. en fait, je suis relativement peu familiarisé avec toutes les possibilités offertes par une chaîne sérialisée, car je n'ai jamais choisi de travailler avec des données sérialisées - toujours JSON dans les applications modernes. S'il y a d'autres personnages possibles en tête ou en fin, laissez un commentaire et je prolongerai les recherches.

Extrait étendu: ( Demo )

$corrupted_byte_counts = <<<STRING
a:12:{i:0;s:3:"three";i:1;s:5:"five";i:2;s:2:"newline1
newline2";i:3;s:6:"garçon";i:4;s:111:"double " quote \"escaped";i:5;s:1:"a,comma";i:6;s:9:"a:colon";i:7;s:0:"single 'quote";i:8;s:999:"semi;colon";s:5:"assoc";s:3:"yes";i:9;s:1:"monkey";wrenching doublequote-semicolon";s:3:"s:";s:9:"val s: val";}
STRING;

$repaired = preg_replace_callback(
        '/(?<=^|;)s:\d+:"(.*?)";(?=$|}|[si]:)/s',
        //^^^^^^^^--------------^^^^^^^^^^^^^-- some additional validation
        function ($m) {
            return 's:' . strlen($m[1]) . ":\"{$m[1]}\";";
        },
        $corrupted_byte_counts
    );

echo "corrupted serialized array:\n$corrupted_byte_counts";
echo "\n---\n";
echo "repaired serialized array:\n$repaired";
echo "\n---\n";
print_r(unserialize($repaired));

Sortie:

corrupted serialized array:
a:12:{i:0;s:3:"three";i:1;s:5:"five";i:2;s:2:"newline1
newline2";i:3;s:6:"garçon";i:4;s:111:"double " quote \"escaped";i:5;s:1:"a,comma";i:6;s:9:"a:colon";i:7;s:0:"single 'quote";i:8;s:999:"semi;colon";s:5:"assoc";s:3:"yes";i:9;s:1:"monkey";wrenching doublequote-semicolon";s:3:"s:";s:9:"val s: val";}
---
repaired serialized array:
a:12:{i:0;s:5:"three";i:1;s:4:"five";i:2;s:17:"newline1
newline2";i:3;s:7:"garçon";i:4;s:24:"double " quote \"escaped";i:5;s:7:"a,comma";i:6;s:7:"a:colon";i:7;s:13:"single 'quote";i:8;s:10:"semi;colon";s:5:"assoc";s:3:"yes";i:9;s:39:"monkey";wrenching doublequote-semicolon";s:2:"s:";s:10:"val s: val";}
---
Array
(
    [0] => three
    [1] => five
    [2] => newline1
newline2
    [3] => garçon
    [4] => double " quote \"escaped
    [5] => a,comma
    [6] => a:colon
    [7] => single 'quote
    [8] => semi;colon
    [assoc] => yes
    [9] => monkey";wrenching doublequote-semicolon
    [s:] => val s: val
)
0
mickmackusa

J'ai rencontré le même problème lors de la désérialisation des données. Découvert que s'il y a un ", ',:, ou; dans l'une des valeurs du tableau, la sérialisation est corrompue. J'avais un: dans mon tableau, donc l'a supprimé et il a été corrigé.

J'espère que ça aide quelqu'un.

0
Sajal

Une autre raison de ce problème peut être le type de colonne de la table de sessions "charge utile". Si vous avez d'énormes données en session, une colonne de texte ne suffirait pas. Vous aurez besoin de MEDIUMTEXT ou même de LONGTEXT.

0
GarryOne

Vous devrez modifier le type de classement en utf8_unicode_ci et le problème sera corrigé.

0
Ankit Vishwakarma