diff --git a/src/wp-includes/option.php b/src/wp-includes/option.php index 66b22e9fc498..344dfee088cb 100644 --- a/src/wp-includes/option.php +++ b/src/wp-includes/option.php @@ -1330,7 +1330,8 @@ function get_transient( $transient ) { if ( ! isset( $alloptions[ $transient_option ] ) ) { $transient_timeout = '_transient_timeout_' . $transient; - $timeout = get_option( $transient_timeout ); + wp_prime_option_caches( array( $transient_option, $transient_timeout ) ); + $timeout = get_option( $transient_timeout ); if ( false !== $timeout && $timeout < time() ) { delete_option( $transient_option ); delete_option( $transient_timeout ); @@ -1410,6 +1411,7 @@ function set_transient( $transient, $value, $expiration = 0 ) { } else { $transient_timeout = '_transient_timeout_' . $transient; $transient_option = '_transient_' . $transient; + wp_prime_option_caches( array( $transient_option, $transient_timeout ) ); if ( false === get_option( $transient_option ) ) { $autoload = 'on'; diff --git a/tests/phpunit/tests/option/transient.php b/tests/phpunit/tests/option/transient.php index 6c6ba4e2da4b..d4f6c6ce43ae 100644 --- a/tests/phpunit/tests/option/transient.php +++ b/tests/phpunit/tests/option/transient.php @@ -80,6 +80,87 @@ public function test_transient_data_with_timeout() { $this->assertFalse( get_transient( $key ) ); } + /** + * Ensure get_transient() makes a single database request. + * + * @ticket 61193 + * + * @covers ::get_transient + */ + public function test_get_transient_with_timeout_makes_a_single_database_call() { + global $wpdb; + $key = 'test_transient'; + $value = 'test_value'; + $timeout = 100; + $expected_query = "SELECT option_name, option_value FROM $wpdb->options WHERE option_name IN ('_transient_{$key}','_transient_timeout_{$key}')"; + $unexpected_query_transient = "SELECT option_value FROM $wpdb->options WHERE option_name = '_transient_{$key}' LIMIT 1"; + $unexpected_query_timeout = "SELECT option_value FROM $wpdb->options WHERE option_name = '_transient_timeout_{$key}' LIMIT 1"; + $queries = array(); + + set_transient( $key, $value, $timeout ); + + // Clear the cache of both the transient and the timeout. + $option_names = array( + '_transient_' . $key, + '_transient_timeout_' . $key, + ); + foreach ( $option_names as $option_name ) { + wp_cache_delete( $option_name, 'options' ); + } + + add_filter( + 'query', + function ( $query ) use ( &$queries ) { + $queries[] = $query; + return $query; + } + ); + + $before_queries = get_num_queries(); + $this->assertSame( $value, get_transient( $key ) ); + $transient_queries = get_num_queries() - $before_queries; + $this->assertSame( 1, $transient_queries, 'Expected a single database query to retrieve the transient.' ); + $this->assertContains( $expected_query, $queries, 'Expected query to prime both transient options in a single call.' ); + // Note: Some versions of PHPUnit and/or the test suite may report failures as asserting to contain rather than not to contain. + $this->assertNotContains( $unexpected_query_transient, $queries, 'Unexpected query of transient option individually.' ); + $this->assertNotContains( $unexpected_query_timeout, $queries, 'Unexpected query of transient timeout option individually.' ); + } + + /** + * Ensure set_transient() primes the option cache checking for an existing transient. + * + * @ticket 61193 + * + * @covers ::set_transient + */ + public function test_set_transient_primes_option_cache() { + global $wpdb; + $key = 'test_transient'; + $value = 'test_value'; + $timeout = 100; + $expected_query = "SELECT option_name, option_value FROM $wpdb->options WHERE option_name IN ('_transient_{$key}','_transient_timeout_{$key}')"; + $unexpected_query_transient = "SELECT option_value FROM $wpdb->options WHERE option_name = '_transient_{$key}' LIMIT 1"; + $unexpected_query_timeout = "SELECT option_value FROM $wpdb->options WHERE option_name = '_transient_timeout_{$key}' LIMIT 1"; + $queries = array(); + + add_filter( + 'query', + function ( $query ) use ( &$queries ) { + $queries[] = $query; + return $query; + } + ); + + $before_queries = get_num_queries(); + $this->assertTrue( set_transient( $key, $value, $timeout ) ); + $transient_queries = get_num_queries() - $before_queries; + $this->assertSame( 3, $transient_queries, 'Expected three database queries setting the transient.' ); + $this->assertContains( $expected_query, $queries, 'Expected query to prime both transient options in a single call.' ); + // Note: Some versions of PHPUnit and/or the test suite may report failures as asserting to contain rather than not to contain. + $this->assertNotContains( $unexpected_query_transient, $queries, 'Unexpected query of transient option individually.' ); + $this->assertNotContains( $unexpected_query_timeout, $queries, 'Unexpected query of transient timeout option individually.' ); + } + /** * @ticket 22807 *