[wp-trac] [WordPress Trac] #63610: Inconsistency in responses from get_option() for false value
WordPress Trac
noreply at wordpress.org
Fri Jun 20 22:28:45 UTC 2025
#63610: Inconsistency in responses from get_option() for false value
--------------------------------+-----------------------------
Reporter: RavanH | Owner: (none)
Type: defect (bug) | Status: new
Priority: normal | Milestone: Awaiting Review
Component: Options, Meta APIs | Version: 6.8
Severity: normal | Keywords:
Focuses: |
--------------------------------+-----------------------------
When saving an option as false with `update_option( 'test_option', false
)` the value in the database will be empty, same as when doing
`update_option( 'test_option', '' )`. When fetched with `get_option(
'test_option' )` the returned value will be an empty string. Only when an
option does not exist in the database, the returned value will be the
boolean `false`.
So far, so good: Both false and empty are stored and returned as empty
string and a non-existant option can be recognized as boolean false.
But...
This behavior is different in two scenarios:
1. when an option is updated as `false` and then fetched within the same
request, or
2. when there is a persistent object cache active.
In these cases, after doing `update_option( 'test_option', false )` then
`get_option( 'test_option' )` will return the boolean `false` and not an
empty string as should be expected because it exists in the database as en
empty string.
To reproduce, create a file called test.php in a fresh site root, then
populate with:
{{{
<?php
require('./wp-load.php');
// Get a non-existent option for comparison.
// Should always return false.
var_dump( get_option( 'non_existant_option' ) );
// Prepare an option in the DB.
// This is only done once, on the first run.
add_option( 'test_option', 'first run' );
// Get our option from the DB (or from options cache the first time).
// Should return "first run" on the first request, then after
// the last update to 'false' it should return an empty string.
var_dump( get_option( 'test_option' ) );
// Update the test option with non-falsy value.
update_option( 'test_option', 'some value' );
// Update the test option with false.
update_option( 'test_option', false );
// Get our option a second time.
// Should also return an empty string.
var_dump( get_option( 'test_option' ) );
}}}
The first request for /test.php should render:
{{{
bool(false) string(9) "first run" string(0) ""
}}}
And subsequent request should render:
{{{
bool(false) string(0) "" string(0) ""
}}}
But in practice, the last dump gives an unexpected `bool(false)` instead
(same as the non-existant option) even though the option exists in the
database.
This shows the issue happening within one request, which might be unusual
but is still possible. But with a persistent object cache (redis,
memcached), this inconsistency between the first returned test value and
the second one is made persistent across requests and much more likely to
cause unexpected behavior! Hence my initial assumption it was an object
cache bug, as discussed on https://wordpress.org/support/topic/object-
cache-bug-3
But it seems the issue lies with how `false` is stored in the options
cache with `wp_cache_set()`.
Note: This happens only for options with autoload set to "auto" (in this
example), "auto-on" or "on". When the option is first created with
`add_option( 'test_option', 'first run', null, false );` so autoload is
set to "off", the last dump ''will'' show the expected `string(0) ""`!
--
Ticket URL: <https://core.trac.wordpress.org/ticket/63610>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform
More information about the wp-trac
mailing list