diff --git a/src/wp-includes/widgets.php b/src/wp-includes/widgets.php index aed3cd8d5a..d41ce31712 100644 --- a/src/wp-includes/widgets.php +++ b/src/wp-includes/widgets.php @@ -214,20 +214,24 @@ class WP_Widget { $settings = $this->get_settings(); $empty = true; - if ( is_array($settings) ) { - foreach ( array_keys($settings) as $number ) { - if ( is_numeric($number) ) { - $this->_set($number); - $this->_register_one($number); + // When $settings is an array-like object, get an intrinsic array for use with array_keys(). + if ( $settings instanceof ArrayObject || $settings instanceof ArrayIterator ) { + $settings = $settings->getArrayCopy(); + } + + if ( is_array( $settings ) ) { + foreach ( array_keys( $settings ) as $number ) { + if ( is_numeric( $number ) ) { + $this->_set( $number ); + $this->_register_one( $number ); $empty = false; } } } if ( $empty ) { - // If there are none, we register the widget's existence with a - // generic template - $this->_set(1); + // If there are none, we register the widget's existence with a generic template. + $this->_set( 1 ); $this->_register_one(); } } @@ -294,15 +298,16 @@ class WP_Widget { * } */ public function display_callback( $args, $widget_args = 1 ) { - if ( is_numeric($widget_args) ) + if ( is_numeric( $widget_args ) ) { $widget_args = array( 'number' => $widget_args ); + } $widget_args = wp_parse_args( $widget_args, array( 'number' => -1 ) ); $this->_set( $widget_args['number'] ); - $instance = $this->get_settings(); + $instances = $this->get_settings(); - if ( array_key_exists( $this->number, $instance ) ) { - $instance = $instance[$this->number]; + if ( isset( $instances[ $this->number ] ) ) { + $instance = $instances[ $this->number ]; /** * Filter the settings for a particular widget instance. @@ -424,6 +429,7 @@ class WP_Widget { * @access public * * @param int|array $widget_args Widget instance number or array of widget arguments. + * @return string|null */ public function form_callback( $widget_args = 1 ) { if ( is_numeric($widget_args) ) @@ -516,20 +522,22 @@ class WP_Widget { */ public function get_settings() { - $settings = get_option($this->option_name); + $settings = get_option( $this->option_name ); - if ( false === $settings && isset($this->alt_option_name) ) - $settings = get_option($this->alt_option_name); - - if ( !is_array($settings) ) - $settings = array(); - - if ( !empty($settings) && !array_key_exists('_multiwidget', $settings) ) { - // old format, convert if single widget - $settings = wp_convert_widget_settings($this->id_base, $this->option_name, $settings); + if ( false === $settings && isset( $this->alt_option_name ) ) { + $settings = get_option( $this->alt_option_name ); } - unset($settings['_multiwidget'], $settings['__i__']); + if ( ! is_array( $settings ) && ! ( $settings instanceof ArrayObject || $settings instanceof ArrayIterator ) ) { + $settings = array(); + } + + if ( ! empty( $settings ) && ! isset( $settings['_multiwidget'] ) ) { + // Old format, convert if single widget. + $settings = wp_convert_widget_settings( $this->id_base, $this->option_name, $settings ); + } + + unset( $settings['_multiwidget'], $settings['__i__'] ); return $settings; } } diff --git a/tests/phpunit/tests/widgets.php b/tests/phpunit/tests/widgets.php index f84ba3e953..443d3f3d7c 100644 --- a/tests/phpunit/tests/widgets.php +++ b/tests/phpunit/tests/widgets.php @@ -1,33 +1,48 @@ assertTrue( isset( $wp_widget_factory->widgets['WP_Widget_Search'] ) ); + $wp_registered_sidebars = array(); + $wp_registered_widgets = array(); + $wp_registered_widget_controls = array(); + $wp_registered_widget_updates = array(); + $wp_widget_factory->widgets = array(); + parent::clean_up_global_scope(); } - function test_unregister_widget_core_widget() { - - global $wp_widget_factory; - - unregister_widget( 'WP_Widget_Search' ); - - $this->assertFalse( isset( $wp_widget_factory->widgets['WP_Widget_Search'] ) ); - + function tearDown() { + global $wp_customize; + $wp_customize = null; + parent::tearDown(); } + /** + * @see register_widget() + * @see unregister_widget() + */ + function test_register_and_unregister_widget_core_widget() { + global $wp_widget_factory; + + $widget_class = 'WP_Widget_Search'; + register_widget( $widget_class ); + $this->assertArrayHasKey( $widget_class, $wp_widget_factory->widgets ); + + unregister_widget( $widget_class ); + $this->assertArrayNotHasKey( $widget_class, $wp_widget_factory->widgets ); + } + + /** + * @see register_sidebars() + */ function test_register_sidebars_single() { global $wp_registered_sidebars; @@ -38,41 +53,244 @@ class Tests_Widgets extends WP_UnitTestCase { } + /** + * @see register_sidebars() + */ function test_register_sidebars_multiple() { global $wp_registered_sidebars; + $result = array(); $num = 3; $id_base = 'WP Unit Test'; register_sidebars( $num, array( 'name' => $id_base . ' %d' ) ); $names = wp_list_pluck( $wp_registered_sidebars, 'name' ); for ( $i = 1; $i <= $num; $i++ ) { - if ( in_array( "$id_base $i", $names ) ) + if ( in_array( "$id_base $i", $names ) ) { $result[] = true; + } } $this->assertEquals( $num, count( $result ) ); } - function test_register_sidebar() { + /** + * @see register_sidebar + * @see unregister_sidebar + */ + function test_register_and_unregister_sidebar() { global $wp_registered_sidebars; - register_sidebar( array( 'id' => 'wp-unit-test' ) ); - - $this->assertTrue( isset( $wp_registered_sidebars['wp-unit-test'] ) ); + $sidebar_id = 'wp-unit-test'; + register_sidebar( array( 'id' => $sidebar_id ) ); + $this->assertArrayHasKey( $sidebar_id, $wp_registered_sidebars ); + unregister_sidebar( $sidebar_id ); + $this->assertArrayNotHasKey( 'wp-unit-test', $wp_registered_sidebars ); } - function test_unregister_sidebar() { - - global $wp_registered_sidebars; - - unregister_sidebar( 'sidebar-1' ); - - $this->assertFalse( isset( $wp_registered_sidebars['sidebar-1'] ) ); - + /** + * @see WP_Widget_Search::form() + */ + function test_wp_widget_search_form() { + $widget = new WP_Widget_Search( 'foo', 'Foo' ); + ob_start(); + $args = array( + 'before_widget' => '
', + 'after_widget' => "
\n", + 'before_title' => '

', + 'after_title' => "

\n", + ); + $instance = array( 'title' => 'Buscar' ); + $widget->_set( 2 ); + $widget->widget( $args, $instance ); + $output = ob_get_clean(); + $this->assertNotContains( 'no-options-widget', $output ); + $this->assertContains( '

Buscar

', $output ); + $this->assertContains( '
', $output ); + $this->assertContains( '
', $output ); } + + /** + * @see WP_Widget::form() + */ + function test_wp_widget_form() { + $widget = new WP_Widget( 'foo', 'Foo' ); + ob_start(); + $retval = $widget->form( array() ); + $output = ob_get_clean(); + $this->assertEquals( 'noform', $retval ); + $this->assertContains( 'no-options-widget', $output ); + } + + /** + * @see WP_Widget::__construct() + */ + function test_wp_widget_constructor() { + $id_base = 'foo'; + $name = 'Foo'; + $foo_widget = new WP_Widget( $id_base, $name ); + + $this->assertEquals( $id_base, $foo_widget->id_base ); + $this->assertEquals( $name, $foo_widget->name ); + $this->assertEquals( "widget_{$id_base}", $foo_widget->option_name ); + $this->assertArrayHasKey( 'classname', $foo_widget->widget_options ); + $this->assertEquals( "widget_{$id_base}", $foo_widget->widget_options['classname'] ); + $this->assertArrayHasKey( 'id_base', $foo_widget->control_options ); + $this->assertEquals( $id_base, $foo_widget->control_options['id_base'] ); + + $id_base = 'bar'; + $name = 'Bar'; + $widget_options = array( + 'classname' => 'bar_classname', + ); + $control_options = array( + 'id_base' => 'bar_id_base', + ); + $bar_widget = new WP_Widget( $id_base, $name, $widget_options, $control_options ); + $this->assertEquals( $widget_options['classname'], $bar_widget->widget_options['classname'] ); + $this->assertEquals( $control_options['id_base'], $bar_widget->control_options['id_base'] ); + } + + /** + * @see WP_Widget::get_field_name() + */ + function test_wp_widget_get_field_name() { + $widget = new WP_Widget( 'foo', 'Foo' ); + $widget->_set( 2 ); + $this->assertEquals( 'widget-foo[2][title]', $widget->get_field_name( 'title' ) ); + } + + /** + * @see WP_Widget::get_field_id() + */ + function test_wp_widget_get_field_id() { + $widget = new WP_Widget( 'foo', 'Foo' ); + $widget->_set( 2 ); + $this->assertEquals( 'widget-foo-2-title', $widget->get_field_id( 'title' ) ); + } + + /** + * @see WP_Widget::_register() + */ + function test_wp_widget__register() { + global $wp_registered_widgets; + + $settings = get_option( 'widget_search' ); + unset( $settings['_multiwidget'] ); + $this->assertArrayHasKey( 2, $settings ); + + $this->assertEmpty( $wp_registered_widgets ); + wp_widgets_init(); + + // Note: We cannot use array_keys() here because $settings could be an ArrayIterator + foreach ( $settings as $widget_number => $instance ) { + $widget_id = "search-$widget_number"; + $this->assertArrayHasKey( $widget_id, $wp_registered_widgets ); + } + } + + // @todo test WP_Widget::display_callback() + + /** + * @see WP_Widget::is_preview() + */ + function test_wp_widget_is_preview() { + global $wp_customize; + + $widget = new WP_Widget( 'foo', 'Foo' ); + + $this->assertEmpty( $wp_customize ); + $this->assertFalse( $widget->is_preview() ); + + wp_set_current_user( $this->factory->user->create( array( 'role' => 'administrator' ) ) ); + require_once ABSPATH . WPINC . '/class-wp-customize-manager.php'; + $wp_customize = new WP_Customize_Manager(); + $wp_customize->start_previewing_theme(); + + $this->assertTrue( $widget->is_preview() ); + } + + // @todo test WP_Widget::update_callback() + // @todo test WP_Widget::form_callback() + // @todo test WP_Widget::_register_one() + + /** + * @see WP_Widget::get_settings() + */ + function test_wp_widget_get_settings() { + global $wp_registered_widgets; + + $option_value = get_option( 'widget_search' ); + $this->assertArrayHasKey( '_multiwidget', $option_value ); + $this->assertEquals( 1, $option_value['_multiwidget'] ); + $this->assertArrayHasKey( 2, $option_value ); + $instance = $option_value[2]; + $this->assertInternalType( 'array', $instance ); + $this->assertArrayHasKey( 'title', $instance ); + unset( $option_value['_multiwidget'] ); + + wp_widgets_init(); + $wp_widget_search = $wp_registered_widgets['search-2']['callback'][0]; + + $settings = $wp_widget_search->get_settings(); + // @todo $this->assertArrayNotHasKey( '_multiwidget', $settings ); ? + $this->assertArrayHasKey( 2, $settings ); + + foreach ( $option_value as $widget_number => $instance ) { + $this->assertEquals( $settings[ $widget_number ], $option_value[ $widget_number ] ); + } + } + + /** + * @see WP_Widget::save_settings() + */ + function test_wp_widget_save_settings() { + global $wp_registered_widgets; + + wp_widgets_init(); + $wp_widget_search = $wp_registered_widgets['search-2']['callback'][0]; + + $settings = $wp_widget_search->get_settings(); + $overridden_title = 'Unit Tested'; + + /* + * Note that if a plugin is filtering $settings to be an ArrayIterator, + * then doing this: + * $settings[2]['title'] = $overridden_title; + * Will fail with this: + * > Indirect modification of overloaded element of X has no effect. + * So this is why the value must be obtained. + */ + $instance = $settings[2]; + $instance['title'] = $overridden_title; + $settings[2] = $instance; + + $wp_widget_search->save_settings( $settings ); + + $option_value = get_option( $wp_widget_search->option_name ); + $this->assertArrayHasKey( '_multiwidget', $option_value ); + $this->assertEquals( $overridden_title, $option_value[2]['title'] ); + } + + /** + * @see WP_Widget::save_settings() + */ + function test_wp_widget_save_settings_delete() { + global $wp_registered_widgets; + + wp_widgets_init(); + $wp_widget_search = $wp_registered_widgets['search-2']['callback'][0]; + + $settings = $wp_widget_search->get_settings(); + $this->assertArrayHasKey( 2, $settings ); + unset( $settings[2] ); + $wp_widget_search->save_settings( $settings ); + $option_value = get_option( $wp_widget_search->option_name ); + $this->assertArrayNotHasKey( 2, $option_value ); + } + }