From 7950b0e306ee83530fb933d281e33c13744d7b9f Mon Sep 17 00:00:00 2001 From: Peter Wilson Date: Sun, 11 Dec 2016 21:42:12 +0000 Subject: [PATCH] Options: Prevent unnecessary SQL updates by `update_option`. Previously an option containing an object would trigger an SQL `UPDATE` on all calls to `update_option`, even if the old and new values were identical. This was due to the old and new values having differing resource IDs. This change compares the old and new values as serialized data to remove the resource ID from the comparison. Props salcode, bradyvercher, peterwilsoncc. Fixes #38903. git-svn-id: https://develop.svn.wordpress.org/trunk@39564 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/option.php | 13 +++++++++-- tests/phpunit/tests/option/updateOption.php | 25 +++++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/wp-includes/option.php b/src/wp-includes/option.php index ac17c2f501..66fb6e4fd1 100644 --- a/src/wp-includes/option.php +++ b/src/wp-includes/option.php @@ -295,9 +295,18 @@ function update_option( $option, $value, $autoload = null ) { */ $value = apply_filters( 'pre_update_option', $value, $option, $old_value ); - // If the new and old values are the same, no need to update. - if ( $value === $old_value ) + /* + * If the new and old values are the same, no need to update. + * + * Unserialized values will be adequate in most cases. If the unserialized + * data differs, the (maybe) serialized data is checked to avoid + * unnecessary database calls for otherwise identical object instances. + * + * See https://core.trac.wordpress.org/ticket/38903 + */ + if ( $value === $old_value || maybe_serialize( $value ) === maybe_serialize( $old_value ) ) { return false; + } /** This filter is documented in wp-includes/option.php */ if ( apply_filters( 'default_option_' . $option, false, $option, false ) === $old_value ) { diff --git a/tests/phpunit/tests/option/updateOption.php b/tests/phpunit/tests/option/updateOption.php index 32ae5e5b4e..eb85b1203a 100644 --- a/tests/phpunit/tests/option/updateOption.php +++ b/tests/phpunit/tests/option/updateOption.php @@ -165,6 +165,31 @@ class Tests_Option_UpdateOption extends WP_UnitTestCase { $this->assertEquals( $value, 'bar2' ); } + /** + * @ticket 38903 + */ + public function test_update_option_array_with_object() { + $array_w_object = array( + 'url' => 'http://src.wordpress-develop.dev/wp-content/uploads/2016/10/cropped-Blurry-Lights.jpg', + 'meta_data' => (object) array( + 'attachment_id' => 292, + 'height' => 708, + 'width' => 1260, + ), + ); + + // Add the option, it did not exist before this. + add_option( 'array_w_object', $array_w_object ); + + $num_queries_pre_update = get_num_queries(); + + // Update the option using the same array with an object for the value. + $this->assertFalse( update_option( 'array_w_object', $array_w_object ) ); + + // Check that no new database queries were performed. + $this->assertEquals( $num_queries_pre_update, get_num_queries() ); + } + /** * `add_filter()` callback for test_should_respect_default_option_filter_when_option_does_not_yet_exist_in_database(). */