Widgets: Make sure WP_Widget constructor creates a correct id_base value for a namespaced widget class.

The `id_base` value is used for the widget's `id` and `class` attributes and also for the option name which stores the widget settings (unless the widget specifies a custom `option_name` value).

With this change, any backslashes in the `id_base` for a namespaced widget class are converted to hyphens, making it easier to style the output or target the widget with JavaScript.

This also avoids a `preg_match(): Compilation failed` PHP warning from `next_widget_id_number()` on the Widgets screen, previously caused by unescaped backslashes.

Props Mte90, hermpheus, rogerlos, welcher, SergeyBiryukov.
Fixes #44098.

git-svn-id: https://develop.svn.wordpress.org/trunk@50953 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
Sergey Biryukov 2021-05-23 08:50:44 +00:00
parent cc6d56f503
commit c4fd562663
3 changed files with 69 additions and 5 deletions

View File

@ -160,7 +160,14 @@ class WP_Widget {
* information on accepted arguments. Default empty array.
*/
public function __construct( $id_base, $name, $widget_options = array(), $control_options = array() ) {
$this->id_base = empty( $id_base ) ? preg_replace( '/(wp_)?widget_/', '', strtolower( get_class( $this ) ) ) : strtolower( $id_base );
if ( ! empty( $id_base ) ) {
$id_base = strtolower( $id_base );
} else {
$id_base = preg_replace( '/(wp_)?widget_/', '', strtolower( get_class( $this ) ) );
$id_base = str_replace( '\\', '-', $id_base );
}
$this->id_base = $id_base;
$this->name = $name;
$this->option_name = 'widget_' . $this->id_base;
$this->widget_options = wp_parse_args(

View File

@ -0,0 +1,14 @@
<?php
namespace Test\Sub\Sub {
class Namespaced_Widget extends \WP_Widget {
}
}
namespace {
class Non_Namespaced_Widget extends \WP_Widget {
}
}

View File

@ -23,6 +23,7 @@ class Tests_Widgets extends WP_UnitTestCase {
function tearDown() {
global $wp_customize;
$wp_customize = null;
parent::tearDown();
@ -36,6 +37,7 @@ class Tests_Widgets extends WP_UnitTestCase {
global $wp_widget_factory;
$widget_class = 'WP_Widget_Search';
register_widget( $widget_class );
$this->assertArrayHasKey( $widget_class, $wp_widget_factory->widgets );
@ -52,6 +54,7 @@ class Tests_Widgets extends WP_UnitTestCase {
*/
function test_register_and_unregister_widget_instance() {
global $wp_widget_factory, $wp_registered_widgets;
$this->assertEmpty( $wp_widget_factory->widgets );
$this->assertEmpty( $wp_registered_widgets );
@ -127,7 +130,6 @@ class Tests_Widgets extends WP_UnitTestCase {
* @group sidebar
*/
function test_register_sidebars_single() {
global $wp_registered_sidebars;
register_sidebars( 1, array( 'id' => 'wp-unit-test' ) );
@ -140,7 +142,6 @@ class Tests_Widgets extends WP_UnitTestCase {
* @group sidebar
*/
function test_register_sidebars_multiple() {
global $wp_registered_sidebars;
$result = array();
@ -199,7 +200,6 @@ class Tests_Widgets extends WP_UnitTestCase {
* @group sidebar
*/
function test_register_sidebar_with_string_id() {
global $wp_registered_sidebars;
$sidebar_id = 'wp-unit-test';
@ -425,6 +425,50 @@ class Tests_Widgets extends WP_UnitTestCase {
$this->assertSame( $control_options['id_base'], $bar_widget->control_options['id_base'] );
}
/**
* @ticket 44098
* @see WP_Widget::__construct()
* @dataProvider data_wp_widget_id_base
*/
function test_wp_widget_id_base( $expected, $widget_class ) {
require_once DIR_TESTDATA . '/widgets/custom-widget-classes.php';
$widget = new $widget_class( '', 'Foo' );
$this->assertSame( $expected, $widget->id_base );
}
/**
* Data provider.
*
* Passes the expected `id_base` value and the class name.
*
* @since 5.8.0
*
* @return array {
* @type array {
* @type string $expected The expected `id_base` value to be returned.
* @type string $widget_class The widget class name for creating an instance.
* }
* }
*/
function data_wp_widget_id_base() {
return array(
array(
'search',
'WP_Widget_Search',
),
array(
'test-sub-sub-namespaced_widget',
'Test\Sub\Sub\Namespaced_Widget',
),
array(
'non_namespaced_widget',
'Non_Namespaced_Widget',
),
);
}
/**
* @see WP_Widget::get_field_name()
* @dataProvider data_wp_widget_get_field_name
@ -450,7 +494,6 @@ class Tests_Widgets extends WP_UnitTestCase {
* }
*/
function data_wp_widget_get_field_name() {
return array(
array(
'widget-foo[2][title]',