Wie repariere ich eine serialisierte Zeichenfolge, die durch eine falsche Bytelänge beschädigt wurde?

96

Ich verwende Hotaru CMS mit dem Image Upload-Plugin. Ich erhalte diese Fehlermeldung, wenn ich versuche, ein Bild an einen Beitrag anzuhängen. Andernfalls tritt kein Fehler auf:

unserialize () [function.unserialize]: Fehler beim Offset

Der fehlerhafte Code (Fehler zeigt auf **):

/**
     * 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; }** 
        }
    }

Daten aus der Tabelle, beachten Sie, dass das Endbit die Bildinformationen enthält. Ich bin kein Experte für PHP, also habe ich mich gefragt, was Sie Jungs / Mädels denken könnten.

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: Ich glaube ich habe das Serialize Bit gefunden ...

/**
     * 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;
    }
user576820
quelle
3
Für mich bestand die schnelle Lösung darin, base64_encode / decode vor serialize / unserialize zu verwenden. davidwalsh.name/php-serialize-unserialize-issues
Valentin Despa
1
Ich weiß nicht warum, aber meine gelöst mit hinzugefügt @,@unserialize($product->des_txtmopscol);
Bhavin Rana
2
Das Hinzufügen von @ BhavinRana @ist keine Fehlerbehebung, sondern eine Fehlerunterdrückung - mit dieser Technik wird nichts "behoben".
Mickmackusa

Antworten:

218

unserialize() [function.unserialize]: Error at offset war Gebühren an invalid serialization data wegen ungültiger Länge fällig

Schnelle Lösung

Was Sie tun können, sind recalculating the lengthdie Elemente in einem serialisierten Array

Sie aktuell serialisierte Daten

$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";}';

Beispiel ohne Neuberechnung

var_dump(unserialize($data));

Ausgabe

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

Neuberechnung

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

Ausgabe

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)

Empfehlung .. I.

Anstatt diese Art der schnellen Lösung zu verwenden, rate ich Ihnen, die Frage mit zu aktualisieren

  • Wie Sie Ihre Daten serialisieren

  • Wie Sie es speichern ..

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

Der Fehler

Der Fehler wurde aufgrund der Verwendung eines doppelten Anführungszeichens "anstelle eines einfachen Anführungszeichens generiert ', weshalb C:\fakepath\100.pngin konvertiert wurdeC:fakepath100.jpg

Um den Fehler zu beheben

Sie müssen von ändern $h->vars['submitted_data'](Beachten Sie den Stich ganz ')

Ersetzen

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

Mit

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

Zusätzlicher Filter

Sie können diesen einfachen Filter auch hinzufügen, bevor Sie serialize aufrufen

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

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

Wenn Sie UTF-Zeichen haben, können Sie diese auch ausführen

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

So erkennen Sie das Problem in zukünftigen serialisierten Daten

  findSerializeError ( $data1 ) ;

Ausgabe

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 Funktion

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

    }

}

Ein besserer Weg, um in der Datenbank zu speichern

$toDatabse = base64_encode(serialize($data));  // Save to database
$fromDatabase = unserialize(base64_decode($data)); //Getting Save Format 
Baba
quelle
1
Baba, ich habe deine erstaunliche findSerializeErrorFunktion benutzt und viele Fehler gefunden. Bitte werfen Sie einen Blick auf mein Thema
Max Koretskyi
1
Verwenden Sie base64auf Artikel, bevor Sie es zur Datenbank hinzufügen ... es würde das Nullzeichen beibehalten
Baba
1
Das ist also kein besserer Weg, um in der Datenbank zu speichern. Dies ist der Fall, es sei denn, Sie möchten den Zweck der Datenbank vollständig vernachlässigen. Wie führen Sie eine Suche in einer Reihe verschlüsselter Werte durch? Ganz zu schweigen vom Aufblähen, ugh. Die richtige Codierung ist die richtige Antwort.
Deji
4
Wenn Sie PHP 5.5 verwenden, lesen Sie die Antwort unter @ r00tAcc3ss! stackoverflow.com/a/21389439/1003020
Vinicius Garcia
5
Wenn Sie die Fehlermeldung "preg_replace (): Der Modifikator / e wird nicht mehr unterstützt, verwenden Sie stattdessen preg_replace_callback" in php7 - diese Antwort funktioniert stackoverflow.com/a/21389439/2011434
BenB
81

Ich habe nicht genug Ruf, um Kommentare abzugeben, daher hoffe ich, dass dies von Leuten gesehen wird, die die obige "richtige" Antwort verwenden:

Seit PHP 5.5 ist der Modifikator / e in preg_replace () vollständig veraltet und der obige preg_match wird fehlerhaft. In der PHP-Dokumentation wird empfohlen, stattdessen preg_match_callback zu verwenden.

Als Alternative zum oben vorgeschlagenen preg_match finden Sie die folgende Lösung.

$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 );
r00tAcc3ss
quelle
3
Dies scheint die einzige Antwort auf der Seite zu sein, die die erste Erfassungsgruppe tatsächlich sinnvoll nutzt. Während eine sinnvolle Programmierung nur Ersetzungen vornimmt , bei denen die Anzahl der Bytes tatsächlich falsch ist, speichert diese Lösung die nicht zwischen strlen()und führt daher redundante Funktionsaufrufe durch. Persönlich finde ich das Hinzufügen einer Inline-Bedingung zu ausführlich, aber dieses Snippet macht aus guten Gründen gute Dinge.
Mickmackusa
3
Es funktionierte für mich mit dem folgenden regulären Ausdruck '!s:(\d+):"(.*?)";!s'(mit einem Ende 's', um auch neue Zeilen aufzunehmen). Vielen Dank an Adilbos Kommentar unten.
ArnoHolo
13

Es gibt einen weiteren Grund, der unserialize()fehlgeschlagen ist, weil Sie serialisierte Daten nicht ordnungsgemäß in die Datenbank gestellt haben (siehe Offizielle Erklärung hier). Schon seitserialize() Rückgabedaten und PHP-Variablen keine Codierungsmethoden interessieren, verursacht VARCHAR () diesen Fehler, wenn sie in TEXT eingefügt werden.

Lösung: Speichern Sie serialisierte Daten in BLOB in Ihrer Tabelle.

Ge Rong
quelle
Dies löste mein Problem in Laravel 5. Ich habe die Spaltendefinition von string () in binary () geändert.
WNRosenberg
Die Frage des OP scheint kein Problem mit dem MySQL-Spaltentyp zu haben. Es ist anscheinend durch eine falsche Byte-Berechnung des imageWertes beschädigt . Ihre Antwort bezieht sich nicht auf die spezifische Frage des OP. Sie können Ihren Rat an folgende Adresse
verschieben
11

Schnelle Lösung

Berechnen Sie die Länge der Elemente in einem serialisierten Array neu - verwenden Sie jedoch nicht (preg_replace), es ist veraltet - verwenden Sie besser preg_replace_callback:

Edit: Neue Version jetzt nicht nur falsche Länge, sondern auch Zeilenumbrüche korrigiert und korrekte Zeichen mit aczent gezählt (dank mickmackusa )

// New Version
$data = preg_replace_callback('!s:\d+:"(.*?)";!s', function($m) { return "s:" . strlen($m[1]) . ':"'.$m[1].'";'; }, $data);
Adilbo
quelle
1
Wie hat diese falsche Lösung 8 positive Stimmen? Ich schließe aus, um zu überlegen, wie viele Leute diesen Einzeiler unwissentlich kopiert hätten. [trauriges Gesicht] Hier ist ein Beweis für zwei Möglichkeiten, wie dieses Snippet fehlschlagen wird: 3v4l.org/Cf6Nh Siehe mein verfeinertes Muster und benutzerdefinierten Ersatz @ stackoverflow.com/a/55074706/2943403
mickmackusa
1
Meine Lösung befindet sich nicht mehr auf der anderen Seite, da es sich um eine falsche Lösung für die katastrophal beschädigte serialisierte Zeichenfolge handelt. Ich habe mein Snippet zu dieser Seite hinzugefügt und Erklärungen und Demonstrationen bereitgestellt. stackoverflow.com/a/55566407/2943403
mickmackusa
5

Dieser Fehler wird verursacht, weil Ihr Zeichensatz falsch ist.

Zeichensatz nach offenem Tag setzen:

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

Und setzen Sie charset utf8 in Ihrer Datenbank:

mysql_query("SET NAMES 'utf8'");
Wille
quelle
Ich sehe in der vom OP veröffentlichten Frage keinen Hinweis darauf, dass die Beschädigung auf den Zeichensatz zurückzuführen ist. Es steht Ihnen frei, Ihren Anspruch zu verteidigen, aber soweit ich das beurteilen kann, hat jemand den imageWert manuell aktualisiert und die Anzahl der Bytes nicht aktualisiert. Sofern nicht anders angegeben, muss ich davon ausgehen, dass diese Antwort für die Frage des OP falsch ist.
Mickmackusa
4

Sie können fehlerhafte Serialisierungszeichenfolgen mithilfe der folgenden Funktion mit Multibyte-Zeichenbehandlung beheben .

function repairSerializeString($value)
{

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

    return preg_replace_callback(
        $regex, function($match) {
            return "s:".mb_strlen($match[2]).":\"".$match[2]."\""; 
        },
        $value
    );
}
Rajesh Meniya
quelle
Der Kern dessen, was diese Antwort empfiehlt, ist einfach falsch und kann möglicherweise perfekt gültige serialisierte Zeichenfolgen beschädigen. Dieses Snippet sollte nicht verwendet / vertrauenswürdig sein.
Mickmackusa
@mickmackusa Ich verstehe Ihren Standpunkt nicht. Können Sie bitte den besten Weg vorschlagen, dies zu tun? oder schlagen Sie vor, diese Antwort zu bearbeiten.
Rajesh Meniya
Ich habe hier eine korrekte Lösung angegeben: stackoverflow.com/a/55566407/2943403 und erklärt, dass dies mb_strlen()unangemessen ist, da serialize()die Anzahl der Bytes und nicht die Anzahl der Zeichen gespeichert wird . Wenn Sie Ihre Antwort so bearbeiten, dass sie korrekt ist, werden nur redundante Hinweise auf der Seite erstellt.
Mickmackusa
4

öffentliche Funktion 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;
}
Pardeep Goyal
quelle
php unserialize
Pardeep Goyal
Diese Lösung ist für viele Fälle nicht geeignet. Es wird davon ausgegangen, dass jeder die Werte in der serialisierten Zeichenfolge mutieren möchte, um 2 oder mehr Leerzeichen in einen Literalraum UND trim()jede übereinstimmende Teilzeichenfolge zu konvertieren . Dieser Punkt allein macht es unmöglich, diese Lösung zu empfehlen. Darüber hinaus wird es an Zeilenumbrüchen ersticken und unnötigerweise die bereits vorhandene Byteanzahl erfassen, die ohnehin nur überschrieben wird. Schließlich ist dies eine "Nur-Code-Antwort", und diese Arten von Antworten sind von geringem Wert, da sie wenig dazu beitragen, zukünftige Forscher auszubilden / zu befähigen.
Mickmackusa
4
$badData = 'a:2:{i:0;s:16:"as:45:"d";
Is \n";i:1;s:19:"as:45:"d";
Is \r\n";}';

Sie können eine fehlerhafte Serialisierungszeichenfolge mit den vorgeschlagenen regulären Ausdrücken nicht beheben:

$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:' . strlen($m[2]) . ':"' . $m[2] . '";';
    },
    $badData
);
var_dump(@unserialize($data)); // Output: bool(false)

Sie können eine fehlerhafte Serialisierungszeichenfolge mithilfe des folgenden regulären Ausdrucks beheben:

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

var_dump(@unserialize($data));

Ausgabe

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

oder

array(2) {
  [0] =>
  string(16) "as:45:"d";
Is \n"
  [1] =>
  string(18) "as:45:"d";
Is \r\n"
}
Даниил Путилин
quelle
1
@ Mickmackusa Danke. Ein Problem mit Multibyte-Codierungen wurde behoben.
Даниил Путилин
2

die offiziellen Dokumente sagen, dass es false zurückgeben und E_NOTICE setzen sollte

Da Sie jedoch einen Fehler erhalten haben, wird die Fehlerberichterstattung so eingestellt, dass sie von E_NOTICE ausgelöst wird

Hier ist ein Fix, mit dem Sie falsche Rückmeldungen von erkennen können unserialize

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

Möglicherweise möchten Sie die Verwendung von base64 encode / decode in Betracht ziehen

$string=base64_encode(serialize($obj));
unserialize(base64_decode($string));
Muayyad Alsadi
quelle
base64_encodehat den Trick für mich gemacht. In meinem Fall übergeben wir serialized Daten über die Befehlszeile und es sah so aus, als würden einige seltsame Zeichen verhindern, dass sie richtig funktionieren.
Quickshiftin
base64_encode()ist nicht die Lösung für die vom OP gestellte Frage. Die Frage / das Problem des OP befasst sich speziell mit der Tatsache, dass (wahrscheinlich aufgrund eines unangemessenen Ersetzens von Teilzeichenfolgen für das "letzte Array-Element" der serialisierten Zeichenfolge) eine falsche Byteanzahl in der serialisierten Zeichenfolge vorhanden ist. Bitte posten Sie nur Antworten, die sich direkt mit der gestellten Frage befassen.
Mickmackusa
2

Die Beschädigung in dieser Frage ist auf eine einzelne Teilzeichenfolge am Ende der serialisierten Zeichenfolge beschränkt, die wahrscheinlich manuell durch jemanden ersetzt wurde, der den imageDateinamen träge aktualisieren wollte . Diese Tatsache wird in meinem Demonstrationslink unten unter Verwendung der vom OP veröffentlichten Daten deutlich - kurz gesagt, C:fakepath100.jpghat keine Länge von 19, sollte es sein 17.

Da die Beschädigung der serialisierten Zeichenfolge auf eine falsche Anzahl von Bytes / Zeichen beschränkt ist, können Sie die beschädigte Zeichenfolge im Folgenden mit dem richtigen Wert für die Anzahl der Bytes aktualisieren.

Der folgende regexbasierte Ersatz ersetzt nur die Anzahl der Bytes, nicht mehr.

Es sieht so aus, als würden viele der früheren Beiträge nur ein Regex-Muster von jemand anderem kopieren und einfügen. Es gibt keinen Grund, die möglicherweise beschädigte Byteanzahl zu erfassen, wenn sie beim Ersetzen nicht verwendet wird. Auch das Hinzufügen ders Mustermodifikators ist auch eine sinnvolle Einbeziehung, wenn ein Zeichenfolgenwert Zeilenumbrüche enthält.

* Für diejenigen, die sich der Behandlung von Multibyte-Zeichen mit Serialisierung nicht bewusst sind, dürfen Sie sie nicht mb_strlen()im benutzerdefinierten Rückruf verwenden, da die Anzahl der Bytes und nicht die Anzahl der Zeichen gespeichert wird ist. Siehe meine Ausgabe ...

Code: ( Demo mit OP-Daten ) ( Demo mit beliebigen Beispieldaten ) ( Demo mit Ersetzen der Bedingung )

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

Ausgabe:

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',
)

Ein Bein das Kaninchenloch hinunter ... Das Obige funktioniert auch dann einwandfrei, wenn in einem Zeichenfolgenwert doppelte Anführungszeichen stehen, ein Zeichenfolgenwert jedoch enthält"; einwandfrei, andere Affenschlüssel enthält, müssen Sie ein wenig weiter gehen und "Lookarounds" implementieren. Mein neues Muster

prüft, ob der führende s ist:

  • der Anfang der gesamten Eingabezeichenfolge oder
  • vorangestellt von ;

und prüft, ob die "; ist:

  • am Ende der gesamten Eingabezeichenfolge oder
  • gefolgt von } oder
  • gefolgt von einer Zeichenfolge- oder Ganzzahldeklaration s:oderi:

Ich habe nicht jede Möglichkeit getestet. Tatsächlich bin ich mit allen Möglichkeiten einer serialisierten Zeichenfolge relativ unbekannt, da ich mich nie dafür entscheide, mit serialisierten Daten zu arbeiten - in modernen Anwendungen immer json. Wenn es zusätzliche mögliche führende oder nachfolgende Zeichen gibt, hinterlasse einen Kommentar und ich werde die Lookarounds erweitern.

Erweitertes Snippet: ( 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));

Ausgabe:

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
)
mickmackusa
quelle
1

Sie müssen den Sortierungstyp in ändern utf8_unicode_ciund das Problem wird behoben.

Ankit Vishwakarma
quelle
Welches spezifische Zeichen in den Beispieldaten des OP wird Ihrer Meinung nach durch Ändern der Sortierung in geändert utf8_unicode_ci? Ich habe meine Zweifel an diesem.
Mickmackusa
Dies funktionierte auch für mich (abgesehen von der Antwort von r00tAcc3ss), ein Wort von jemandem, der klarstellte, warum? Als Hintergrund nehme ich Daten aus einem API-Aufruf in eine ResourceSpace-Anwendung, speichere sie in einem Array, serialisiere sie und speichere sie. Die serialisierten Daten hatten Probleme beim Speichern, so dass ich sie manuell in UTF-8 codieren musste, ich spielte mit der Kollatierung und dem Zeichensatz in der Datenbank herum und blieb schließlich bei der Kollatierung utf8_general_ci, als ich sie in utf8_unicode_ci änderte, es funktionierte .
Roberto Becerra
1

In meinem Fall habe ich serialisierte Daten im BLOBFeld von MySQL DB gespeichert, das anscheinend nicht groß genug war, um den gesamten Wert zu enthalten, und ihn abgeschnitten. Eine solche Zeichenfolge konnte offensichtlich nicht unserialisiert werden.
Einmal konvertiert dieses Feld in MEDIUMBLOBdas Problem aufgelöst. Es kann auch erforderlich sein, Tabellenoptionen ROW_FORMATauf DYNAMICoder umzuschalten COMPRESSED.

Adam Bubela
quelle
Ich zu - obwohl meins ein TEXTFeld war und als solches bei 65kb abgeschnitten.
Antony
Diese Frage wird nicht abgeschnitten. Die Frage / das Problem des OP befasst sich speziell mit der Tatsache, dass (wahrscheinlich aufgrund eines unangemessenen Ersetzens von Teilzeichenfolgen für das "letzte Array-Element" der serialisierten Zeichenfolge) eine falsche Byteanzahl in der serialisierten Zeichenfolge vorhanden ist. Bitte posten Sie nur Antworten, die sich direkt mit der gestellten Frage befassen.
Mickmackusa
1

Nachdem ich einige Dinge auf dieser Seite erfolglos ausprobiert hatte, schaute ich in die Seitenquelle und bemerkte, dass alle Anführungszeichen in der serialisierten Zeichenfolge durch HTML-Entitäten ersetzt wurden. Das Dekodieren dieser Entitäten hilft, viel Kopfschmerz zu vermeiden:

$myVar = html_entity_decode($myVar);
David
quelle
Diese Frage leidet nicht unter HTML-codierten Entitäten in der serialisierten Zeichenfolge. Die Frage / das Problem des OP befasst sich speziell mit der Tatsache, dass (wahrscheinlich aufgrund eines unangemessenen Ersetzens von Teilzeichenfolgen für das "letzte Array-Element" der serialisierten Zeichenfolge) eine falsche Byteanzahl in der serialisierten Zeichenfolge vorhanden ist. Bitte posten Sie nur Antworten, die sich direkt mit der gestellten Frage befassen.
Mickmackusa
@mickmackusa Diese Frage ist fast 7 Jahre alt und meine Antwort ~ 1,5. Trotzdem schön, dass du dich so sehr engagierst!
David
Ich liebe SO Seiten - jung und alt. Ich suche nach Forschern, die den Unterschied zwischen einer guten und einer nicht so guten Antwort nicht kennen. Diese Seite ist leider voller Ratschläge außerhalb des Themas.
Mickmackusa
Toll! Es gibt bereits Qualitätskontrolle und Abstimmung, aber ich habe keinen Grund, Sie aufzuhalten ;-)
David
Oh nein, schau mal. Es gibt hochgestimmte Antworten, die herabgestuft werden sollten. Zu viele Menschen können nicht unterscheiden. Auf dieser Seite ist die Abstimmungsergebnisse absolut kein Hinweis auf Qualität / Angemessenheit. Ich werde meine Zeit nicht mit Downvoting verschwenden, weil mein Downvote die Bilanz nicht beeinträchtigt. Das Beste, was ich tun kann, ist Kommentare zu hinterlassen, um zu erklären, was gut / schlecht / hässlich ist.
Mickmackusa
1

Hier ist ein Online-Tool zum Beheben einer beschädigten serialisierten Zeichenfolge.

Ich möchte hinzufügen, dass dies hauptsächlich aufgrund einer Suche und Ersetzung der Datenbank und der Serialisierungsdaten ( insbesondere der) geschiehtkey length ) nicht gemäß der Ersetzung aktualisiert werden und dies die "Beschädigung" verursacht.

Das obige Tool verwendet jedoch die folgende Logik, um die Serialisierungsdaten zu korrigieren ( von hier kopiert ).

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;
} 
Mohd Abdul Mujib
quelle
0

Ein weiterer Grund für dieses Problem kann der Spaltentyp der Sitzungstabelle "Payload" sein. Wenn Sie große Datenmengen in der Sitzung haben, würde eine Textspalte nicht ausreichen. Sie benötigen MEDIUMTEXT oder sogar LONGTEXT.

GarryOne
quelle
Diese Frage wird nicht abgeschnitten. Die Frage / das Problem des OP befasst sich speziell mit der Tatsache, dass (wahrscheinlich aufgrund eines unangemessenen Ersetzens von Teilzeichenfolgen für das "letzte Array-Element" der serialisierten Zeichenfolge) eine falsche Byteanzahl in der serialisierten Zeichenfolge vorhanden ist. Bitte posten Sie nur Antworten, die sich direkt mit der gestellten Frage befassen.
Mickmackusa