Ticket #27504: 27504.patch
File 27504.patch, 8.4 KB (added by , 10 years ago) |
---|
-
src/wp-includes/class-wp-customize-widgets.php
906 906 static function call_widget_update( $widget_id ) { 907 907 global $wp_registered_widget_updates, $wp_registered_widget_controls; 908 908 909 $option s_transaction = new Options_Transaction();909 $option_capture = new Option_Update_Capture(); 910 910 911 $option s_transaction->start();911 $option_capture->start(); 912 912 $parsed_id = self::parse_widget_id( $widget_id ); 913 913 $option_name = 'widget_' . $parsed_id['id_base']; 914 914 … … 920 920 if ( ! empty( $_POST['sanitized_widget_setting'] ) ) { 921 921 $sanitized_widget_setting = json_decode( self::get_post_value( 'sanitized_widget_setting' ), true ); 922 922 if ( empty( $sanitized_widget_setting ) ) { 923 $option s_transaction->rollback();923 $option_capture->stop(); 924 924 return new WP_Error( 'malformed_data', 'Malformed sanitized_widget_setting' ); 925 925 } 926 926 927 927 $instance = self::sanitize_widget_instance( $sanitized_widget_setting ); 928 928 if ( is_null( $instance ) ) { 929 $option s_transaction->rollback();929 $option_capture->stop(); 930 930 return new WP_Error( 'unsanitary_data', 'Unsanitary sanitized_widget_setting' ); 931 931 } 932 932 … … 965 965 /** 966 966 * Make sure the expected option was updated 967 967 */ 968 if ( 0 !== $option s_transaction->count() ) {969 if ( count( $option s_transaction->options ) > 1 ) {970 $option s_transaction->rollback();968 if ( 0 !== $option_capture->count() ) { 969 if ( count( $option_capture->options ) > 1 ) { 970 $option_capture->stop(); 971 971 return new WP_Error( 'unexpected_update', 'Widget unexpectedly updated more than one option.' ); 972 972 } 973 973 974 $updated_option_name = key( $option s_transaction->options );974 $updated_option_name = key( $option_capture->options ); 975 975 if ( $updated_option_name !== $option_name ) { 976 $option s_transaction->rollback();976 $option_capture->stop(); 977 977 return new WP_Error( 'wrong_option', sprintf( 'Widget updated option "%1$s", but expected "%2$s".', $updated_option_name, $option_name ) ); 978 978 } 979 979 } … … 998 998 $instance = $option; 999 999 } 1000 1000 1001 $option s_transaction->rollback();1001 $option_capture->stop(); 1002 1002 return compact( 'instance', 'form' ); 1003 1003 } 1004 1004 … … 1053 1053 } 1054 1054 } 1055 1055 1056 class Option s_Transaction{1056 class Option_Update_Capture { 1057 1057 1058 1058 /** 1059 * @var array $options values updated while transaction is open1059 * @var array $options values updated while capturing is happening 1060 1060 */ 1061 1061 public $options = array(); 1062 1062 1063 1063 protected $_ignore_transients = true; 1064 1064 protected $_is_current = false; 1065 protected $_operations = array();1066 1065 1067 1066 function __construct( $ignore_transients = true ) { 1068 1067 $this->_ignore_transients = $ignore_transients; … … 1085 1084 } 1086 1085 1087 1086 /** 1088 * Get the number of op erations performed in the transaction1087 * Get the number of options updated 1089 1088 * @return bool 1090 1089 */ 1091 1090 function count() { 1092 return count( $this-> _operations );1091 return count( $this->options ); 1093 1092 } 1094 1093 1095 1094 /** 1096 1095 * Start keeping track of changes to options, and cache their new values 1097 1096 */ 1098 1097 function start() { 1099 $this->_is_current = true; 1100 add_action( 'added_option', array( $this, '_capture_added_option' ), 10, 2 ); 1101 add_action( 'updated_option', array( $this, '_capture_updated_option' ), 10, 3 ); 1102 add_action( 'delete_option', array( $this, '_capture_pre_deleted_option' ), 10, 1 ); 1103 add_action( 'deleted_option', array( $this, '_capture_deleted_option' ), 10, 1 ); 1098 if ( $this->_is_current ) { 1099 return; 1104 1100 } 1105 1101 1106 /** 1107 * @action added_option 1108 * @param $option_name 1109 * @param $new_value 1110 */ 1111 function _capture_added_option( $option_name, $new_value ) { 1112 if ( $this->is_option_ignored( $option_name ) ) { 1113 return; 1102 $this->_is_current = true; 1103 add_filter( 'pre_update_option', array( $this, 'pre_update_option' ), 10, 3 ); 1114 1104 } 1115 $this->options[$option_name] = $new_value;1116 $operation = 'add';1117 $this->_operations[] = compact( 'operation', 'option_name', 'new_value' );1118 }1119 1105 1120 1106 /** 1121 * @action updated_option 1107 * 1108 * @param mixed $new_value 1122 1109 * @param string $option_name 1123 * @param mixed $old_value1124 1110 * @param mixed $new_value 1111 * @return mixed 1125 1112 */ 1126 function _capture_updated_option( $option_name, $old_value, $new_value ) {1113 function pre_update_option( $new_value, $option_name, $old_value ) { 1127 1114 if ( $this->is_option_ignored( $option_name ) ) { 1128 1115 return; 1129 1116 } 1130 $this->options[$option_name] = $new_value; 1131 $operation = 'update';1132 $this->_operations[] = compact( 'operation', 'option_name', 'old_value', 'new_value');1117 1118 if ( ! isset( $this->options[$option_name] ) ) { 1119 add_filter( "pre_option_{$option_name}", array( $this, 'pre_get_option' ) ); 1133 1120 } 1134 1121 1135 protected $_pending_delete_option_autoload; 1136 protected $_pending_delete_option_value; 1122 $this->options[$option_name] = $new_value; 1137 1123 1138 /** 1139 * It's too bad the old_value and autoload aren't passed into the deleted_option action 1140 * @action delete_option 1141 * @param string $option_name 1142 */ 1143 function _capture_pre_deleted_option( $option_name ) { 1144 if ( $this->is_option_ignored( $option_name ) ) { 1145 return; 1124 return $old_value; 1146 1125 } 1147 global $wpdb;1148 $autoload = $wpdb->get_var( $wpdb->prepare( "SELECT autoload FROM $wpdb->options WHERE option_name = %s", $option_name ) ); // db call ok; no-cache ok1149 $this->_pending_delete_option_autoload = $autoload;1150 $this->_pending_delete_option_value = get_option( $option_name );1151 }1152 1126 1153 1127 /** 1154 * @action deleted_option1155 * @param string $option_name1128 * 1129 * @param mixed $value 1156 1130 */ 1157 function _capture_deleted_option( $option_name ) { 1158 if ( $this->is_option_ignored( $option_name ) ) { 1159 return; 1131 function pre_get_option( $value ) { 1132 $option_name = preg_replace( '/^pre_option_/', '', current_filter() ); 1133 if ( isset( $this->options[$option_name] ) ) { 1134 $value = $this->options[$option_name]; 1135 $value = apply_filters( 'option_' . $option_name, $value ); 1160 1136 } 1161 unset( $this->options[$option_name] ); 1162 $operation = 'delete'; 1163 $old_value = $this->_pending_delete_option_value; 1164 $autoload = $this->_pending_delete_option_autoload; 1165 $this->_operations[] = compact( 'operation', 'option_name', 'old_value', 'autoload' ); 1137 1138 return $value; 1166 1139 } 1167 1140 1168 1141 /** 1169 1142 * Undo any changes to the options since start() was called 1170 1143 */ 1171 function rollback() { 1172 remove_action( 'updated_option', array( $this, '_capture_updated_option' ), 10, 3 ); 1173 remove_action( 'added_option', array( $this, '_capture_added_option' ), 10, 2 ); 1174 remove_action( 'delete_option', array( $this, '_capture_pre_deleted_option' ), 10, 1 ); 1175 remove_action( 'deleted_option', array( $this, '_capture_deleted_option' ), 10, 1 ); 1176 while ( 0 !== count( $this->_operations ) ) { 1177 $option_operation = array_pop( $this->_operations ); 1178 if ( 'add' === $option_operation['operation'] ) { 1179 delete_option( $option_operation['option_name'] ); 1144 function stop() { 1145 if ( ! $this->_is_current ) { 1146 return; 1180 1147 } 1181 else if ( 'delete' === $option_operation['operation'] ) { 1182 add_option( $option_operation['option_name'], $option_operation['old_value'], null, $option_operation['autoload'] ); 1148 1149 remove_filter( 'pre_update_option', array( $this, 'pre_update_option' ), 10, 3 ); 1150 foreach ( array_keys( $this->options ) as $option_name ) { 1151 remove_filter( "pre_option_{$option_name}", array( $this, 'pre_get_option' ) ); 1183 1152 } 1184 else if ( 'update' === $option_operation['operation'] ) { 1185 update_option( $option_operation['option_name'], $option_operation['old_value'] ); 1153 1154 $this->options = array(); 1155 $this->_is_current = false; 1186 1156 } 1157 1158 function __destruct() { 1159 $this->stop(); 1187 1160 } 1188 $this->_is_current = false;1189 1161 } 1190 } -
src/wp-includes/option.php
255 255 */ 256 256 $value = apply_filters( 'pre_update_option_' . $option, $value, $old_value ); 257 257 258 /** 259 * Filter an option before its value is (maybe) serialized and updated. 260 * 261 * @since 3.9.0 262 * 263 * @param mixed $value The new, unserialized option value. 264 * @param string $option Name of the option. 265 * @param mixed $old_value The old option value. 266 */ 267 $value = apply_filters( 'pre_update_option', $value, $option, $old_value ); 268 258 269 // If the new and old values are the same, no need to update. 259 270 if ( $value === $old_value ) 260 271 return false;