WordPress.org

Make WordPress Core

Changeset 32602


Ignore:
Timestamp:
05/26/2015 04:50:03 PM (4 years ago)
Author:
westonruter
Message:

Add support for WP_Widget::get_settings() returning ArrayIterator/ArrayObject instances.

Plugins can use pre_option_widget_{$id_base} filters to return ArrayIterator/ArrayObject instances instead of primitive arrays. This makes possible for widget instance data to be drawn from somewhere else than wp_options, such as a custom post type.

Add unit tests for widgets.

Fixes #32474.

Location:
trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/widgets.php

    r32545 r32602  
    215215        $empty = true;
    216216
    217         if ( is_array($settings) ) {
    218             foreach ( array_keys($settings) as $number ) {
    219                 if ( is_numeric($number) ) {
    220                     $this->_set($number);
    221                     $this->_register_one($number);
     217        // When $settings is an array-like object, get an intrinsic array for use with array_keys().
     218        if ( $settings instanceof ArrayObject || $settings instanceof ArrayIterator ) {
     219            $settings = $settings->getArrayCopy();
     220        }
     221
     222        if ( is_array( $settings ) ) {
     223            foreach ( array_keys( $settings ) as $number ) {
     224                if ( is_numeric( $number ) ) {
     225                    $this->_set( $number );
     226                    $this->_register_one( $number );
    222227                    $empty = false;
    223228                }
     
    226231
    227232        if ( $empty ) {
    228             // If there are none, we register the widget's existence with a
    229             // generic template
    230             $this->_set(1);
     233            // If there are none, we register the widget's existence with a generic template.
     234            $this->_set( 1 );
    231235            $this->_register_one();
    232236        }
     
    295299     */
    296300    public function display_callback( $args, $widget_args = 1 ) {
    297         if ( is_numeric($widget_args) )
     301        if ( is_numeric( $widget_args ) ) {
    298302            $widget_args = array( 'number' => $widget_args );
     303        }
    299304
    300305        $widget_args = wp_parse_args( $widget_args, array( 'number' => -1 ) );
    301306        $this->_set( $widget_args['number'] );
    302         $instance = $this->get_settings();
    303 
    304         if ( array_key_exists( $this->number, $instance ) ) {
    305             $instance = $instance[$this->number];
     307        $instances = $this->get_settings();
     308
     309        if ( isset( $instances[ $this->number ] ) ) {
     310            $instance = $instances[ $this->number ];
    306311
    307312            /**
     
    425430     *
    426431     * @param int|array $widget_args Widget instance number or array of widget arguments.
     432     * @return string|null
    427433     */
    428434    public function form_callback( $widget_args = 1 ) {
     
    517523    public function get_settings() {
    518524
    519         $settings = get_option($this->option_name);
    520 
    521         if ( false === $settings && isset($this->alt_option_name) )
    522             $settings = get_option($this->alt_option_name);
    523 
    524         if ( !is_array($settings) )
     525        $settings = get_option( $this->option_name );
     526
     527        if ( false === $settings && isset( $this->alt_option_name ) ) {
     528            $settings = get_option( $this->alt_option_name );
     529        }
     530
     531        if ( ! is_array( $settings ) && ! ( $settings instanceof ArrayObject || $settings instanceof ArrayIterator ) ) {
    525532            $settings = array();
    526 
    527         if ( !empty($settings) && !array_key_exists('_multiwidget', $settings) ) {
    528             // old format, convert if single widget
    529             $settings = wp_convert_widget_settings($this->id_base, $this->option_name, $settings);
    530         }
    531 
    532         unset($settings['_multiwidget'], $settings['__i__']);
     533        }
     534
     535        if ( ! empty( $settings ) && ! isset( $settings['_multiwidget'] ) ) {
     536            // Old format, convert if single widget.
     537            $settings = wp_convert_widget_settings( $this->id_base, $this->option_name, $settings );
     538        }
     539
     540        unset( $settings['_multiwidget'], $settings['__i__'] );
    533541        return $settings;
    534542    }
  • trunk/tests/phpunit/tests/widgets.php

    r25002 r32602  
    22
    33/**
    4  * Test widget template tags
     4 * Test functions and classes for widgets and sidebars.
    55 *
    66 * @group widgets
     
    88class Tests_Widgets extends WP_UnitTestCase {
    99
    10     function test_register_widget_core_widget() {
    11 
     10    function clean_up_global_scope() {
     11        global $wp_widget_factory, $wp_registered_sidebars, $wp_registered_widgets, $wp_registered_widget_controls, $wp_registered_widget_updates;
     12
     13        $wp_registered_sidebars = array();
     14        $wp_registered_widgets = array();
     15        $wp_registered_widget_controls = array();
     16        $wp_registered_widget_updates = array();
     17        $wp_widget_factory->widgets = array();
     18
     19        parent::clean_up_global_scope();
     20    }
     21
     22    function tearDown() {
     23        global $wp_customize;
     24        $wp_customize = null;
     25        parent::tearDown();
     26    }
     27
     28    /**
     29     * @see register_widget()
     30     * @see unregister_widget()
     31     */
     32    function test_register_and_unregister_widget_core_widget() {
    1233        global $wp_widget_factory;
    1334
    14         unregister_widget( 'WP_Widget_Search' );
    15         register_widget( 'WP_Widget_Search' );
    16 
    17         $this->assertTrue( isset( $wp_widget_factory->widgets['WP_Widget_Search'] ) );
    18 
    19     }
    20 
    21     function test_unregister_widget_core_widget() {
    22 
    23         global $wp_widget_factory;
    24 
    25         unregister_widget( 'WP_Widget_Search' );
    26 
    27         $this->assertFalse( isset( $wp_widget_factory->widgets['WP_Widget_Search'] ) );
    28 
    29     }
    30 
     35        $widget_class = 'WP_Widget_Search';
     36        register_widget( $widget_class );
     37        $this->assertArrayHasKey( $widget_class, $wp_widget_factory->widgets );
     38
     39        unregister_widget( $widget_class );
     40        $this->assertArrayNotHasKey( $widget_class, $wp_widget_factory->widgets );
     41    }
     42
     43    /**
     44     * @see register_sidebars()
     45     */
    3146    function test_register_sidebars_single() {
    3247
     
    3954    }
    4055
     56    /**
     57     * @see register_sidebars()
     58     */
    4159    function test_register_sidebars_multiple() {
    4260
    4361        global $wp_registered_sidebars;
    4462
     63        $result = array();
    4564        $num = 3;
    4665        $id_base = 'WP Unit Test';
     
    4968        $names = wp_list_pluck( $wp_registered_sidebars, 'name' );
    5069        for ( $i = 1; $i <= $num; $i++ ) {
    51             if ( in_array( "$id_base $i", $names ) )
     70            if ( in_array( "$id_base $i", $names ) ) {
    5271                $result[] = true;
     72            }
    5373        }
    5474
     
    5777    }
    5878
    59     function test_register_sidebar() {
     79    /**
     80     * @see register_sidebar
     81     * @see unregister_sidebar
     82     */
     83    function test_register_and_unregister_sidebar() {
    6084
    6185        global $wp_registered_sidebars;
    6286
    63         register_sidebar( array( 'id' => 'wp-unit-test' ) );
    64 
    65         $this->assertTrue( isset( $wp_registered_sidebars['wp-unit-test'] ) );
    66 
    67     }
    68 
    69     function test_unregister_sidebar() {
    70 
    71         global $wp_registered_sidebars;
    72 
    73         unregister_sidebar( 'sidebar-1' );
    74 
    75         $this->assertFalse( isset( $wp_registered_sidebars['sidebar-1'] ) );
    76 
    77     }
     87        $sidebar_id = 'wp-unit-test';
     88        register_sidebar( array( 'id' => $sidebar_id ) );
     89        $this->assertArrayHasKey( $sidebar_id, $wp_registered_sidebars );
     90
     91        unregister_sidebar( $sidebar_id );
     92        $this->assertArrayNotHasKey( 'wp-unit-test', $wp_registered_sidebars );
     93    }
     94
     95    /**
     96     * @see WP_Widget_Search::form()
     97     */
     98    function test_wp_widget_search_form() {
     99        $widget = new WP_Widget_Search( 'foo', 'Foo' );
     100        ob_start();
     101        $args = array(
     102            'before_widget' => '<section>',
     103            'after_widget' => "</section>\n",
     104            'before_title' => '<h2>',
     105            'after_title' => "</h2>\n",
     106        );
     107        $instance = array( 'title' => 'Buscar' );
     108        $widget->_set( 2 );
     109        $widget->widget( $args, $instance );
     110        $output = ob_get_clean();
     111        $this->assertNotContains( 'no-options-widget', $output );
     112        $this->assertContains( '<h2>Buscar</h2>', $output );
     113        $this->assertContains( '<section>', $output );
     114        $this->assertContains( '</section>', $output );
     115    }
     116
     117    /**
     118     * @see WP_Widget::form()
     119     */
     120    function test_wp_widget_form() {
     121        $widget = new WP_Widget( 'foo', 'Foo' );
     122        ob_start();
     123        $retval = $widget->form( array() );
     124        $output = ob_get_clean();
     125        $this->assertEquals( 'noform', $retval );
     126        $this->assertContains( 'no-options-widget', $output );
     127    }
     128
     129    /**
     130     * @see WP_Widget::__construct()
     131     */
     132    function test_wp_widget_constructor() {
     133        $id_base = 'foo';
     134        $name = 'Foo';
     135        $foo_widget = new WP_Widget( $id_base, $name );
     136
     137        $this->assertEquals( $id_base, $foo_widget->id_base );
     138        $this->assertEquals( $name, $foo_widget->name );
     139        $this->assertEquals( "widget_{$id_base}", $foo_widget->option_name );
     140        $this->assertArrayHasKey( 'classname', $foo_widget->widget_options );
     141        $this->assertEquals( "widget_{$id_base}", $foo_widget->widget_options['classname'] );
     142        $this->assertArrayHasKey( 'id_base', $foo_widget->control_options );
     143        $this->assertEquals( $id_base, $foo_widget->control_options['id_base'] );
     144
     145        $id_base = 'bar';
     146        $name = 'Bar';
     147        $widget_options = array(
     148            'classname' => 'bar_classname',
     149        );
     150        $control_options = array(
     151            'id_base' => 'bar_id_base',
     152        );
     153        $bar_widget = new WP_Widget( $id_base, $name, $widget_options, $control_options );
     154        $this->assertEquals( $widget_options['classname'], $bar_widget->widget_options['classname'] );
     155        $this->assertEquals( $control_options['id_base'], $bar_widget->control_options['id_base'] );
     156    }
     157
     158    /**
     159     * @see WP_Widget::get_field_name()
     160     */
     161    function test_wp_widget_get_field_name() {
     162        $widget = new WP_Widget( 'foo', 'Foo' );
     163        $widget->_set( 2 );
     164        $this->assertEquals( 'widget-foo[2][title]', $widget->get_field_name( 'title' ) );
     165    }
     166
     167    /**
     168     * @see WP_Widget::get_field_id()
     169     */
     170    function test_wp_widget_get_field_id() {
     171        $widget = new WP_Widget( 'foo', 'Foo' );
     172        $widget->_set( 2 );
     173        $this->assertEquals( 'widget-foo-2-title', $widget->get_field_id( 'title' ) );
     174    }
     175
     176    /**
     177     * @see WP_Widget::_register()
     178     */
     179    function test_wp_widget__register() {
     180        global $wp_registered_widgets;
     181
     182        $settings = get_option( 'widget_search' );
     183        unset( $settings['_multiwidget'] );
     184        $this->assertArrayHasKey( 2, $settings );
     185
     186        $this->assertEmpty( $wp_registered_widgets );
     187        wp_widgets_init();
     188
     189        // Note: We cannot use array_keys() here because $settings could be an ArrayIterator
     190        foreach ( $settings as $widget_number => $instance ) {
     191            $widget_id = "search-$widget_number";
     192            $this->assertArrayHasKey( $widget_id, $wp_registered_widgets );
     193        }
     194    }
     195
     196    // @todo test WP_Widget::display_callback()
     197
     198    /**
     199     * @see WP_Widget::is_preview()
     200     */
     201    function test_wp_widget_is_preview() {
     202        global $wp_customize;
     203
     204        $widget = new WP_Widget( 'foo', 'Foo' );
     205
     206        $this->assertEmpty( $wp_customize );
     207        $this->assertFalse( $widget->is_preview() );
     208
     209        wp_set_current_user( $this->factory->user->create( array( 'role' => 'administrator' ) ) );
     210        require_once ABSPATH . WPINC . '/class-wp-customize-manager.php';
     211        $wp_customize = new WP_Customize_Manager();
     212        $wp_customize->start_previewing_theme();
     213
     214        $this->assertTrue( $widget->is_preview() );
     215    }
     216
     217    // @todo test WP_Widget::update_callback()
     218    // @todo test WP_Widget::form_callback()
     219    // @todo test WP_Widget::_register_one()
     220
     221    /**
     222     * @see WP_Widget::get_settings()
     223     */
     224    function test_wp_widget_get_settings() {
     225        global $wp_registered_widgets;
     226
     227        $option_value = get_option( 'widget_search' );
     228        $this->assertArrayHasKey( '_multiwidget', $option_value );
     229        $this->assertEquals( 1, $option_value['_multiwidget'] );
     230        $this->assertArrayHasKey( 2, $option_value );
     231        $instance = $option_value[2];
     232        $this->assertInternalType( 'array', $instance );
     233        $this->assertArrayHasKey( 'title', $instance );
     234        unset( $option_value['_multiwidget'] );
     235
     236        wp_widgets_init();
     237        $wp_widget_search = $wp_registered_widgets['search-2']['callback'][0];
     238
     239        $settings = $wp_widget_search->get_settings();
     240        // @todo $this->assertArrayNotHasKey( '_multiwidget', $settings ); ?
     241        $this->assertArrayHasKey( 2, $settings );
     242
     243        foreach ( $option_value as $widget_number => $instance ) {
     244            $this->assertEquals( $settings[ $widget_number ], $option_value[ $widget_number ] );
     245        }
     246    }
     247
     248    /**
     249     * @see WP_Widget::save_settings()
     250     */
     251    function test_wp_widget_save_settings() {
     252        global $wp_registered_widgets;
     253
     254        wp_widgets_init();
     255        $wp_widget_search = $wp_registered_widgets['search-2']['callback'][0];
     256
     257        $settings = $wp_widget_search->get_settings();
     258        $overridden_title = 'Unit Tested';
     259
     260        /*
     261         * Note that if a plugin is filtering $settings to be an ArrayIterator,
     262         * then doing this:
     263         *     $settings[2]['title'] = $overridden_title;
     264         * Will fail with this:
     265         * > Indirect modification of overloaded element of X has no effect.
     266         * So this is why the value must be obtained.
     267         */
     268        $instance = $settings[2];
     269        $instance['title'] = $overridden_title;
     270        $settings[2] = $instance;
     271
     272        $wp_widget_search->save_settings( $settings );
     273
     274        $option_value = get_option( $wp_widget_search->option_name );
     275        $this->assertArrayHasKey( '_multiwidget', $option_value );
     276        $this->assertEquals( $overridden_title, $option_value[2]['title'] );
     277    }
     278
     279    /**
     280     * @see WP_Widget::save_settings()
     281     */
     282    function test_wp_widget_save_settings_delete() {
     283        global $wp_registered_widgets;
     284
     285        wp_widgets_init();
     286        $wp_widget_search = $wp_registered_widgets['search-2']['callback'][0];
     287
     288        $settings = $wp_widget_search->get_settings();
     289        $this->assertArrayHasKey( 2, $settings );
     290        unset( $settings[2] );
     291        $wp_widget_search->save_settings( $settings );
     292        $option_value = get_option( $wp_widget_search->option_name );
     293        $this->assertArrayNotHasKey( 2, $option_value );
     294    }
     295
    78296}
Note: See TracChangeset for help on using the changeset viewer.