Fix Backslashes in Advanced Custom Fields

There is a somewhat serious bug in Advanced Custom Fields 5.0 and its handling of backslashes, at least on my WordPress installs. The problem is that the slashes are stripped, but after the data has been serialized resulting in a mismatch between the declared string length in the serialized data, and it’s actual value length.

Imagine for example a Validated Field that uses a regular expression to check that the value is a digit. The validation pattern would look something like \d for our example. When this is serialized, it’s length is 2 and the data looks like s:2:"\d". The problem is that when this is represented as a string to insert into the database, the backslash just escapes the d and what gets saved to the database is s:2:"d".

Now we have a much bigger problem than a missing backslash – because the declared length is 2 but the length of the string "d" is 1, the serialized data can no longer be loaded from the database and all the field configuration values are lost.

This occurs during the upgrade process from ACF 4 to 5, as well as when saving a field with a backslash as part of its configuration. The field will default back to the “Text” field type in the UI, and the settings will be unloadable in the database. It is possible to manually fix this data by adding the missing \ characters back in, but this is going to be tedious work depending on how many fields are affected and the complexity. Even when fixed manually the field will break again the next time it is saved.

A bug has been listed on the ACF support site.

Fix for Backslashes in Field Configuration

The following code attempts to correct this behavior, the following code attempts to use the filters content_save_pre and acf/get_valid_field to double slash any single slashes found in the configuration so that they are saved correctly to the database.

class ACF5_Slash_Fix {
	function __construct(){

		// bug fix for acf with backslashes in the content.
		add_filter( 'content_save_pre', array( $this, 'fix_post_content' ) );
		add_filter( 'acf/get_valid_field', array( $this, 'fix_upgrade' ) );
	}

	function fix_upgrade( $field ){

		// the $_POST will tell us if this is an upgrade
		$is_5_upgrade = 
			isset( $_POST['action'] ) && $_POST['action'] == 'acf/admin/data_upgrade' && 
			isset( $_POST['version'] ) && $_POST['version'] == '5.0.0';

		// if it is an upgrade recursively fix the field values
		if ( $is_5_upgrade ){
			$field = $this->do_recursive_slash_fix( $field );
		}

		return $field;
	}

	function fix_post_content( $content ){
		global $post;

		// are we saving a field group?
		$is_field_group = get_post_type() == 'acf-field-group';

		// are we upgrading to ACF 5?
		$is_5_upgrade = 
			isset( $_POST['action'] ) && $_POST['action'] == 'acf/admin/data_upgrade' && 
			isset( $_POST['version'] ) && $_POST['version'] == '5.0.0';

		// if we are, we need to check the values for single, but not double, backslashes and make them double
		if ( $is_field_group || $is_5_upgrade ){
			$content = $this->do_slash_fix( $content );
		}
		
		return $content;
	}

	function do_slash_fix( $string ){
		return preg_match( '~(?<!\\\\)\\\\(?!\\\\)~', $string )?
			str_replace('\\', '\\\\', $string ) :
			$string;
	}

	function do_recursive_slash_fix( $array ){

		// loop through all levels of the array
		foreach( $array as $key => &$value ){
			if ( is_array( $value ) ){
				// div deeper
				$value = $this->do_recursive_slash_fix( $value );
			} elseif ( is_string( $value ) ){
				// fix single backslashes to double
				$value = $this->do_slash_fix( $value );
			}
		}

		return $array;
	}
}
new ACF5_Slash_Fix();

Included in Validated Field

This fix is also included in the Validated Field plugin available in the WordPress repository. It offers additional enhancements as well to support field validation, read-only values, unique key, and more.

You may also like...

Leave a Reply

Your email address will not be published.