Make WordPress Core

Opened 4 years ago

Last modified 13 months ago

#28701 new defect (bug)

Deleted option is not deleted if value was ever null

Reported by: rmccue Owned by:
Milestone: Awaiting Review Priority: normal
Severity: normal Version: 2.2
Component: Options, Meta APIs Keywords: has-patch needs-unit-tests
Focuses: Cc:


If an autoloaded option ever contains null, it will cease to be autoloaded after this. It will then be stored in the individual option cache (wp_cache_get('optname', 'options')), rather than the autoloaded options. The autoloaded option cache will then contain optname with the value null (and isset(null) === false).

Typically, this would be fine, but delete_option checks the database autoload value directly. It then only attempts to delete this from the autoload options cache (which again fails because of an isset check).

After deleting, the value is still stored in the object cache (!!!), causing it to return a value despite not existing in the database.

As far as I can tell, this has existed since r4855 (WP 2.2).

To reproduce (requires external object cache to be enabled):

// Add the option with initial value set to null
add_option( 'testingkey', null );

// Update to real value
update_option( 'testingkey', 'realvalue' );

// Delete the option completely
delete_option( 'testingkey' );

// Check the value
var_dump( get_option( 'testingkey', 'default' ) );

Expected result:

string(7) "default"

Actual result:

string(9) "realvalue"

Attachments (1)

28701.diff (717 bytes) - added by MikeHansenMe 3 years ago.

Download all attachments as: .zip

Change History (8)

#1 @rmccue
4 years ago

Also occurs if you use update_option later rather than adding with null:

add_option( 'testingkeynew', 'oldvalue' );
update_option( 'testingkeynew', 'firstvalue' );
update_option( 'testingkeynew', null );
update_option( 'testingkeynew', 'secondvalue' );
delete_option( 'testingkeynew' );
var_dump( get_option( 'testingkeynew', 'default' ) );

This ticket was mentioned in IRC in #wordpress-dev by rmccue. View the logs.

4 years ago

#3 @rmccue
4 years ago

Using https://gist.github.com/rmccue/4a0dadc68aa4e75c851f as a workaround for now. Will try and patch soon.

3 years ago

#4 @MikeHansenMe
3 years ago

  • Keywords has-patch added

#5 @boonebgorges
3 years ago

This needs unit tests.

The change suggested in [28701.diff] seems innocuous to me, but it also doesn't seem to address the root problem. At a glance, it looks to me like we ought to fix the cache-busting logic in update_option(). It currently does this:

	if ( ! defined( 'WP_INSTALLING' ) ) {
		$alloptions = wp_load_alloptions();
		if ( isset( $alloptions[$option] ) ) {
			$alloptions[ $option ] = $serialized_value;
			wp_cache_set( 'alloptions', $alloptions, 'options' );
		} else {
			wp_cache_set( $option, $serialized_value, 'options' );

But maybe (a) that first isset() should be array_key_exists(), and maybe (b) we should be more aggressive about invalidating both possible cache locations. (Though I want to careful about messing with 'alloptions' concurrency issues.)

#6 @samuelsidler
3 years ago

  • Keywords needs-unit-tests added

This ticket was mentioned in Slack in #core-multisite by jjj. View the logs.

13 months ago

Note: See TracTickets for help on using tickets.