BeTheme 28.5.5 / 28.5.6 / 28.5.7 Theme Options "Restore revision" fails with a fatal TypeError Supplemental notes NOTE ON FORMATTING: PHP open/close tags are omitted throughout, and one pre-existing line is summarised in angle brackets rather than quoted, because this site's firewall rejects uploads containing those constructs. Everything else is verbatim from the shipped 28.5.7 package. Line numbers are exact, so the original is easy to compare against. -------------------------------------------------------------------------- 1. THE DEFECT File: muffin-options/options.php Method: MFN_Options::_validate_options() Line 1129 is the line added in 28.5.5. -------------------------------------------------------------------------- $options = $revisions[$time]; $options = ; $options = array_map('stripslashes', $options); <-- line 1129, ADDED IN 28.5.5 return $options; The value decoded on the previous line is not flat: a large share of its keys hold arrays. stripslashes requires a string, so on PHP 8 the first array-valued key raises: Uncaught TypeError: stripslashes(): Argument #1 ($string) must be of type string, array given ... /wp-content/themes/betheme/muffin-options/options.php:1129 -------------------------------------------------------------------------- 2. SUGGESTED FIX -------------------------------------------------------------------------- Replace line 1129 with WordPress's own recursive helper: $options = stripslashes_deep( $options ); This preserves the intent of the 28.5.5 change (revisions are stored from a slashed request context, so stripping is correct) without assuming the structure is flat. If array_map is preferred, the callback needs a type check so non-strings pass through untouched: $options = array_map( function( $v ) { return is_string( $v ) ? stripslashes( $v ) : $v; }, $options ); That avoids the fatal but strips only the top level, so nested string values inside the 'typography' and 'dimensions' arrays keep their slashes. stripslashes_deep is the closer match to the stated goal. -------------------------------------------------------------------------- 3. THE PRE-WRITE THAT MAKES A FAILED RETRY COSTLY File: muffin-options/js/options.js Function: revisions.restore() -------------------------------------------------------------------------- if( 'backup' != type ){ revision = revisions.set( 'backup' ); // admin-ajax call, succeeds } // the restore form is then submitted, and _validate_options() throws set_revision() keeps a five-slot ring per type (options.php line 140: "$limit = 5; // max number of revisions of specified type"). So a failed Restore launched from the "Update:" or "Revision:" list consumes one "Backup:" slot and delivers nothing. A Restore launched from the "Backup:" list skips the pre-write, because of the guard above. The three lists render separately at options.php lines 2128 ("Update:"), 2142 ("Revision:") and 2158 ("Backup:"), each its own ring, so only the "Backup:" ring is affected. -------------------------------------------------------------------------- 4. WHY THIS IS NOT SITE-SPECIFIC -------------------------------------------------------------------------- The array-valued keys come from the theme's own field types, counted in muffin-options/theme-options.php: color_multi ~40 definitions typography ~36 (e.g. button-font) checkbox ~34 (e.g. transparent, logo-advanced) gradient ~8 dimensions ~5 (e.g. button-padding, button-border-width) box_shadow ~4 On the options blob of an ordinary configured install: 803 top-level keys, of which 126 were arrays. Any site that has saved Theme Options will have these.