Changeset 59631
- Timestamp:
- 01/15/2025 10:11:15 PM (4 weeks ago)
- Location:
- trunk
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/wp-includes/option.php
r59373 r59631 163 163 if ( ! wp_installing() ) { 164 164 $alloptions = wp_load_alloptions(); 165 165 /* 166 * When getting an option value, we check in the following order for performance: 167 * 168 * 1. Check the 'alloptions' cache first to prioritize existing loaded options. 169 * 2. Check the 'notoptions' cache before a cache lookup or DB hit. 170 * 3. Check the 'options' cache prior to a DB hit. 171 * 4. Check the DB for the option and cache it in either the 'options' or 'notoptions' cache. 172 */ 166 173 if ( isset( $alloptions[ $option ] ) ) { 167 174 $value = $alloptions[ $option ]; 168 175 } else { 176 // Check for non-existent options first to avoid unnecessary object cache lookups and DB hits. 177 $notoptions = wp_cache_get( 'notoptions', 'options' ); 178 179 if ( ! is_array( $notoptions ) ) { 180 $notoptions = array(); 181 wp_cache_set( 'notoptions', $notoptions, 'options' ); 182 } 183 184 if ( isset( $notoptions[ $option ] ) ) { 185 /** 186 * Filters the default value for an option. 187 * 188 * The dynamic portion of the hook name, `$option`, refers to the option name. 189 * 190 * @since 3.4.0 191 * @since 4.4.0 The `$option` parameter was added. 192 * @since 4.7.0 The `$passed_default` parameter was added to distinguish between a `false` value and the default parameter value. 193 * 194 * @param mixed $default_value The default value to return if the option does not exist 195 * in the database. 196 * @param string $option Option name. 197 * @param bool $passed_default Was `get_option()` passed a default value? 198 */ 199 return apply_filters( "default_option_{$option}", $default_value, $option, $passed_default ); 200 } 201 169 202 $value = wp_cache_get( $option, 'options' ); 170 203 171 204 if ( false === $value ) { 172 // Prevent non-existent options from triggering multiple queries.173 $notoptions = wp_cache_get( 'notoptions', 'options' );174 175 // Prevent non-existent `notoptions` key from triggering multiple key lookups.176 if ( ! is_array( $notoptions ) ) {177 $notoptions = array();178 wp_cache_set( 'notoptions', $notoptions, 'options' );179 } elseif ( isset( $notoptions[ $option ] ) ) {180 /**181 * Filters the default value for an option.182 *183 * The dynamic portion of the hook name, `$option`, refers to the option name.184 *185 * @since 3.4.0186 * @since 4.4.0 The `$option` parameter was added.187 * @since 4.7.0 The `$passed_default` parameter was added to distinguish between a `false` value and the default parameter value.188 *189 * @param mixed $default_value The default value to return if the option does not exist190 * in the database.191 * @param string $option Option name.192 * @param bool $passed_default Was `get_option()` passed a default value?193 */194 return apply_filters( "default_option_{$option}", $default_value, $option, $passed_default );195 }196 205 197 206 $row = $wpdb->get_row( $wpdb->prepare( "SELECT option_value FROM $wpdb->options WHERE option_name = %s LIMIT 1", $option ) ); -
trunk/tests/phpunit/tests/option/option.php
r58945 r59631 113 113 114 114 $before = get_num_queries(); 115 $value =get_option( 'invalid' );116 $after 115 get_option( 'invalid' ); 116 $after = get_num_queries(); 117 117 118 118 $this->assertSame( 0, $after - $before ); … … 128 128 129 129 $before = get_num_queries(); 130 $value =get_option( 'invalid' );131 $after 130 get_option( 'invalid' ); 131 $after = get_num_queries(); 132 132 133 133 $notoptions = wp_cache_get( 'notoptions', 'options' ); … … 136 136 $this->assertIsArray( $notoptions, 'The notoptions cache should be set.' ); 137 137 $this->assertArrayHasKey( 'invalid', $notoptions, 'The "invalid" option should be in the notoptions cache.' ); 138 }139 140 /**141 * @ticket 58277142 *143 * @covers ::get_option144 */145 public function test_get_option_notoptions_do_not_load_cache() {146 add_option( 'foo', 'bar', '', false );147 wp_cache_delete( 'notoptions', 'options' );148 149 $before = get_num_queries();150 $value = get_option( 'foo' );151 $after = get_num_queries();152 153 $notoptions = wp_cache_get( 'notoptions', 'options' );154 155 $this->assertSame( 0, $after - $before, 'The options cache was not hit on the second call to `get_option()`.' );156 $this->assertFalse( $notoptions, 'The notoptions cache should not be set.' );157 138 } 158 139 … … 549 530 $this->assertArrayNotHasKey( $option_name, $updated_notoptions, 'The "foobar" option should not be in the notoptions cache after adding it.' ); 550 531 } 532 533 /** 534 * Test that get_option() does not hit the external cache multiple times for the same option. 535 * 536 * @ticket 62692 537 * 538 * @covers ::get_option 539 * 540 * @dataProvider data_get_option_does_not_hit_the_external_cache_multiple_times_for_the_same_option 541 * 542 * @param int $expected_connections Expected number of connections to the memcached server. 543 * @param bool $option_exists Whether the option should be set. Default true. 544 * @param string $autoload Whether the option should be auto loaded. Default true. 545 */ 546 public function test_get_option_does_not_hit_the_external_cache_multiple_times_for_the_same_option( $expected_connections, $option_exists = true, $autoload = true ) { 547 if ( ! wp_using_ext_object_cache() ) { 548 $this->markTestSkipped( 'This test requires an external object cache.' ); 549 } 550 551 if ( false === $this->helper_object_cache_stats_cmd_get() ) { 552 $this->markTestSkipped( 'This test requires access to the number of get requests to the external object cache.' ); 553 } 554 555 if ( $option_exists ) { 556 add_option( 'ticket-62692', 'value', '', $autoload ); 557 } 558 559 wp_cache_delete_multiple( array( 'ticket-62692', 'notoptions', 'alloptions' ), 'options' ); 560 561 $connections_start = $this->helper_object_cache_stats_cmd_get(); 562 563 $call_getter = 10; 564 while ( $call_getter-- ) { 565 get_option( 'ticket-62692' ); 566 } 567 568 $connections_end = $this->helper_object_cache_stats_cmd_get(); 569 570 $this->assertSame( $expected_connections, $connections_end - $connections_start ); 571 } 572 573 /** 574 * Data provider. 575 * 576 * @return array[] 577 */ 578 public function data_get_option_does_not_hit_the_external_cache_multiple_times_for_the_same_option() { 579 return array( 580 'exists, autoload' => array( 1, true, true ), 581 'exists, not autoloaded' => array( 3, true, false ), 582 'does not exist' => array( 3, false ), 583 ); 584 } 585 586 /** 587 * Helper function to get the number of get commands from the external object cache. 588 * 589 * @return int|false Number of get command calls, false if unavailable. 590 */ 591 public function helper_object_cache_stats_cmd_get() { 592 if ( ! wp_using_ext_object_cache() || ! function_exists( 'wp_cache_get_stats' ) ) { 593 return false; 594 } 595 596 $stats = wp_cache_get_stats(); 597 598 // Check the shape of the stats. 599 if ( ! is_array( $stats ) ) { 600 return false; 601 } 602 603 // Get the first server's stats. 604 $stats = array_shift( $stats ); 605 606 if ( ! is_array( $stats ) ) { 607 return false; 608 } 609 610 if ( ! array_key_exists( 'cmd_get', $stats ) ) { 611 return false; 612 } 613 614 return $stats['cmd_get']; 615 } 551 616 }
Note: See TracChangeset
for help on using the changeset viewer.