Unserialize() warning caused by make_link_local() in live-link-helper.php

Bug Summary

Local / MacBookPro / Life link on any browser

Steps to reproduce

nothing spécial

Environment Info

Last OS on macbook pro, WP 7 , Divi5,

Supporting info

Bug: unserialize() warning caused by make_link_local() in live-link-helper.php

When using Live Links, a PHP warning appears at the top of every page:

Warning: unserialize(): Error at offset 5489 of 5512 bytes in .../local-by-flywheel-live-link-helper.php on line 198

Root cause:
In the make_link_local() method, when the option is not a string, the code:

  1. Serializes the value with serialize()
  2. Replaces the URL via string replacement (which changes string lengths)
  3. Calls unserialize() on the result

Step 3 fails because PHP serialization uses byte-length prefixes (s:XX:) that are no longer correct after the URL replacement.

Fix:
Replace the serialize/unserialize cycle with a recursive traversal that replaces URLs directly in strings, without touching serialization:

public function make_link_local( $option ) {
    if ( gettype( $option ) !== 'string' ) {
        return $this->replace_host_in_value( $this->get_tunnel_host(), $this->home_domain, $option );
    }
    return $this->replace_host( $this->get_tunnel_host(), $this->home_domain, $option );
}

private function replace_host_in_value( $from, $to, $data ) {
    if ( is_array( $data ) ) {
        foreach ( $data as $key => $value ) {
            $data[ $key ] = $this->replace_host_in_value( $from, $to, $value );
        }
    } elseif ( is_object( $data ) ) {
        foreach ( get_object_vars( $data ) as $key => $value ) {
            $data->$key = $this->replace_host_in_value( $from, $to, $value );
        }
    } elseif ( is_string( $data ) ) {
        $data = $this->replace_host( $from, $to, $data );
    }
    return $data;
}

This fix has been tested and resolves the warning completely.

Environment: macOS, Local 9.x, PHP 8.x, WordPress 7 with Divi theme + Complianz plugin.

Hi @ericFabre!

If you test using Live Links on a new, blank site in Local do you get the same error/warning? Or is it only replicable with the specific site configuration as you laid out? (Local 9.x, PHP 8.x, WordPress 7 with Divi theme + Complianz plugin).

Can you also try updating to the latest Local v10.1.0?

Hello. My Local is : Version 10.1.0+6912 The bug remain after install new version. My solution is ok but the new install delete it.

The bug is only in one web site… so .. no worries. I can give you the link in a PM if you need.

Thank you for confirming that it’s site specific @ericFabre! We don’t believe this is a bug in Live Links itself since the key indicator is that Live Links works fine on a blank site. It seems that Live Links is encountering serialized data in your options table that’s already malformed before it touches it. When it tries to do a URL replacement and then unserialize the result, the byte-length prefixes are out of sync and PHP throws the warning.

This kind of corruption could be due to the Complianz plugin or Divi theme options framework that stored data in a non-standard way at some point. If the warning isn’t causing any real functional issues for you, sticking with your manual workaround might be the way to go for now!

Same problem here, version: Version 10.1.1+6939
Connect to the web store via the live link with https, and only when I login (the standard WP login for a store customer) I see this error.
I had Gemini fix this in ‘local-by-flywheel-live-link-helper.php’, but when I restart the livelink or the site your code overwrite it.

However if I connect locally I do not see the issue.

Full Gemini report with fix:


Bug Report: Serialized String Corruption Warning in Live Link Helper Plugin

Description of the Issue

When managing a WordPress site via a LocalWP Live Link, saving certain settings or options (especially those with complex arrays/objects like active plugins, widgets, theme mods, or transient data) triggers the following warning:

Warning: unserialize(): Error at offset 204 of 7704 bytes in wp-content/mu-plugins/local-by-flywheel-live-link-helper.php on line 198

This warning appears at the top of pages or when logging in, and can prevent certain serialized options from saving correctly.


Root Cause

In wp-content/mu-plugins/local-by-flywheel-live-link-helper.php, the method make_link_local performs host replacement on options before they are saved to the database:

public function make_link_local( $option ) {
    if (gettype($option) !== 'string') {
        $old_str = serialize($option);
        $new_str = $this->replace_host( $this->get_tunnel_host(), $this->home_domain, $old_str );
        return unserialize($new_str);
    }

    return $this->replace_host( $this->get_tunnel_host(), $this->home_domain, $option );
}

When $option is an array or object, it serializes it to a string, runs a naive str_replace (replace_host()) to swap the live link tunnel domain with the local domain, and then tries to unserialize() it.

Because the tunnel domain (e.g., xyz.localwp.live) and the local domain (e.g., mysite.local) typically have different character lengths, a simple string replacement corrupts the serialization format (the length declarations like s:19:"..." no longer match the actual string length). As a result, PHP’s unserialize() fails and throws a warning.


Suggested Solution / Proposed Fix

Rather than serializing/unserializing the data structure and performing string replacement on the raw serialized string, the plugin should recursively traverse arrays and objects to replace host strings in-place, keeping the data structures intact.

Replacing the code in local-by-flywheel-live-link-helper.php with the following recursive implementation fixes the issue completely:

	/**
	 * Recursively replace host in any type of data structure without corruption.
	 *
	 * @param mixed $data Data structure to replace
	 * @param string $old Old host
	 * @param string $new New host
	 */
	public function replace_host_recursive( $data, $old, $new ) {
		if ( is_string( $data ) ) {
			return $this->replace_host( $old, $new, $data );
		}
		if ( is_array( $data ) ) {
			foreach ( $data as $key => $val ) {
				$data[ $key ] = $this->replace_host_recursive( $val, $old, $new );
			}
		} elseif ( is_object( $data ) ) {
			$properties = get_object_vars( $data );
			foreach ( $properties as $name => $val ) {
				$data->$name = $this->replace_host_recursive( $val, $old, $new );
			}
		}
		return $data;
	}

	/**
	 * Generic replacement of the site's tunnel hostname to the local hostname,
	 * used when saving options to the database via the pre_update_option filter hook
	 *
	 * @param mixed $option Option being saved, provided by the filter.
	 */
	public function make_link_local( $option ) {
		if (gettype($option) !== 'string') {
			return $this->replace_host_recursive( $option, $this->get_tunnel_host(), $this->home_domain );
		}

		return $this->replace_host( $this->get_tunnel_host(), $this->home_domain, $option );
	}

Would changing the serialize/unserialize to json_encode/json_decode be simpler. I have not tested this, just thinking out loud.