WordPress.org

Make WordPress Core

Ticket #27112: 27112.no-options-transaction.diff

File 27112.no-options-transaction.diff, 7.8 KB (added by westonruter, 8 years ago)

Add pre_update_option filter, replace Options_Transaction with Option_Update_Capture. https://github.com/x-team/wordpress-develop/pull/1/files

  • src/wp-includes/class-wp-customize-widgets.php

    diff --git src/wp-includes/class-wp-customize-widgets.php src/wp-includes/class-wp-customize-widgets.php
    index 94d5974..b1436e8 100644
    class WP_Customize_Widgets { 
    941941        static function call_widget_update( $widget_id ) {
    942942                global $wp_registered_widget_updates, $wp_registered_widget_controls;
    943943
    944                 $options_transaction = new Options_Transaction();
     944                $option_capture = new Option_Update_Capture();
    945945
    946946                try {
    947                         $options_transaction->start();
     947                        $option_capture->start();
    948948                        $parsed_id   = self::parse_widget_id( $widget_id );
    949949                        $option_name = 'widget_' . $parsed_id['id_base'];
    950950
    class WP_Customize_Widgets { 
    997997                        /**
    998998                         * Make sure the expected option was updated
    999999                         */
    1000                         if ( 0 !== $options_transaction->count() ) {
    1001                                 if ( count( $options_transaction->options ) > 1 ) {
     1000                        if ( 0 !== $option_capture->count() ) {
     1001                                if ( count( $option_capture->options ) > 1 ) {
    10021002                                        throw new Widget_Customizer_Exception( sprintf( 'Widget %1$s unexpectedly updated more than one option.', $widget_id ) );
    10031003                                }
    1004                                 $updated_option_name = key( $options_transaction->options );
     1004                                $updated_option_name = key( $option_capture->options );
    10051005                                if ( $updated_option_name !== $option_name ) {
    10061006                                        throw new Widget_Customizer_Exception( sprintf( 'Widget %1$s updated option "%2$s", but expected "%3$s".', $widget_id, $updated_option_name, $option_name ) );
    10071007                                }
    class WP_Customize_Widgets { 
    10271027                                $instance = $option;
    10281028                        }
    10291029
    1030                         $options_transaction->rollback();
     1030                        $option_capture->stop();
    10311031                        return compact( 'instance', 'form' );
    10321032                }
    10331033                catch ( Exception $e ) {
    1034                         $options_transaction->rollback();
     1034                        $option_capture->stop();
    10351035                        throw $e;
    10361036                }
    10371037        }
    class WP_Customize_Widgets { 
    10931093
    10941094class Widget_Customizer_Exception extends Exception {}
    10951095
    1096 class Options_Transaction {
     1096class Option_Update_Capture {
    10971097
    10981098        /**
    1099          * @var array $options values updated while transaction is open
     1099         * @var array $options values updated while capturing is happening
    11001100         */
    11011101        public $options = array();
    11021102
    11031103        protected $_ignore_transients = true;
    11041104        protected $_is_current = false;
    1105         protected $_operations = array();
    11061105
    11071106        function __construct( $ignore_transients = true ) {
    11081107                $this->_ignore_transients = $ignore_transients;
    class Options_Transaction { 
    11251124        }
    11261125
    11271126        /**
    1128          * Get the number of operations performed in the transaction
     1127         * Get the number of options updated
    11291128         * @return bool
    11301129         */
    11311130        function count() {
    1132                 return count( $this->_operations );
     1131                return count( $this->options );
    11331132        }
    11341133
    11351134        /**
    11361135         * Start keeping track of changes to options, and cache their new values
    11371136         */
    11381137        function start() {
    1139                 $this->_is_current = true;
    1140                 add_action( 'added_option', array( $this, '_capture_added_option' ), 10, 2 );
    1141                 add_action( 'updated_option', array( $this, '_capture_updated_option' ), 10, 3 );
    1142                 add_action( 'delete_option', array( $this, '_capture_pre_deleted_option' ), 10, 1 );
    1143                 add_action( 'deleted_option', array( $this, '_capture_deleted_option' ), 10, 1 );
    1144         }
    1145 
    1146         /**
    1147          * @action added_option
    1148          * @param $option_name
    1149          * @param $new_value
    1150          */
    1151         function _capture_added_option( $option_name, $new_value ) {
    1152                 if ( $this->is_option_ignored( $option_name ) ) {
     1138                if ( $this->_is_current ) {
    11531139                        return;
    11541140                }
    1155                 $this->options[$option_name] = $new_value;
    1156                 $operation = 'add';
    1157                 $this->_operations[] = compact( 'operation', 'option_name', 'new_value' );
     1141                $this->_is_current = true;
     1142                add_filter( 'pre_update_option', array( $this, 'pre_update_option' ), 10, 3 );
    11581143        }
    11591144
    11601145        /**
    1161          * @action updated_option
     1146         * @param mixed $new_value
    11621147         * @param string $option_name
    11631148         * @param mixed $old_value
    1164          * @param mixed $new_value
     1149         * @return mixed
     1150         * @filter pre_update_option
    11651151         */
    1166         function _capture_updated_option( $option_name, $old_value, $new_value ) {
     1152        function pre_update_option( $new_value, $option_name, $old_value ) {
    11671153                if ( $this->is_option_ignored( $option_name ) ) {
    11681154                        return;
    11691155                }
     1156                if ( ! isset( $this->options[$option_name] ) ) {
     1157                        add_filter( "pre_option_{$option_name}", array( $this, 'pre_get_option' ) );
     1158                }
    11701159                $this->options[$option_name] = $new_value;
    1171                 $operation = 'update';
    1172                 $this->_operations[] = compact( 'operation', 'option_name', 'old_value', 'new_value' );
     1160                return $old_value;
    11731161        }
    11741162
    1175         protected $_pending_delete_option_autoload;
    1176         protected $_pending_delete_option_value;
    1177 
    11781163        /**
    1179          * It's too bad the old_value and autoload aren't passed into the deleted_option action
    1180          * @action delete_option
    1181          * @param string $option_name
     1164         * @param $value
     1165         * @return mixed
    11821166         */
    1183         function _capture_pre_deleted_option( $option_name ) {
    1184                 if ( $this->is_option_ignored( $option_name ) ) {
    1185                         return;
     1167        function pre_get_option( $value ) {
     1168                $option_name = preg_replace( '/^pre_option_/', '', current_filter() );
     1169                if ( isset( $this->options[$option_name] ) ) {
     1170                        $value = $this->options[$option_name];
     1171                        $value = apply_filters( 'option_' . $option_name, $value );
    11861172                }
    1187                 global $wpdb;
    1188                 $autoload = $wpdb->get_var( $wpdb->prepare( "SELECT autoload FROM $wpdb->options WHERE option_name = %s", $option_name ) ); // db call ok; no-cache ok
    1189                 $this->_pending_delete_option_autoload = $autoload;
    1190                 $this->_pending_delete_option_value    = get_option( $option_name );
     1173                return $value;
    11911174        }
    11921175
    11931176        /**
    1194          * @action deleted_option
    1195          * @param string $option_name
     1177         * Undo any changes to the options since start() was called
    11961178         */
    1197         function _capture_deleted_option( $option_name ) {
    1198                 if ( $this->is_option_ignored( $option_name ) ) {
     1179        function stop() {
     1180                if ( ! $this->_is_current ) {
    11991181                        return;
    12001182                }
    1201                 unset( $this->options[$option_name] );
    1202                 $operation = 'delete';
    1203                 $old_value = $this->_pending_delete_option_value;
    1204                 $autoload  = $this->_pending_delete_option_autoload;
    1205                 $this->_operations[] = compact( 'operation', 'option_name', 'old_value', 'autoload' );
    1206         }
    1207 
    1208         /**
    1209          * Undo any changes to the options since start() was called
    1210          */
    1211         function rollback() {
    1212                 remove_action( 'updated_option', array( $this, '_capture_updated_option' ), 10, 3 );
    1213                 remove_action( 'added_option', array( $this, '_capture_added_option' ), 10, 2 );
    1214                 remove_action( 'delete_option', array( $this, '_capture_pre_deleted_option' ), 10, 1 );
    1215                 remove_action( 'deleted_option', array( $this, '_capture_deleted_option' ), 10, 1 );
    1216                 while ( 0 !== count( $this->_operations ) ) {
    1217                         $option_operation = array_pop( $this->_operations );
    1218                         if ( 'add' === $option_operation['operation'] ) {
    1219                                 delete_option( $option_operation['option_name'] );
    1220                         }
    1221                         else if ( 'delete' === $option_operation['operation'] ) {
    1222                                 add_option( $option_operation['option_name'], $option_operation['old_value'], null, $option_operation['autoload'] );
    1223                         }
    1224                         else if ( 'update' === $option_operation['operation'] ) {
    1225                                 update_option( $option_operation['option_name'], $option_operation['old_value'] );
    1226                         }
    1227                         else {
    1228                                 throw new Exception( 'Unexpected operation' );
    1229                         }
     1183                remove_filter( 'pre_update_option', array( $this, 'pre_update_option' ), 10, 3 );
     1184                foreach ( array_keys( $this->options ) as $option_name ) {
     1185                        remove_filter( "pre_option_{$option_name}", array( $this, 'pre_get_option' ) );
    12301186                }
     1187                $this->options     = array();
    12311188                $this->_is_current = false;
    12321189        }
     1190
     1191        function __destruct() {
     1192                $this->stop();
     1193        }
    12331194}
  • src/wp-includes/option.php

    diff --git src/wp-includes/option.php src/wp-includes/option.php
    index e1ad731..8f5405a 100644
    function update_option( $option, $value ) { 
    254254         * @param mixed $old_value The old option value.
    255255         */
    256256        $value = apply_filters( 'pre_update_option_' . $option, $value, $old_value );
     257        $value = apply_filters( 'pre_update_option', $value, $option, $old_value );
    257258
    258259        // If the new and old values are the same, no need to update.
    259260        if ( $value === $old_value )