[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