How to fix a broken serialization

Fixing database serialization issues and recovering corrupted serialized string

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:

  1. Create the database dump (you should have a .sql file)
  2. Run the script: `php ./fix-serialization.php database.sql`
  3. 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! 👨‍💻

5 Comments

Your email address will not be published.