Jak naprawić serializowany ciąg, który został uszkodzony przez nieprawidłową długość liczby bajtów?

97

Używam Hotaru CMS z wtyczką Image Upload, pojawia się ten błąd jeśli próbuję dołączyć obrazek do posta, w przeciwnym razie nie ma błędu:

unserialize () [function.unserialize]: Błąd przy przesunięciu

Obrażający kod (błąd wskazuje wiersz z **):

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

Dane z tabeli, zwróć uwagę, że na końcu bitu są informacje o obrazku, nie jestem ekspertem w PHP, więc zastanawiałem się, co wy możecie sobie pomyśleć

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

Edycja: myślę, że znalazłem bit serializacji ...

/**
     * 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
źródło
3
Dla mnie szybkim rozwiązaniem było użycie base64_encode / decode przed serializacją / odserializowaniem. davidwalsh.name/php-serialize-unserialize-issues
Valentin
1
Nie wiem dlaczego, ale mój rozwiązany przez dodanie @,@unserialize($product->des_txtmopscol);
Bhavin Rana
2
Dodanie @BhavinRana @nie jest usuwaniem błędów, jest to wyciszanie błędów - tak naprawdę nic nie "zostaje naprawione" tą techniką.
mickmackusa

Odpowiedzi:

218

unserialize() [function.unserialize]: Error at offsetbyło invalid serialization datanależne z powodu nieprawidłowej długości

Szybka naprawa

To, co możesz zrobić, to recalculating the lengthelementy w serializowanej tablicy

Bieżące dane serializowane

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

Przykład bez przeliczania

var_dump(unserialize($data));

Wynik

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

Przeliczam

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

Wynik

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)

Zalecenie .. I

Zamiast korzystać z tego rodzaju szybkiej poprawki ... radzę zaktualizować pytanie za pomocą

  • Jak serializujesz swoje dane

  • Jak to oszczędzasz ...

================================ EDYCJA 1 ================ ===============

Błąd

Błąd został wygenerowany z powodu użycia podwójnego cudzysłowu "zamiast pojedynczego cudzysłowu ', dlatego C:\fakepath\100.pngzostał przekonwertowany naC:fakepath100.jpg

Aby naprawić błąd

Musisz zmienić $h->vars['submitted_data']From (Zwróć uwagę na singe ')

Zastąpić

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

Z

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

Dodatkowy filtr

Możesz również dodać ten prosty filtr przed wywołaniem serializacji

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

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

Jeśli masz znaki UTF, możesz również uruchomić

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

Jak wykryć problem w przyszłych serializowanych danych

  findSerializeError ( $data1 ) ;

Wynik

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 Funkcjonować

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

    }

}

Lepszy sposób zapisywania w bazie danych

$toDatabse = base64_encode(serialize($data));  // Save to database
$fromDatabase = unserialize(base64_decode($data)); //Getting Save Format 
Baba
źródło
1
Baba, użyłem twojej niesamowitej findSerializeErrorfunkcji i znalazłem wiele błędów. Proszę spojrzeć na mój temat
Max Koretskyi
1
użyj base64na artykule przed dodaniem go do bazy danych ... zachowałby znak null
Baba
1
Nie jest to więc lepszy sposób zapisywania w bazie danych. Tak jest, chyba że chcesz całkowicie zaniedbać przeznaczenie bazy danych. Jak zamierzasz przeprowadzić wyszukiwanie w grupie zaszyfrowanych wartości? Nie wspominając o wzdęciu, ugh. Właściwe kodowanie to właściwa odpowiedź.
Deji
4
Jeśli używasz PHP 5.5, zobacz odpowiedź @ r00tAcc3ss! stackoverflow.com/a/21389439/1003020
Vinicius Garcia
6
Jeśli pojawi się ten błąd „preg_replace (): modyfikator / e nie jest już obsługiwany, użyj zamiast tego preg_replace_callback” w php7 - ta odpowiedź działa stackoverflow.com/a/21389439/2011434
BenB
82

Nie mam wystarczającej reputacji, aby komentować, więc mam nadzieję, że widzą to osoby używające powyższej „poprawnej” odpowiedzi:

Od php 5.5 modyfikator / e w preg_replace () został całkowicie przestarzały, a powyższa preg_match zakończy się błędem. Dokumentacja php zaleca użycie preg_match_callback w jego miejsce.

Proszę znaleźć następujące rozwiązanie jako alternatywę dla powyższego proponowanego preg_match.

$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
źródło
3
Wydaje się, że jest to jedyna odpowiedź na stronie, która faktycznie dobrze wykorzystuje pierwszą grupę przechwytywania. Chociaż rozsądne programowanie polega na wykonywaniu podmian tylko wtedy, gdy liczba bajtów jest rzeczywiście nieprawidłowa, to rozwiązanie nie buforuje danych, strlen()a zatem powoduje nadmiarowe wywołania funkcji. Osobiście uważam, że dodanie warunku w tekście jest zbyt szczegółowe, ale ten fragment kodu robi dobre rzeczy z ważnych powodów.
mickmackusa
4
U mnie zadziałało z następującym wyrażeniem regularnym '!s:(\d+):"(.*?)";!s'(z końcówką „s”, aby również wprowadzić nowe wiersze). Dzięki komentarzowi adilbo poniżej.
ArnoHolo
13

Istnieje inny powód unserialize()niepowodzenia, ponieważ nieprawidłowo umieściłeś serializowane dane w bazie danych, patrz Oficjalne wyjaśnienie tutaj. Ponieważ serialize()zwraca dane binarne i zmienne php nie przejmują się metodami kodowania, więc umieszczenie ich w TEXT, VARCHAR () spowoduje ten błąd.

Rozwiązanie: przechowuj serializowane dane w BLOB w swojej tabeli.

Ge Rong
źródło
To rozwiązało mój problem w Laravel 5. Zmieniłem definicję kolumny z string () na binary ().
WNRosenberg
Wydaje się, że pytanie OP nie ma problemu z typem kolumny mysql. Najwyraźniej jest uszkodzony przez nieprawidłowe obliczenie imagewartości bajtów . Twoja odpowiedź nie odnosi się do konkretnego pytania PO. Możesz przenieść swoje rady na: stackoverflow.com/q/5544749/2943403
mickmackusa
11

Szybka naprawa

Ponowne obliczanie długości elementów w serializowanej tablicy - ale nie używaj (preg_replace), jest to przestarzałe - lepiej użyj preg_replace_callback:

EDIT: Nowa wersja teraz nie tylko źle długość ale również określić line-breaks i liczyć poprawne znaki z aczent (dzięki mickmackusa )

// New Version
$data = preg_replace_callback('!s:\d+:"(.*?)";!s', function($m) { return "s:" . strlen($m[1]) . ':"'.$m[1].'";'; }, $data);
adilbo
źródło
1
W jaki sposób to nieprawidłowe rozwiązanie ma 8 głosów za? Mrugam, żeby pomyśleć, ile osób nieświadomie skopiowałoby wklejony ten jeden wiersz. [smutna buźka] Oto dowód dwóch przyczyn niepowodzenia tego fragmentu: 3v4l.org/Cf6Nh Zobacz mój udoskonalony wzór i niestandardowy zamiennik @ stackoverflow.com/a/55074706/2943403
mickmackusa
1
Mojego rozwiązania nie ma już na drugiej stronie, ponieważ było to niepoprawne rozwiązanie dla katastrofalnie uszkodzonego serializowanego ciągu. Dodałem mój fragment do tej strony i dostarczyłem wyjaśnienia i demonstracje. stackoverflow.com/a/55566407/2943403
mickmackusa
5

Ten błąd jest spowodowany nieprawidłowym zestawem znaków.

Ustaw zestaw znaków po otwartym tagu:

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

I ustaw charset utf8 w swojej bazie danych:

mysql_query("SET NAMES 'utf8'");
Będzie
źródło
Nie widzę żadnych wskazówek w opublikowanym pytaniu OP, które sugerowałyby, że przyczyną uszkodzenia jest zestaw znaków. Obrona roszczenia jest bezpłatna, ale o ile wiem, ktoś ręcznie zaktualizował imagewartość i nie zaktualizował liczby bajtów. O ile nie poinformowano inaczej, muszę założyć, że ta odpowiedź jest nieprawidłowa na pytanie PO.
mickmackusa
4

Możesz naprawić uszkodzony ciąg serializacji za pomocą następującej funkcji, z obsługą znaków wielobajtowych .

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
źródło
Rdzeń tego, co zaleca ta odpowiedź, jest po prostu błędny i może spowodować uszkodzenie doskonale poprawnych serializowanych ciągów. Ten fragment nie powinien być używany / zaufany.
mickmackusa
@mickmackusa Nie rozumiem, czy możesz zasugerować najlepszy sposób na zrobienie tego? lub zasugeruj edycję tej odpowiedzi ..
Rajesh Meniya
Podałem poprawne rozwiązanie tutaj: stackoverflow.com/a/55566407/2943403 i wyjaśniłem, że mb_strlen()jest to niewłaściwe, ponieważ serialize()przechowuje liczbę bajtów, a nie liczbę znaków. Zmiana odpowiedzi na poprawną spowodowałaby tylko zbędne porady na stronie.
mickmackusa
4

public function 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
źródło
php unserialize
Pardeep Goyal
To rozwiązanie nie jest odpowiednie w wielu przypadkach. Zakłada, że ​​każdy będzie chciał zmutować wartości w serializowanym ciągu, aby przekonwertować 2 lub więcej białych znaków na literalną spację ORAZ trim()każdy dopasowany podciąg. Już sam ten punkt uniemożliwia rekomendację tego rozwiązania. Co więcej, będzie się dławić znakami nowej linii i niepotrzebnie przechwytuje istniejącą liczbę bajtów, która i tak zostanie nadpisana. Wreszcie, jest to „odpowiedź oparta tylko na kodzie”, a tego typu odpowiedzi mają niewielką wartość, ponieważ nie przyczyniają się do edukacji / wspierania przyszłych naukowców.
mickmackusa
4
$badData = 'a:2:{i:0;s:16:"as:45:"d";
Is \n";i:1;s:19:"as:45:"d";
Is \r\n";}';

Nie możesz naprawić zepsutego ciągu serializacji za pomocą proponowanych wyrażeń regularnych:

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

Możesz naprawić uszkodzony ciąg serializacji za pomocą następującego wyrażenia regularnego:

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

var_dump(@unserialize($data));

Wynik

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

lub

array(2) {
  [0] =>
  string(16) "as:45:"d";
Is \n"
  [1] =>
  string(18) "as:45:"d";
Is \r\n"
}
Даниил Путилин
źródło
1
@mickmackusa Thanks. Naprawiono problem z kodowaniem wielobajtowym.
Даниил Путилин
2

że oficjalne docs mówi, powinien on powrócić fałszywe i ustawić e_notice

ale ponieważ wystąpił błąd, raportowanie błędów jest wyzwalane przez E_NOTICE

tutaj jest poprawka, która pozwala wykryć fałszywe zwrócone przez unserialize

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

możesz rozważyć użycie kodowania / dekodowania base64

$string=base64_encode(serialize($obj));
unserialize(base64_decode($string));
Muayyad Alsadi
źródło
base64_encodezałatwił sprawę dla mnie. W moim przypadku przekazujemy serializedane d przez wiersz poleceń i wyglądało na to, że jakieś dziwne znaki uniemożliwiały mu prawidłowe działanie.
quickshift w
base64_encode()nie jest odpowiedzią na pytanie zadane przez PO. Pytanie / problem OP dotyczy w szczególności faktu, że (prawdopodobnie dotyczy to niewłaściwej zamiany podciągu w „końcowym elemencie tablicy” serializowanego ciągu), że w serializowanym ciągu jest nieprawidłowa liczba bajtów. Prosimy o publikowanie tylko odpowiedzi, które dotyczą bezpośrednio zadanego pytania.
mickmackusa
2

Uszkodzenie w tym pytaniu jest izolowane do pojedynczego podciągu na końcu serializowanego ciągu, który prawdopodobnie został ręcznie zastąpiony przez kogoś, kto leniwie chciał zaktualizować imagenazwę pliku. Fakt ten będzie widoczny w moim linku demonstracyjnym poniżej, korzystając z opublikowanych danych PO - krótko mówiąc, C:fakepath100.jpgnie ma długości 19, a powinno 17.

Ponieważ uszkodzenie zserializowanego ciągu jest ograniczone do nieprawidłowej liczby bajtów / znaków, poniższe czynności wykonają dobrą robotę aktualizacji uszkodzonego ciągu z poprawną wartością liczby bajtów.

Poniższe zastąpienie oparte na wyrażeniach regularnych będzie skuteczne tylko w naprawianiu liczby bajtów, nic więcej.

Wygląda na to, że wiele wcześniejszych postów po prostu kopiowało i wklejało wzorzec regex od kogoś innego. Nie ma powodu, aby przechwytywać liczbę potencjalnie uszkodzonych bajtów, jeśli nie będzie ona używana w zamianie. Ponadto dodanie smodyfikatora wzorca jest rozsądnym włączeniem w przypadku, gdy wartość ciągu zawiera znaki nowej linii / powrotu.

* Dla tych, którzy nie są świadomi traktowania znaków wielobajtowych podczas serializacji, nie wolno ich używać mb_strlen()w niestandardowym wywołaniu zwrotnym, ponieważ jest to liczba bajtów, która jest przechowywana, a nie liczba znaków , zobacz moje dane wyjściowe ...

Kod: ( Demo z danymi OP ) ( Demo z dowolnymi przykładowymi danymi ) ( Demo z zamianą warunków )

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

Wynik:

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

Jedna noga w dół króliczej nory ... Powyższe działa dobrze, nawet jeśli w wartości ciągu występują podwójne cudzysłowy, ale jeśli wartość ciągu zawiera ";lub jakieś inne wyrażanie małpią, musisz pójść trochę dalej i zaimplementować „lookarounds”. Mój nowy wzór

sprawdza, czy prowadzenie sto:

  • początek całego ciągu wejściowego lub
  • poprzedzony ;

i sprawdza, czy ";:

  • na końcu całego ciągu wejściowego lub
  • po którym następuje }lub
  • po którym następuje deklaracja ciągu lub liczby całkowitej s:lubi:

Nie testowałem wszystkich możliwości; w rzeczywistości jestem stosunkowo nieobeznany ze wszystkimi możliwościami serializowanego ciągu, ponieważ nigdy nie decyduję się na pracę z serializowanymi danymi - zawsze json w nowoczesnych aplikacjach. Jeśli są dodatkowe możliwe znaki początkowe lub końcowe, zostaw komentarz, a ja rozszerzę wyszukiwania.

Rozszerzony fragment: ( 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));

Wynik:

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
źródło
1

Będziesz musiał zmienić typ sortowania na, utf8_unicode_cia problem zostanie rozwiązany.

Ankit Vishwakarma
źródło
Jaki konkretny charakter w przykładowych danych PO zostanie Twoim zdaniem zmodyfikowany przez zmianę sortowania na utf8_unicode_ci? Mam co do tego wątpliwości.
mickmackusa
To też faktycznie zadziałało dla mnie (poza odpowiedzią r00tAcc3ss) jakiekolwiek słowo od kogoś wyjaśniającego dlaczego? W tle pobieram dane z wywołania API do aplikacji ResourceSpace, przechowuję je w tablicy, serializuję i zapisuję. Zserializowane dane miały problemy z zapisywaniem, więc musiałem ręcznie zakodować je do UTF-8, bawiłem się sortowaniem i zestawem znaków w DB i ostatecznie zostawiłem sortowanie utf8_general_ci, kiedy zmieniłem je na utf8_unicode_ci, zadziałało .
Roberto Becerra
1

W moim przypadku przechowywałem zserializowane dane w BLOBpolu bazy danych MySQL, które najwyraźniej nie były wystarczająco duże, aby pomieścić całą wartość i ją obcięłem. Takiego sznurka oczywiście nie można odserializować.
Po przekonwertowaniu tego pola MEDIUMBLOBna problem zniknął. Może być również konieczne przełączenie opcji tabeli ROW_FORMATna DYNAMIClub COMPRESSED.

Adam Bubela
źródło
Ja do - chociaż moje było TEXTpolem i jako takie obcięte do 65kb.
Antony
To pytanie nie cierpi z powodu obcięcia. Pytanie / problem OP dotyczy w szczególności faktu, że (prawdopodobnie dotyczy to niewłaściwej zamiany podciągu w „końcowym elemencie tablicy” serializowanego ciągu), że w serializowanym ciągu jest nieprawidłowa liczba bajtów. Prosimy o publikowanie tylko odpowiedzi, które dotyczą bezpośrednio zadanego pytania.
mickmackusa
1

Po wypróbowaniu kilku rzeczy na tej stronie bez powodzenia zajrzałem do źródła strony i zauważyłem, że wszystkie cudzysłowy w serializowanym ciągu zostały zastąpione encjami html. Dekodowanie tych jednostek pomaga uniknąć dużego bólu głowy:

$myVar = html_entity_decode($myVar);
David
źródło
To pytanie nie dotyczy jednostek zakodowanych w html w serializowanym ciągu. Pytanie / problem OP dotyczy w szczególności faktu, że (prawdopodobnie dotyczy to niewłaściwej zamiany podciągu w „końcowym elemencie tablicy” serializowanego ciągu), że w serializowanym ciągu jest nieprawidłowa liczba bajtów. Prosimy o publikowanie tylko odpowiedzi, które dotyczą bezpośrednio zadanego pytania.
mickmackusa
@mickmackusa To pytanie ma już prawie 7 lat, a moja odpowiedź ~ 1,5. Niemniej miło, że tak bardzo się angażujesz!
David
Uwielbiam tak strony - młode i stare. Szukam badaczy, którzy nie znają różnicy między dobrą odpowiedzią a niezbyt dobrą odpowiedzią. Ta strona, niestety, jest pełna porad nie na temat.
mickmackusa
Wspaniały! Jest już kontrola jakości i głosowanie, ale nie mam powodu, aby cię powstrzymywać ;-)
David
O nie, spójrz. Istnieją odpowiedzi, które powinny zostać odrzucone. Zbyt wielu ludzi nie potrafi tego rozróżnić. Na tej stronie liczba głosów absolutnie nie wskazuje na jakość / stosowność. Nie zamierzam tracić czasu na obniżanie głosów, ponieważ mój głos nie wpłynie na wynik. Najlepsze, co mogę zrobić, to zostawić komentarze, aby wyjaśnić, co jest dobre / złe / brzydkie.
mickmackusa
1

Oto narzędzie online do naprawy uszkodzonego serializowanego ciągu.

Chciałbym dodać, że to dzieje się głównie ze względu na wyszukiwanie i zamiana odbywa się na DB i danych serializacji ( specjalniekey length ) nie zostanie uaktualnione zgodnie Replace i który powoduje, że „korupcja”.

Niemniej jednak powyższe narzędzie używa następującej logiki do naprawiania danych serializacji ( skopiowane stąd ).

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
źródło
0

Inną przyczyną tego problemu może być kolumna tabeli sesji „ładunku”. Jeśli masz ogromne dane dotyczące sesji, kolumna tekstowa nie wystarczy. Będziesz potrzebował MEDIUMTEXT lub nawet LONGTEXT.

GarryOne
źródło
To pytanie nie cierpi z powodu obcięcia. Pytanie / problem OP dotyczy w szczególności faktu, że (prawdopodobnie dotyczy to niewłaściwej zamiany podciągu w „końcowym elemencie tablicy” serializowanego ciągu), że w serializowanym ciągu jest nieprawidłowa liczba bajtów. Prosimy o publikowanie tylko odpowiedzi, które dotyczą bezpośrednio zadanego pytania.
mickmackusa