¿Cómo reparar una cadena serializada que ha sido dañada por una longitud de conteo de bytes incorrecta?
Estoy usando Hotaru CMS con el complemento Image Upload, aparece este error si intento adjuntar una imagen a una publicación; de lo contrario, no hay ningún error:
unserialize() [función.unserialize]: Error en el desplazamiento
El código infractor (el error apunta a la línea con **):
/**
* 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; }**
}
}
Datos de la tabla, observe que el bit final tiene la información de la imagen. No soy un experto en PHP, así que me preguntaba qué podrían pensar ustedes.
valor_tempdata:
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";}
Editar: Creo que encontré el bit de serialización...
/**
* 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;
}
unserialize() [function.unserialize]: Error at offset
se invalid serialization data
debió a una longitud no válida
Arreglo rapido
Lo que puedes hacer es recalculating the length
usar los elementos en una matriz serializada.
Sus datos serializados actuales
$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";}';
Ejemplo sin recálculo
var_dump(unserialize($data));
Producción
Notice: unserialize() [function.unserialize]: Error at offset 337 of 338 bytes
Recalculando
$data = preg_replace('!s:(\d+):"(.*?)";!e', "'s:'.strlen('$2').':\"$2\";'", $data);
var_dump(unserialize($data));
Producción
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)
Recomendación..yo _
En lugar de utilizar este tipo de solución rápida... te aconsejo que actualices la pregunta con
Cómo estás serializando tus datos
Como lo estas guardando..
================================ EDITAR 1 ================ ================
El error
El error se generó debido al uso de comillas dobles "
en lugar de comillas simples '
, por eso C:\fakepath\100.png
se convirtió aC:fakepath100.jpg
Para corregir el error
Necesitas cambiar $h->vars['submitted_data']
Desde (Ten en cuenta el chamuscado bastante '
)
Reemplazar
$h->vars['submitted_data']['image'] = "C:\fakepath\100.png" ;
Con
$h->vars['submitted_data']['image'] = 'C:\fakepath\100.png' ;
Filtro adicional
También puedes agregar este filtro simple antes de llamar a serializar
function satitize(&$value, $key)
{
$value = addslashes($value);
}
array_walk($h->vars['submitted_data'], "satitize");
Si tienes caracteres UTF también puedes ejecutar
$h->vars['submitted_data'] = array_map("utf8_encode",$h->vars['submitted_data']);
Cómo detectar el problema en futuros datos serializados
findSerializeError ( $data1 ) ;
Producción
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
Función
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;
}
}
}
Una mejor manera de guardar en la base de datos
$toDatabse = base64_encode(serialize($data)); // Save to database
$fromDatabase = unserialize(base64_decode($data)); //Getting Save Format
No tengo suficiente reputación para comentar, así que espero que las personas que usan la respuesta "correcta" anterior vean esto:
Desde php 5.5, el modificador /e en preg_replace() ha quedado completamente obsoleto y el preg_match anterior generará un error. La documentación de PHP recomienda usar preg_match_callback en su lugar.
Encuentre la siguiente solución como alternativa al preg_match propuesto anteriormente.
$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 );