Serialization is a way to store multidimensional data as a string. It’s commonly used in the WordPress database, because of the architecture of the tables. When it breaks (for various reasons) it’s a disaster. But not with our handy-dandy scripts.
Fixing a database dump with broken serialization
Steps are simple:
- Create the database dump (you should have a .sql file)
- Run the script: `php
./fix-serialization.php database.sql
` - Upload the .sql file back to the server
The script has been created by an awesome WordPress developer – Wojtek Szałkiewicz.
Recovering the corrupted serialized array
If you have a variable with a string which cannot be unserialized, try to use the below function:
/**
* Extract what remains from an unintentionally truncated serialized string
*
* @param string The serialized array
*/
public function repairSerializedArray($serialized)
{
$tmp = preg_replace('/^a:\d+:\{/', '', $serialized);
return $this->repairSerializedArray_R($tmp); // operates on and whittles down the actual argument
}
/**
* The recursive function that does all of the heavy lifing. Do not call directly.
* @param string The broken serialzized array
* @return string Returns the repaired string
*/
private function repairSerializedArray_R(&$broken)
{
// array and string length can be ignored
// sample serialized data
// a:0:{}
// s:4:"four";
// i:1;
// b:0;
// N;
$data = array();
$index = null;
$len = strlen($broken);
$i = 0;
while(strlen($broken))
{
$i++;
if ($i > $len)
{
break;
}
if (substr($broken, 0, 1) == '}') // end of array
{
$broken = substr($broken, 1);
return $data;
}
else
{
$bite = substr($broken, 0, 2);
switch($bite)
{
case 's:': // key or value
$re = '/^s:\d+:"([^\"]*)";/';
if (preg_match($re, $broken, $m))
{
if ($index === null)
{
$index = $m[1];
}
else
{
$data[$index] = $m[1];
$index = null;
}
$broken = preg_replace($re, '', $broken);
}
break;
case 'i:': // key or value
$re = '/^i:(\d+);/';
if (preg_match($re, $broken, $m))
{
if ($index === null)
{
$index = (int) $m[1];
}
else
{
$data[$index] = (int) $m[1];
$index = null;
}
$broken = preg_replace($re, '', $broken);
}
break;
case 'b:': // value only
$re = '/^b:[01];/';
if (preg_match($re, $broken, $m))
{
$data[$index] = (bool) $m[1];
$index = null;
$broken = preg_replace($re, '', $broken);
}
break;
case 'a:': // value only
$re = '/^a:\d+:\{/';
if (preg_match($re, $broken, $m))
{
$broken = preg_replace('/^a:\d+:\{/', '', $broken);
$data[$index] = $this->repairSerializedArray_R($broken);
$index = null;
}
break;
case 'N;': // value only
$broken = substr($broken, 2);
$data[$index] = null;
$index = null;
break;
}
}
}
return $data;
}
The usage is simple:
$data = @unserialize( $serialized );
if ( false === $data ) {
$data = repairSerializedArray( $serialized );
}
Let me know in the comments if it worked in your case! 👨💻
Nice clean and clear but I can’t use it because I don’t know where to place the files to run them from.
where to place the file to run them from?
You’d need to place it on any PHP-enabled server and run the provided command
Hi. If I go to http://localhost/fix-serialization.php, I get this error written on the page – Error: no input file specified.
And if I go to the terminal and type in sudo ./fix-serialization.php database.sql, I get this error: sudo: ./fix-serialization.php: command not found
I’m running php 8.2. Can you let me know how to resolve this?
Thanks!
Hi, thanks for the notice, we’ve been missing the php command in the shell prompt. It should be
`php ./fix-serialization.php database.sql`