Make WordPress Core

Ticket #27504: 27504.patch

File 27504.patch, 8.4 KB (added by ocean90, 12 years ago)
  • src/wp-includes/class-wp-customize-widgets.php

     
    906906        static function call_widget_update( $widget_id ) {
    907907                global $wp_registered_widget_updates, $wp_registered_widget_controls;
    908908
    909                 $options_transaction = new Options_Transaction();
     909                $option_capture = new Option_Update_Capture();
    910910
    911                 $options_transaction->start();
     911                $option_capture->start();
    912912                $parsed_id   = self::parse_widget_id( $widget_id );
    913913                $option_name = 'widget_' . $parsed_id['id_base'];
    914914
     
    920920                if ( ! empty( $_POST['sanitized_widget_setting'] ) ) {
    921921                        $sanitized_widget_setting = json_decode( self::get_post_value( 'sanitized_widget_setting' ), true );
    922922                        if ( empty( $sanitized_widget_setting ) ) {
    923                                 $options_transaction->rollback();
     923                                $option_capture->stop();
    924924                                return new WP_Error( 'malformed_data', 'Malformed sanitized_widget_setting' );
    925925                        }
    926926
    927927                        $instance = self::sanitize_widget_instance( $sanitized_widget_setting );
    928928                        if ( is_null( $instance ) ) {
    929                                 $options_transaction->rollback();
     929                                $option_capture->stop();
    930930                                return new WP_Error( 'unsanitary_data', 'Unsanitary sanitized_widget_setting' );
    931931                        }
    932932
     
    965965                /**
    966966                 * Make sure the expected option was updated
    967967                 */
    968                 if ( 0 !== $options_transaction->count() ) {
    969                         if ( count( $options_transaction->options ) > 1 ) {
    970                                 $options_transaction->rollback();
     968                if ( 0 !== $option_capture->count() ) {
     969                        if ( count( $option_capture->options ) > 1 ) {
     970                                $option_capture->stop();
    971971                                return new WP_Error( 'unexpected_update', 'Widget unexpectedly updated more than one option.' );
    972972                        }
    973973
    974                         $updated_option_name = key( $options_transaction->options );
     974                        $updated_option_name = key( $option_capture->options );
    975975                        if ( $updated_option_name !== $option_name ) {
    976                                 $options_transaction->rollback();
     976                                $option_capture->stop();
    977977                                return new WP_Error( 'wrong_option', sprintf( 'Widget updated option "%1$s", but expected "%2$s".', $updated_option_name, $option_name ) );
    978978                        }
    979979                }
     
    998998                        $instance = $option;
    999999                }
    10001000
    1001                 $options_transaction->rollback();
     1001                $option_capture->stop();
    10021002                return compact( 'instance', 'form' );
    10031003        }
    10041004
     
    10531053        }
    10541054}
    10551055
    1056 class Options_Transaction {
     1056class Option_Update_Capture {
    10571057
    10581058        /**
    1059          * @var array $options values updated while transaction is open
     1059         * @var array $options values updated while capturing is happening
    10601060         */
    10611061        public $options = array();
    10621062
    10631063        protected $_ignore_transients = true;
    10641064        protected $_is_current = false;
    1065         protected $_operations = array();
    10661065
    10671066        function __construct( $ignore_transients = true ) {
    10681067                $this->_ignore_transients = $ignore_transients;
     
    10851084        }
    10861085
    10871086        /**
    1088          * Get the number of operations performed in the transaction
     1087         * Get the number of options updated
    10891088         * @return bool
    10901089         */
    10911090        function count() {
    1092                 return count( $this->_operations );
     1091                return count( $this->options );
    10931092        }
    10941093
    10951094        /**
    10961095         * Start keeping track of changes to options, and cache their new values
    10971096         */
    10981097        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;
    11041100        }
    11051101
    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 );
    11141104                }
    1115                 $this->options[$option_name] = $new_value;
    1116                 $operation = 'add';
    1117                 $this->_operations[] = compact( 'operation', 'option_name', 'new_value' );
    1118         }
    11191105
    11201106        /**
    1121          * @action updated_option
     1107         *
     1108         * @param  mixed  $new_value
    11221109         * @param string $option_name
    1123          * @param mixed $old_value
    11241110         * @param mixed $new_value
     1111         * @return mixed
    11251112         */
    1126         function _capture_updated_option( $option_name, $old_value, $new_value ) {
     1113        function pre_update_option( $new_value, $option_name, $old_value ) {
    11271114                if ( $this->is_option_ignored( $option_name ) ) {
    11281115                        return;
    11291116                }
    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' ) );
    11331120        }
    11341121
    1135         protected $_pending_delete_option_autoload;
    1136         protected $_pending_delete_option_value;
     1122                $this->options[$option_name] = $new_value;
    11371123
    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;
    11461125                }
    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 ok
    1149                 $this->_pending_delete_option_autoload = $autoload;
    1150                 $this->_pending_delete_option_value    = get_option( $option_name );
    1151         }
    11521126
    11531127        /**
    1154          * @action deleted_option
    1155          * @param string $option_name
     1128         *
     1129         * @param mixed $value
    11561130         */
    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 );
    11601136                }
    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;
    11661139        }
    11671140
    11681141        /**
    11691142         * Undo any changes to the options since start() was called
    11701143         */
    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;
    11801147                        }
    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' ) );
    11831152                        }
    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;
    11861156                        }
     1157
     1158        function __destruct() {
     1159                $this->stop();
    11871160                }
    1188                 $this->_is_current = false;
    11891161        }
    1190 }
  • src/wp-includes/option.php

     
    255255         */
    256256        $value = apply_filters( 'pre_update_option_' . $option, $value, $old_value );
    257257
     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
    258269        // If the new and old values are the same, no need to update.
    259270        if ( $value === $old_value )
    260271                return false;