WordPress.org

Make WordPress Core

Opened 4 years ago

Closed 4 years ago

#33499 closed enhancement (fixed)

Allow `$autoload` to be set in `WP_Customize_Setting::_update_option()`

Reported by: dlh Owned by: westonruter
Milestone: 4.4 Priority: normal
Severity: normal Version:
Component: Customize Keywords: has-patch commit
Focuses: Cc:
PR Number:

Description (last modified by westonruter)

WP_Customize_Setting::_update_option() calls update_option() for us, but it doesn't currently provide a way to use the $autoload parameter that was added to update_option() in 4.2 (see #18244). So, it seems tricky to avoid autoloading options that are managed exclusively through the Customizer.

I'm not sure whether a Customizer-specific filter would be the best way to change this behavior, but the attached patch would add one in WP_Customize_Setting::_update_option().

Adding a property to WP_Customize_Setting didn't seem like the right choice because multiple settings can affect the same option. But an alternative approach might be something that can be used in any add_option() or update_option() call.

Attachments (4)

33499.patch (1.4 KB) - added by dlh 4 years ago.
33499.2.diff (1.2 KB) - added by westonruter 4 years ago.
Alternate approach
33499.3.diff (1.2 KB) - added by westonruter 4 years ago.
33499.4.diff (4.1 KB) - added by westonruter 4 years ago.

Download all attachments as: .zip

Change History (19)

@dlh
4 years ago

#1 @westonruter
4 years ago

  • Keywords reporter-feedback added

Instead of adding a new filter, what about just subclassing WP_Customize_Setting in your plugin? For instance:

<?php

class Non_Autoloaded_Option_Customize_Setting extends WP_Customize_Setting {

        public $type = 'non_autoloaded_option';

        /**
         * Update the non-autoloaded option from the value of the setting.
         *
         * @param mixed $value The value to update.
         * @return bool The result of saving the value.
         */
        protected function _update_option( $value ) {
                $autoload = false;

                // Handle non-array option.
                if ( empty( $this->id_data['keys'] ) ) {
                        return update_option( $this->id_data['base'], $value, $autoload );
                }

                // Handle array-based options.
                $options = get_option( $this->id_data['base'] );
                $options = $this->multidimensional_replace( $options, $this->id_data['keys'], $value );
                if ( isset( $options ) ) {
                        return update_option( $this->id_data['base'], $options, $autoload );
                }
        }
}

And then use it by explicitly constructing the setting object when calling WP_Customize_Manager::add_setting():

<?php
add_action( 'customize_register', function ( $wp_customize ) {
        /** @var WP_Customize_Manager $wp_customize */
        $wp_customize->add_setting( new Non_Autoloaded_Option_Customize_Setting( $wp_customize, 'foo' ));

        $wp_customize->add_control( 'foo', array( /* ... */ ) )
} );

#2 @dlh
4 years ago

  • Keywords reporter-feedback removed

Sure, that works too. If that's the recommended approach, I'm happy to use it.

@westonruter
4 years ago

Alternate approach

#3 @westonruter
4 years ago

  • Description modified (diff)
  • Keywords has-patch added
  • Milestone changed from Awaiting Review to 4.4

I think adding Core support for Customizing non-autoloaded options makes sense too, but I think it can be done nicely without a filter.

Take a look at 33499.2.diff.

This would allow non-autoloaded options to be registered with just:

<?php

$wp_customize->add_setting( 'foo', array(
    'type' => 'option',
    'autoload' => false,
) );

#4 @dlh
4 years ago

Maybe it's so unlikely as to not worry about, but if I'm following correctly, wouldn't autoload bounce around if I registered:

<?php

$wp_customize->add_setting( 'foo[first]', array(
    'type' => 'option',
    'autoload' => false,
) );

$wp_customize->add_setting( 'foo[second]', array(
    'type' => 'option',
    'autoload' => true,
) );

I also wondered whether it would be unexpected that an entire option's autoload could be affected by someone customizing only one part of it, in the case of arrays.

#5 @westonruter
4 years ago

@dlh That's a good point. You're right that these would conflict, or rather whichever setting is saved first would be the one that would determine whether it was autoloaded. Nevertheless, I do agree this is an unlikely scenario. But, at the same time, all of this can be avoided if the plugin, upon activation, just calls:

add_option( 'foo', '', '', 'no' );

This will ensure that subsequent update_option() calls won't result in the option getting autoloaded.

#6 @dlh
4 years ago

@westonruter Calling add_option() on activation would also work, although when I first thought about this behavior, I was creating an option in a site's active theme.

Regardless, I'd be comfortable using any of the methods we've discussed here to override the default value, should the scenario come up again. Thanks for working through them with me. If it turns out there's also a case for a change in core, I'll help with that however I can.

This ticket was mentioned in Slack in #core by sergey. View the logs.


4 years ago

#8 @SergeyBiryukov
4 years ago

  • Owner set to westonruter
  • Status changed from new to assigned

#9 @westonruter
4 years ago

As for the issue where different multidimensional settings may be registered for the same option but have different values for $autoload, there is a possible solution in what I've been working on for #32103, since there is a shared storage area for such setting information.

#10 @DrewAPicture
4 years ago

@westonruter: What's left here to move this ticket forward?

#11 @westonruter
4 years ago

I was waiting for #32103 to land, which it just did. So now we now have the option to store the autoload state centrally in the “aggregated multidimensional” static class array variable.

This ticket was mentioned in Slack in #core by westonruter. View the logs.


4 years ago

@westonruter
4 years ago

#13 @westonruter
4 years ago

33499.3.diff makes use of the multidimensional-aggregate static array to store the autoload flag. So if you wanted to add a non-autoloaded option you can still do this:

<?php
$wp_customize->add_setting( 'foo[first]', array(
    'type' => 'option',
    'autoload' => false,
) );

However, there would not be an $setting->autoload property to read back the value from. I also didn't think it would be good to add a class member variable to the base WP_Customize_Setting class since it is inherited by all other settings, and it wouldn't make sense for them all to have an unused $autoload property.

@westonruter
4 years ago

#14 @westonruter
4 years ago

  • Keywords commit added

33499.4.diff changes behavior to leave autoload undefined if not specified. This ensures that if one multidimensional option setting indicates the autoload=false value, that the others will not override it with autoload=true. Also adds unit tests.

#15 @westonruter
4 years ago

  • Resolution set to fixed
  • Status changed from assigned to closed

In 35305:

Customizer: Allow new option settings to not be saved as autoloaded by passing an autoload arg value of false.

The autoload argument value is passed along to update_option() which has accepted an $autoload parameter since [31628].

Props westonruter, dlh.
See #26394.
Fixes #33499.

Note: See TracTickets for help on using tickets.