Issues (4967)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

src/wp-includes/widgets.php (14 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * Core Widgets API
4
 *
5
 * This API is used for creating dynamic sidebar without hardcoding functionality into
6
 * themes
7
 *
8
 * Includes both internal WordPress routines and theme-use routines.
9
 *
10
 * This functionality was found in a plugin before the WordPress 2.2 release, which
11
 * included it in the core from that point on.
12
 *
13
 * @link https://codex.wordpress.org/Plugins/WordPress_Widgets WordPress Widgets
14
 * @link https://codex.wordpress.org/Plugins/WordPress_Widgets_Api Widgets API
15
 *
16
 * @package WordPress
17
 * @subpackage Widgets
18
 * @since 2.2.0
19
 */
20
21
//
22
// Global Variables
23
//
24
25
/** @ignore */
26
global $wp_registered_sidebars, $wp_registered_widgets, $wp_registered_widget_controls, $wp_registered_widget_updates;
27
28
/**
29
 * Stores the sidebars, since many themes can have more than one.
30
 *
31
 * @global array $wp_registered_sidebars
32
 * @since 2.2.0
33
 */
34
$wp_registered_sidebars = array();
35
36
/**
37
 * Stores the registered widgets.
38
 *
39
 * @global array $wp_registered_widgets
40
 * @since 2.2.0
41
 */
42
$wp_registered_widgets = array();
43
44
/**
45
 * Stores the registered widget control (options).
46
 *
47
 * @global array $wp_registered_widget_controls
48
 * @since 2.2.0
49
 */
50
$wp_registered_widget_controls = array();
51
/**
52
 * @global array $wp_registered_widget_updates
53
 */
54
$wp_registered_widget_updates = array();
55
56
/**
57
 * Private
58
 *
59
 * @global array $_wp_sidebars_widgets
60
 */
61
$_wp_sidebars_widgets = array();
62
63
/**
64
 * Private
65
 *
66
 * @global array $_wp_deprecated_widgets_callbacks
67
 */
68
$GLOBALS['_wp_deprecated_widgets_callbacks'] = array(
69
	'wp_widget_pages',
70
	'wp_widget_pages_control',
71
	'wp_widget_calendar',
72
	'wp_widget_calendar_control',
73
	'wp_widget_archives',
74
	'wp_widget_archives_control',
75
	'wp_widget_links',
76
	'wp_widget_meta',
77
	'wp_widget_meta_control',
78
	'wp_widget_search',
79
	'wp_widget_recent_entries',
80
	'wp_widget_recent_entries_control',
81
	'wp_widget_tag_cloud',
82
	'wp_widget_tag_cloud_control',
83
	'wp_widget_categories',
84
	'wp_widget_categories_control',
85
	'wp_widget_text',
86
	'wp_widget_text_control',
87
	'wp_widget_rss',
88
	'wp_widget_rss_control',
89
	'wp_widget_recent_comments',
90
	'wp_widget_recent_comments_control'
91
);
92
93
//
94
// Template tags & API functions
95
//
96
97
/**
98
 * Register a widget
99
 *
100
 * Registers a WP_Widget widget
101
 *
102
 * @since 2.8.0
103
 *
104
 * @see WP_Widget
105
 *
106
 * @global WP_Widget_Factory $wp_widget_factory
107
 *
108
 * @param string $widget_class The name of a class that extends WP_Widget
109
 */
110
function register_widget($widget_class) {
111
	global $wp_widget_factory;
112
113
	$wp_widget_factory->register($widget_class);
114
}
115
116
/**
117
 * Unregisters a widget.
118
 *
119
 * Unregisters a WP_Widget widget. Useful for un-registering default widgets.
120
 * Run within a function hooked to the {@see 'widgets_init'} action.
121
 *
122
 * @since 2.8.0
123
 *
124
 * @see WP_Widget
125
 *
126
 * @global WP_Widget_Factory $wp_widget_factory
127
 *
128
 * @param string $widget_class The name of a class that extends WP_Widget.
129
 */
130
function unregister_widget($widget_class) {
131
	global $wp_widget_factory;
132
133
	$wp_widget_factory->unregister($widget_class);
134
}
135
136
/**
137
 * Creates multiple sidebars.
138
 *
139
 * If you wanted to quickly create multiple sidebars for a theme or internally.
140
 * This function will allow you to do so. If you don't pass the 'name' and/or
141
 * 'id' in `$args`, then they will be built for you.
142
 *
143
 * @since 2.2.0
144
 *
145
 * @see register_sidebar() The second parameter is documented by register_sidebar() and is the same here.
146
 *
147
 * @global array $wp_registered_sidebars
148
 *
149
 * @param int          $number Optional. Number of sidebars to create. Default 1.
150
 * @param array|string $args {
151
 *     Optional. Array or string of arguments for building a sidebar.
152
 *
153
 *     @type string $id   The base string of the unique identifier for each sidebar. If provided, and multiple
154
 *                        sidebars are being defined, the id will have "-2" appended, and so on.
155
 *                        Default 'sidebar-' followed by the number the sidebar creation is currently at.
156
 *     @type string $name The name or title for the sidebars displayed in the admin dashboard. If registering
157
 *                        more than one sidebar, include '%d' in the string as a placeholder for the uniquely
158
 *                        assigned number for each sidebar.
159
 *                        Default 'Sidebar' for the first sidebar, otherwise 'Sidebar %d'.
160
 * }
161
 */
162
function register_sidebars( $number = 1, $args = array() ) {
163
	global $wp_registered_sidebars;
164
	$number = (int) $number;
165
166
	if ( is_string($args) )
167
		parse_str($args, $args);
168
169
	for ( $i = 1; $i <= $number; $i++ ) {
170
		$_args = $args;
171
172
		if ( $number > 1 )
173
			$_args['name'] = isset($args['name']) ? sprintf($args['name'], $i) : sprintf(__('Sidebar %d'), $i);
174
		else
175
			$_args['name'] = isset($args['name']) ? $args['name'] : __('Sidebar');
176
177
		// Custom specified ID's are suffixed if they exist already.
178
		// Automatically generated sidebar names need to be suffixed regardless starting at -0
179
		if ( isset($args['id']) ) {
180
			$_args['id'] = $args['id'];
181
			$n = 2; // Start at -2 for conflicting custom ID's
182
			while ( is_registered_sidebar( $_args['id'] ) ) {
183
				$_args['id'] = $args['id'] . '-' . $n++;
184
			}
185
		} else {
186
			$n = count( $wp_registered_sidebars );
187
			do {
188
				$_args['id'] = 'sidebar-' . ++$n;
189
			} while ( is_registered_sidebar( $_args['id'] ) );
190
		}
191
		register_sidebar($_args);
192
	}
193
}
194
195
/**
196
 * Builds the definition for a single sidebar and returns the ID.
197
 *
198
 * Accepts either a string or an array and then parses that against a set
199
 * of default arguments for the new sidebar. WordPress will automatically
200
 * generate a sidebar ID and name based on the current number of registered
201
 * sidebars if those arguments are not included.
202
 *
203
 * When allowing for automatic generation of the name and ID parameters, keep
204
 * in mind that the incrementor for your sidebar can change over time depending
205
 * on what other plugins and themes are installed.
206
 *
207
 * If theme support for 'widgets' has not yet been added when this function is
208
 * called, it will be automatically enabled through the use of add_theme_support()
209
 *
210
 * @since 2.2.0
211
 *
212
 * @global array $wp_registered_sidebars Stores the new sidebar in this array by sidebar ID.
213
 *
214
 * @param array|string $args {
215
 *     Optional. Array or string of arguments for the sidebar being registered.
216
 *
217
 *     @type string $name          The name or title of the sidebar displayed in the Widgets
218
 *                                 interface. Default 'Sidebar $instance'.
219
 *     @type string $id            The unique identifier by which the sidebar will be called.
220
 *                                 Default 'sidebar-$instance'.
221
 *     @type string $description   Description of the sidebar, displayed in the Widgets interface.
222
 *                                 Default empty string.
223
 *     @type string $class         Extra CSS class to assign to the sidebar in the Widgets interface.
224
 *                                 Default empty.
225
 *     @type string $before_widget HTML content to prepend to each widget's HTML output when
226
 *                                 assigned to this sidebar. Default is an opening list item element.
227
 *     @type string $after_widget  HTML content to append to each widget's HTML output when
228
 *                                 assigned to this sidebar. Default is a closing list item element.
229
 *     @type string $before_title  HTML content to prepend to the sidebar title when displayed.
230
 *                                 Default is an opening h2 element.
231
 *     @type string $after_title   HTML content to append to the sidebar title when displayed.
232
 *                                 Default is a closing h2 element.
233
 * }
234
 * @return string Sidebar ID added to $wp_registered_sidebars global.
235
 */
236
function register_sidebar($args = array()) {
237
	global $wp_registered_sidebars;
238
239
	$i = count($wp_registered_sidebars) + 1;
240
241
	$id_is_empty = empty( $args['id'] );
242
243
	$defaults = array(
244
		'name' => sprintf(__('Sidebar %d'), $i ),
245
		'id' => "sidebar-$i",
246
		'description' => '',
247
		'class' => '',
248
		'before_widget' => '<li id="%1$s" class="widget %2$s">',
249
		'after_widget' => "</li>\n",
250
		'before_title' => '<h2 class="widgettitle">',
251
		'after_title' => "</h2>\n",
252
	);
253
254
	$sidebar = wp_parse_args( $args, $defaults );
255
256
	if ( $id_is_empty ) {
257
		/* translators: 1: the id argument, 2: sidebar name, 3: recommended id value */
258
		_doing_it_wrong( __FUNCTION__, sprintf( __( 'No %1$s was set in the arguments array for the "%2$s" sidebar. Defaulting to "%3$s". Manually set the %1$s to "%3$s" to silence this notice and keep existing sidebar content.' ), '<code>id</code>', $sidebar['name'], $sidebar['id'] ), '4.2.0' );
259
	}
260
261
	$wp_registered_sidebars[$sidebar['id']] = $sidebar;
262
263
	add_theme_support('widgets');
264
265
	/**
266
	 * Fires once a sidebar has been registered.
267
	 *
268
	 * @since 3.0.0
269
	 *
270
	 * @param array $sidebar Parsed arguments for the registered sidebar.
271
	 */
272
	do_action( 'register_sidebar', $sidebar );
273
274
	return $sidebar['id'];
275
}
276
277
/**
278
 * Removes a sidebar from the list.
279
 *
280
 * @since 2.2.0
281
 *
282
 * @global array $wp_registered_sidebars Stores the new sidebar in this array by sidebar ID.
283
 *
284
 * @param string|int $sidebar_id The ID of the sidebar when it was registered.
285
 */
286
function unregister_sidebar( $sidebar_id ) {
287
	global $wp_registered_sidebars;
288
289
	unset( $wp_registered_sidebars[ $sidebar_id ] );
290
}
291
292
/**
293
 * Checks if a sidebar is registered.
294
 *
295
 * @since 4.4.0
296
 *
297
 * @global array $wp_registered_sidebars Registered sidebars.
298
 *
299
 * @param string|int $sidebar_id The ID of the sidebar when it was registered.
300
 * @return bool True if the sidebar is registered, false otherwise.
301
 */
302
function is_registered_sidebar( $sidebar_id ) {
303
	global $wp_registered_sidebars;
304
305
	return isset( $wp_registered_sidebars[ $sidebar_id ] );
306
}
307
308
/**
309
 * Register an instance of a widget.
310
 *
311
 * The default widget option is 'classname' that can be overridden.
312
 *
313
 * The function can also be used to un-register widgets when `$output_callback`
314
 * parameter is an empty string.
315
 *
316
 * @since 2.2.0
317
 *
318
 * @global array $wp_registered_widgets       Uses stored registered widgets.
319
 * @global array $wp_register_widget_defaults Retrieves widget defaults.
320
 * @global array $wp_registered_widget_updates
321
 * @global array $_wp_deprecated_widgets_callbacks
322
 *
323
 * @param int|string $id              Widget ID.
324
 * @param string     $name            Widget display title.
325
 * @param callable   $output_callback Run when widget is called.
326
 * @param array      $options {
327
 *     Optional. An array of supplementary widget options for the instance.
328
 *
329
 *     @type string $classname   Class name for the widget's HTML container. Default is a shortened
330
 *                               version of the output callback name.
331
 *     @type string $description Widget description for display in the widget administration
332
 *                               panel and/or theme.
333
 * }
334
 */
335
function wp_register_sidebar_widget( $id, $name, $output_callback, $options = array() ) {
336
	global $wp_registered_widgets, $wp_registered_widget_controls, $wp_registered_widget_updates, $_wp_deprecated_widgets_callbacks;
337
338
	$id = strtolower($id);
339
340
	if ( empty($output_callback) ) {
341
		unset($wp_registered_widgets[$id]);
342
		return;
343
	}
344
345
	$id_base = _get_widget_id_base($id);
346
	if ( in_array($output_callback, $_wp_deprecated_widgets_callbacks, true) && !is_callable($output_callback) ) {
347
		unset( $wp_registered_widget_controls[ $id ] );
348
		unset( $wp_registered_widget_updates[ $id_base ] );
349
		return;
350
	}
351
352
	$defaults = array('classname' => $output_callback);
353
	$options = wp_parse_args($options, $defaults);
354
	$widget = array(
355
		'name' => $name,
356
		'id' => $id,
357
		'callback' => $output_callback,
358
		'params' => array_slice(func_get_args(), 4)
359
	);
360
	$widget = array_merge($widget, $options);
361
362
	if ( is_callable($output_callback) && ( !isset($wp_registered_widgets[$id]) || did_action( 'widgets_init' ) ) ) {
363
364
		/**
365
		 * Fires once for each registered widget.
366
		 *
367
		 * @since 3.0.0
368
		 *
369
		 * @param array $widget An array of default widget arguments.
370
		 */
371
		do_action( 'wp_register_sidebar_widget', $widget );
372
		$wp_registered_widgets[$id] = $widget;
373
	}
374
}
375
376
/**
377
 * Retrieve description for widget.
378
 *
379
 * When registering widgets, the options can also include 'description' that
380
 * describes the widget for display on the widget administration panel or
381
 * in the theme.
382
 *
383
 * @since 2.5.0
384
 *
385
 * @global array $wp_registered_widgets
386
 *
387
 * @param int|string $id Widget ID.
388
 * @return string|void Widget description, if available.
389
 */
390 View Code Duplication
function wp_widget_description( $id ) {
0 ignored issues
show
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
391
	if ( !is_scalar($id) )
392
		return;
393
394
	global $wp_registered_widgets;
395
396
	if ( isset($wp_registered_widgets[$id]['description']) )
397
		return esc_html( $wp_registered_widgets[$id]['description'] );
398
}
399
400
/**
401
 * Retrieve description for a sidebar.
402
 *
403
 * When registering sidebars a 'description' parameter can be included that
404
 * describes the sidebar for display on the widget administration panel.
405
 *
406
 * @since 2.9.0
407
 *
408
 * @global array $wp_registered_sidebars
409
 *
410
 * @param string $id sidebar ID.
411
 * @return string|void Sidebar description, if available.
412
 */
413 View Code Duplication
function wp_sidebar_description( $id ) {
0 ignored issues
show
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
414
	if ( !is_scalar($id) )
415
		return;
416
417
	global $wp_registered_sidebars;
418
419
	if ( isset($wp_registered_sidebars[$id]['description']) )
420
		return esc_html( $wp_registered_sidebars[$id]['description'] );
421
}
422
423
/**
424
 * Remove widget from sidebar.
425
 *
426
 * @since 2.2.0
427
 *
428
 * @param int|string $id Widget ID.
429
 */
430
function wp_unregister_sidebar_widget($id) {
431
432
	/**
433
	 * Fires just before a widget is removed from a sidebar.
434
	 *
435
	 * @since 3.0.0
436
	 *
437
	 * @param int $id The widget ID.
438
	 */
439
	do_action( 'wp_unregister_sidebar_widget', $id );
440
441
	wp_register_sidebar_widget($id, '', '');
442
	wp_unregister_widget_control($id);
443
}
444
445
/**
446
 * Registers widget control callback for customizing options.
447
 *
448
 * @since 2.2.0
449
 *
450
 * @todo `$params` parameter?
451
 *
452
 * @global array $wp_registered_widget_controls
453
 * @global array $wp_registered_widget_updates
454
 * @global array $wp_registered_widgets
455
 * @global array $_wp_deprecated_widgets_callbacks
456
 *
457
 * @param int|string   $id               Sidebar ID.
458
 * @param string       $name             Sidebar display name.
459
 * @param callable     $control_callback Run when sidebar is displayed.
460
 * @param array $options {
461
 *     Optional. Array or string of control options. Default empty array.
462
 *
463
 *     @type int        $height  Never used. Default 200.
464
 *     @type int        $width   Width of the fully expanded control form (but try hard to use the default width).
465
 *                               Default 250.
466
 *     @type int|string $id_base Required for multi-widgets, i.e widgets that allow multiple instances such as the
467
 *                               text widget. The widget id will end up looking like `{$id_base}-{$unique_number}`.
468
 * }
469
 */
470
function wp_register_widget_control( $id, $name, $control_callback, $options = array() ) {
471
	global $wp_registered_widget_controls, $wp_registered_widget_updates, $wp_registered_widgets, $_wp_deprecated_widgets_callbacks;
472
473
	$id = strtolower($id);
474
	$id_base = _get_widget_id_base($id);
475
476
	if ( empty($control_callback) ) {
477
		unset($wp_registered_widget_controls[$id]);
478
		unset($wp_registered_widget_updates[$id_base]);
479
		return;
480
	}
481
482
	if ( in_array($control_callback, $_wp_deprecated_widgets_callbacks, true) && !is_callable($control_callback) ) {
483
		unset( $wp_registered_widgets[ $id ] );
484
		return;
485
	}
486
487
	if ( isset($wp_registered_widget_controls[$id]) && !did_action( 'widgets_init' ) )
488
		return;
489
490
	$defaults = array('width' => 250, 'height' => 200 ); // height is never used
491
	$options = wp_parse_args($options, $defaults);
492
	$options['width'] = (int) $options['width'];
493
	$options['height'] = (int) $options['height'];
494
495
	$widget = array(
496
		'name' => $name,
497
		'id' => $id,
498
		'callback' => $control_callback,
499
		'params' => array_slice(func_get_args(), 4)
500
	);
501
	$widget = array_merge($widget, $options);
502
503
	$wp_registered_widget_controls[$id] = $widget;
504
505
	if ( isset($wp_registered_widget_updates[$id_base]) )
506
		return;
507
508
	if ( isset($widget['params'][0]['number']) )
509
		$widget['params'][0]['number'] = -1;
510
511
	unset($widget['width'], $widget['height'], $widget['name'], $widget['id']);
512
	$wp_registered_widget_updates[$id_base] = $widget;
513
}
514
515
/**
516
 * Registers the update callback for a widget.
517
 *
518
 * @since 2.8.0
519
 *
520
 * @global array $wp_registered_widget_updates
521
 *
522
 * @param string   $id_base         The base ID of a widget created by extending WP_Widget.
523
 * @param callable $update_callback Update callback method for the widget.
524
 * @param array    $options         Optional. Widget control options. See wp_register_widget_control().
525
 *                                  Default empty array.
526
 */
527
function _register_widget_update_callback( $id_base, $update_callback, $options = array() ) {
528
	global $wp_registered_widget_updates;
529
530
	if ( isset($wp_registered_widget_updates[$id_base]) ) {
531
		if ( empty($update_callback) )
532
			unset($wp_registered_widget_updates[$id_base]);
533
		return;
534
	}
535
536
	$widget = array(
537
		'callback' => $update_callback,
538
		'params' => array_slice(func_get_args(), 3)
539
	);
540
541
	$widget = array_merge($widget, $options);
542
	$wp_registered_widget_updates[$id_base] = $widget;
543
}
544
545
/**
546
 * Registers the form callback for a widget.
547
 *
548
 * @since 2.8.0
549
 *
550
 * @global array $wp_registered_widget_controls
551
 *
552
 * @param int|string $id            Widget ID.
553
 * @param string     $name          Name attribute for the widget.
554
 * @param callable   $form_callback Form callback.
555
 * @param array      $options       Optional. Widget control options. See wp_register_widget_control().
556
 *                                  Default empty array.
557
 */
558
function _register_widget_form_callback($id, $name, $form_callback, $options = array()) {
559
	global $wp_registered_widget_controls;
560
561
	$id = strtolower($id);
562
563
	if ( empty($form_callback) ) {
564
		unset($wp_registered_widget_controls[$id]);
565
		return;
566
	}
567
568
	if ( isset($wp_registered_widget_controls[$id]) && !did_action( 'widgets_init' ) )
569
		return;
570
571
	$defaults = array('width' => 250, 'height' => 200 );
572
	$options = wp_parse_args($options, $defaults);
573
	$options['width'] = (int) $options['width'];
574
	$options['height'] = (int) $options['height'];
575
576
	$widget = array(
577
		'name' => $name,
578
		'id' => $id,
579
		'callback' => $form_callback,
580
		'params' => array_slice(func_get_args(), 4)
581
	);
582
	$widget = array_merge($widget, $options);
583
584
	$wp_registered_widget_controls[$id] = $widget;
585
}
586
587
/**
588
 * Remove control callback for widget.
589
 *
590
 * @since 2.2.0
591
 *
592
 * @param int|string $id Widget ID.
593
 */
594
function wp_unregister_widget_control($id) {
595
	wp_register_widget_control( $id, '', '' );
596
}
597
598
/**
599
 * Display dynamic sidebar.
600
 *
601
 * By default this displays the default sidebar or 'sidebar-1'. If your theme specifies the 'id' or
602
 * 'name' parameter for its registered sidebars you can pass an id or name as the $index parameter.
603
 * Otherwise, you can pass in a numerical index to display the sidebar at that index.
604
 *
605
 * @since 2.2.0
606
 *
607
 * @global array $wp_registered_sidebars
608
 * @global array $wp_registered_widgets
609
 *
610
 * @param int|string $index Optional, default is 1. Index, name or ID of dynamic sidebar.
0 ignored issues
show
Consider making the type for parameter $index a bit more specific; maybe use integer.
Loading history...
611
 * @return bool True, if widget sidebar was found and called. False if not found or not called.
612
 */
613
function dynamic_sidebar( $index = 1 ) {
614
	global $wp_registered_sidebars, $wp_registered_widgets;
615
616
	if ( is_int( $index ) ) {
617
		$index = "sidebar-$index";
618
	} else {
619
		$index = sanitize_title( $index );
620
		foreach ( (array) $wp_registered_sidebars as $key => $value ) {
621
			if ( sanitize_title( $value['name'] ) == $index ) {
622
				$index = $key;
623
				break;
624
			}
625
		}
626
	}
627
628
	$sidebars_widgets = wp_get_sidebars_widgets();
629
	if ( empty( $wp_registered_sidebars[ $index ] ) || empty( $sidebars_widgets[ $index ] ) || ! is_array( $sidebars_widgets[ $index ] ) ) {
630
		/** This action is documented in wp-includes/widget.php */
631
		do_action( 'dynamic_sidebar_before', $index, false );
632
		/** This action is documented in wp-includes/widget.php */
633
		do_action( 'dynamic_sidebar_after',  $index, false );
634
		/** This filter is documented in wp-includes/widget.php */
635
		return apply_filters( 'dynamic_sidebar_has_widgets', false, $index );
636
	}
637
638
	/**
639
	 * Fires before widgets are rendered in a dynamic sidebar.
640
	 *
641
	 * Note: The action also fires for empty sidebars, and on both the front end
642
	 * and back end, including the Inactive Widgets sidebar on the Widgets screen.
643
	 *
644
	 * @since 3.9.0
645
	 *
646
	 * @param int|string $index       Index, name, or ID of the dynamic sidebar.
647
	 * @param bool       $has_widgets Whether the sidebar is populated with widgets.
648
	 *                                Default true.
649
	 */
650
	do_action( 'dynamic_sidebar_before', $index, true );
651
	$sidebar = $wp_registered_sidebars[$index];
652
653
	$did_one = false;
654
	foreach ( (array) $sidebars_widgets[$index] as $id ) {
655
656
		if ( !isset($wp_registered_widgets[$id]) ) continue;
657
658
		$params = array_merge(
659
			array( array_merge( $sidebar, array('widget_id' => $id, 'widget_name' => $wp_registered_widgets[$id]['name']) ) ),
660
			(array) $wp_registered_widgets[$id]['params']
661
		);
662
663
		// Substitute HTML id and class attributes into before_widget
664
		$classname_ = '';
665
		foreach ( (array) $wp_registered_widgets[$id]['classname'] as $cn ) {
666
			if ( is_string($cn) )
667
				$classname_ .= '_' . $cn;
668
			elseif ( is_object($cn) )
669
				$classname_ .= '_' . get_class($cn);
670
		}
671
		$classname_ = ltrim($classname_, '_');
672
		$params[0]['before_widget'] = sprintf($params[0]['before_widget'], $id, $classname_);
673
674
		/**
675
		 * Filters the parameters passed to a widget's display callback.
676
		 *
677
		 * Note: The filter is evaluated on both the front end and back end,
678
		 * including for the Inactive Widgets sidebar on the Widgets screen.
679
		 *
680
		 * @since 2.5.0
681
		 *
682
		 * @see register_sidebar()
683
		 *
684
		 * @param array $params {
685
		 *     @type array $args  {
686
		 *         An array of widget display arguments.
687
		 *
688
		 *         @type string $name          Name of the sidebar the widget is assigned to.
689
		 *         @type string $id            ID of the sidebar the widget is assigned to.
690
		 *         @type string $description   The sidebar description.
691
		 *         @type string $class         CSS class applied to the sidebar container.
692
		 *         @type string $before_widget HTML markup to prepend to each widget in the sidebar.
693
		 *         @type string $after_widget  HTML markup to append to each widget in the sidebar.
694
		 *         @type string $before_title  HTML markup to prepend to the widget title when displayed.
695
		 *         @type string $after_title   HTML markup to append to the widget title when displayed.
696
		 *         @type string $widget_id     ID of the widget.
697
		 *         @type string $widget_name   Name of the widget.
698
		 *     }
699
		 *     @type array $widget_args {
700
		 *         An array of multi-widget arguments.
701
		 *
702
		 *         @type int $number Number increment used for multiples of the same widget.
703
		 *     }
704
		 * }
705
		 */
706
		$params = apply_filters( 'dynamic_sidebar_params', $params );
707
708
		$callback = $wp_registered_widgets[$id]['callback'];
709
710
		/**
711
		 * Fires before a widget's display callback is called.
712
		 *
713
		 * Note: The action fires on both the front end and back end, including
714
		 * for widgets in the Inactive Widgets sidebar on the Widgets screen.
715
		 *
716
		 * The action is not fired for empty sidebars.
717
		 *
718
		 * @since 3.0.0
719
		 *
720
		 * @param array $widget_id {
721
		 *     An associative array of widget arguments.
722
		 *
723
		 *     @type string $name                Name of the widget.
724
		 *     @type string $id                  Widget ID.
725
		 *     @type array|callable $callback    When the hook is fired on the front end, $callback is an array
726
		 *                                       containing the widget object. Fired on the back end, $callback
727
		 *                                       is 'wp_widget_control', see $_callback.
728
		 *     @type array          $params      An associative array of multi-widget arguments.
729
		 *     @type string         $classname   CSS class applied to the widget container.
730
		 *     @type string         $description The widget description.
731
		 *     @type array          $_callback   When the hook is fired on the back end, $_callback is populated
732
		 *                                       with an array containing the widget object, see $callback.
733
		 * }
734
		 */
735
		do_action( 'dynamic_sidebar', $wp_registered_widgets[ $id ] );
736
737
		if ( is_callable($callback) ) {
738
			call_user_func_array($callback, $params);
739
			$did_one = true;
740
		}
741
	}
742
743
	/**
744
	 * Fires after widgets are rendered in a dynamic sidebar.
745
	 *
746
	 * Note: The action also fires for empty sidebars, and on both the front end
747
	 * and back end, including the Inactive Widgets sidebar on the Widgets screen.
748
	 *
749
	 * @since 3.9.0
750
	 *
751
	 * @param int|string $index       Index, name, or ID of the dynamic sidebar.
752
	 * @param bool       $has_widgets Whether the sidebar is populated with widgets.
753
	 *                                Default true.
754
	 */
755
	do_action( 'dynamic_sidebar_after', $index, true );
756
757
	/**
758
	 * Filters whether a sidebar has widgets.
759
	 *
760
	 * Note: The filter is also evaluated for empty sidebars, and on both the front end
761
	 * and back end, including the Inactive Widgets sidebar on the Widgets screen.
762
	 *
763
	 * @since 3.9.0
764
	 *
765
	 * @param bool       $did_one Whether at least one widget was rendered in the sidebar.
766
	 *                            Default false.
767
	 * @param int|string $index   Index, name, or ID of the dynamic sidebar.
768
	 */
769
	return apply_filters( 'dynamic_sidebar_has_widgets', $did_one, $index );
770
}
771
772
/**
773
 * Whether widget is displayed on the front end.
774
 *
775
 * Either $callback or $id_base can be used
776
 * $id_base is the first argument when extending WP_Widget class
777
 * Without the optional $widget_id parameter, returns the ID of the first sidebar
778
 * in which the first instance of the widget with the given callback or $id_base is found.
779
 * With the $widget_id parameter, returns the ID of the sidebar where
780
 * the widget with that callback/$id_base AND that ID is found.
781
 *
782
 * NOTE: $widget_id and $id_base are the same for single widgets. To be effective
783
 * this function has to run after widgets have initialized, at action {@see 'init'} or later.
784
 *
785
 * @since 2.2.0
786
 *
787
 * @global array $wp_registered_widgets
788
 *
789
 * @param string|false $callback      Optional, Widget callback to check. Default false.
790
 * @param int|false    $widget_id     Optional. Widget ID. Optional, but needed for checking. Default false.
791
 * @param string|false $id_base       Optional. The base ID of a widget created by extending WP_Widget. Default false.
792
 * @param bool         $skip_inactive Optional. Whether to check in 'wp_inactive_widgets'. Default true.
793
 * @return string|false False if widget is not active or id of sidebar in which the widget is active.
0 ignored issues
show
Should the return type not be integer|string|false?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
794
 */
795
function is_active_widget( $callback = false, $widget_id = false, $id_base = false, $skip_inactive = true ) {
796
	global $wp_registered_widgets;
797
798
	$sidebars_widgets = wp_get_sidebars_widgets();
799
800
	if ( is_array($sidebars_widgets) ) {
801
		foreach ( $sidebars_widgets as $sidebar => $widgets ) {
802
			if ( $skip_inactive && ( 'wp_inactive_widgets' === $sidebar || 'orphaned_widgets' === substr( $sidebar, 0, 16 ) ) ) {
803
				continue;
804
			}
805
806
			if ( is_array($widgets) ) {
807
				foreach ( $widgets as $widget ) {
808
					if ( ( $callback && isset($wp_registered_widgets[$widget]['callback']) && $wp_registered_widgets[$widget]['callback'] == $callback ) || ( $id_base && _get_widget_id_base($widget) == $id_base ) ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $callback of type false|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
Bug Best Practice introduced by
The expression $id_base of type false|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
809
						if ( !$widget_id || $widget_id == $wp_registered_widgets[$widget]['id'] )
0 ignored issues
show
Bug Best Practice introduced by
The expression $widget_id of type false|integer is loosely compared to false; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
810
							return $sidebar;
811
					}
812
				}
813
			}
814
		}
815
	}
816
	return false;
817
}
818
819
/**
820
 * Whether the dynamic sidebar is enabled and used by theme.
821
 *
822
 * @since 2.2.0
823
 *
824
 * @global array $wp_registered_widgets
825
 * @global array $wp_registered_sidebars
826
 *
827
 * @return bool True, if using widgets. False, if not using widgets.
828
 */
829
function is_dynamic_sidebar() {
830
	global $wp_registered_widgets, $wp_registered_sidebars;
831
	$sidebars_widgets = get_option('sidebars_widgets');
832
	foreach ( (array) $wp_registered_sidebars as $index => $sidebar ) {
833
		if ( ! empty( $sidebars_widgets[ $index ] ) ) {
834
			foreach ( (array) $sidebars_widgets[$index] as $widget )
835
				if ( array_key_exists($widget, $wp_registered_widgets) )
836
					return true;
837
		}
838
	}
839
	return false;
840
}
841
842
/**
843
 * Whether a sidebar is in use.
844
 *
845
 * @since 2.8.0
846
 *
847
 * @param string|int $index Sidebar name, id or number to check.
848
 * @return bool true if the sidebar is in use, false otherwise.
849
 */
850
function is_active_sidebar( $index ) {
851
	$index = ( is_int($index) ) ? "sidebar-$index" : sanitize_title($index);
852
	$sidebars_widgets = wp_get_sidebars_widgets();
853
	$is_active_sidebar = ! empty( $sidebars_widgets[$index] );
854
855
	/**
856
	 * Filters whether a dynamic sidebar is considered "active".
857
	 *
858
	 * @since 3.9.0
859
	 *
860
	 * @param bool       $is_active_sidebar Whether or not the sidebar should be considered "active".
861
	 *                                      In other words, whether the sidebar contains any widgets.
862
	 * @param int|string $index             Index, name, or ID of the dynamic sidebar.
863
	 */
864
	return apply_filters( 'is_active_sidebar', $is_active_sidebar, $index );
865
}
866
867
//
868
// Internal Functions
869
//
870
871
/**
872
 * Retrieve full list of sidebars and their widget instance IDs.
873
 *
874
 * Will upgrade sidebar widget list, if needed. Will also save updated list, if
875
 * needed.
876
 *
877
 * @since 2.2.0
878
 * @access private
879
 *
880
 * @global array $_wp_sidebars_widgets
881
 * @global array $sidebars_widgets
882
 *
883
 * @param bool $deprecated Not used (argument deprecated).
884
 * @return array Upgraded list of widgets to version 3 array format when called from the admin.
885
 */
886
function wp_get_sidebars_widgets( $deprecated = true ) {
887
	if ( $deprecated !== true )
888
		_deprecated_argument( __FUNCTION__, '2.8.1' );
889
890
	global $_wp_sidebars_widgets, $sidebars_widgets;
891
892
	// If loading from front page, consult $_wp_sidebars_widgets rather than options
893
	// to see if wp_convert_widget_settings() has made manipulations in memory.
894
	if ( !is_admin() ) {
895
		if ( empty($_wp_sidebars_widgets) )
896
			$_wp_sidebars_widgets = get_option('sidebars_widgets', array());
897
898
		$sidebars_widgets = $_wp_sidebars_widgets;
899
	} else {
900
		$sidebars_widgets = get_option('sidebars_widgets', array());
901
	}
902
903
	if ( is_array( $sidebars_widgets ) && isset($sidebars_widgets['array_version']) )
904
		unset($sidebars_widgets['array_version']);
905
906
	/**
907
	 * Filters the list of sidebars and their widgets.
908
	 *
909
	 * @since 2.7.0
910
	 *
911
	 * @param array $sidebars_widgets An associative array of sidebars and their widgets.
912
	 */
913
	return apply_filters( 'sidebars_widgets', $sidebars_widgets );
914
}
915
916
/**
917
 * Set the sidebar widget option to update sidebars.
918
 *
919
 * @since 2.2.0
920
 * @access private
921
 *
922
 * @param array $sidebars_widgets Sidebar widgets and their settings.
923
 */
924
function wp_set_sidebars_widgets( $sidebars_widgets ) {
925
	if ( !isset( $sidebars_widgets['array_version'] ) )
926
		$sidebars_widgets['array_version'] = 3;
927
	update_option( 'sidebars_widgets', $sidebars_widgets );
928
}
929
930
/**
931
 * Retrieve default registered sidebars list.
932
 *
933
 * @since 2.2.0
934
 * @access private
935
 *
936
 * @global array $wp_registered_sidebars
937
 *
938
 * @return array
939
 */
940
function wp_get_widget_defaults() {
941
	global $wp_registered_sidebars;
942
943
	$defaults = array();
944
945
	foreach ( (array) $wp_registered_sidebars as $index => $sidebar )
946
		$defaults[$index] = array();
947
948
	return $defaults;
949
}
950
951
/**
952
 * Convert the widget settings from single to multi-widget format.
953
 *
954
 * @since 2.8.0
955
 *
956
 * @global array $_wp_sidebars_widgets
957
 *
958
 * @param string $base_name
959
 * @param string $option_name
960
 * @param array  $settings
961
 * @return array
0 ignored issues
show
Consider making the return type a bit more specific; maybe use array<string,integer>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
962
 */
963
function wp_convert_widget_settings($base_name, $option_name, $settings) {
964
	// This test may need expanding.
965
	$single = $changed = false;
966
	if ( empty($settings) ) {
967
		$single = true;
968
	} else {
969
		foreach ( array_keys($settings) as $number ) {
970
			if ( 'number' == $number )
971
				continue;
972
			if ( !is_numeric($number) ) {
973
				$single = true;
974
				break;
975
			}
976
		}
977
	}
978
979
	if ( $single ) {
980
		$settings = array( 2 => $settings );
981
982
		// If loading from the front page, update sidebar in memory but don't save to options
983
		if ( is_admin() ) {
984
			$sidebars_widgets = get_option('sidebars_widgets');
985
		} else {
986
			if ( empty($GLOBALS['_wp_sidebars_widgets']) )
987
				$GLOBALS['_wp_sidebars_widgets'] = get_option('sidebars_widgets', array());
988
			$sidebars_widgets = &$GLOBALS['_wp_sidebars_widgets'];
989
		}
990
991
		foreach ( (array) $sidebars_widgets as $index => $sidebar ) {
992
			if ( is_array($sidebar) ) {
993
				foreach ( $sidebar as $i => $name ) {
994
					if ( $base_name == $name ) {
995
						$sidebars_widgets[$index][$i] = "$name-2";
996
						$changed = true;
997
						break 2;
998
					}
999
				}
1000
			}
1001
		}
1002
1003
		if ( is_admin() && $changed )
1004
			update_option('sidebars_widgets', $sidebars_widgets);
1005
	}
1006
1007
	$settings['_multiwidget'] = 1;
1008
	if ( is_admin() )
1009
		update_option( $option_name, $settings );
1010
1011
	return $settings;
1012
}
1013
1014
/**
1015
 * Output an arbitrary widget as a template tag.
1016
 *
1017
 * @since 2.8.0
1018
 *
1019
 * @global WP_Widget_Factory $wp_widget_factory
1020
 *
1021
 * @param string $widget   The widget's PHP class name (see class-wp-widget.php).
1022
 * @param array  $instance Optional. The widget's instance settings. Default empty array.
1023
 * @param array  $args {
1024
 *     Optional. Array of arguments to configure the display of the widget.
1025
 *
1026
 *     @type string $before_widget HTML content that will be prepended to the widget's HTML output.
1027
 *                                 Default `<div class="widget %s">`, where `%s` is the widget's class name.
1028
 *     @type string $after_widget  HTML content that will be appended to the widget's HTML output.
1029
 *                                 Default `</div>`.
1030
 *     @type string $before_title  HTML content that will be prepended to the widget's title when displayed.
1031
 *                                 Default `<h2 class="widgettitle">`.
1032
 *     @type string $after_title   HTML content that will be appended to the widget's title when displayed.
1033
 *                                 Default `</h2>`.
1034
 * }
1035
 */
1036
function the_widget( $widget, $instance = array(), $args = array() ) {
1037
	global $wp_widget_factory;
1038
1039
	$widget_obj = $wp_widget_factory->widgets[$widget];
1040
	if ( ! ( $widget_obj instanceof WP_Widget ) ) {
1041
		return;
1042
	}
1043
1044
	$default_args = array(
1045
		'before_widget' => '<div class="widget %s">',
1046
		'after_widget'  => "</div>",
1047
		'before_title'  => '<h2 class="widgettitle">',
1048
		'after_title'   => '</h2>',
1049
	);
1050
	$args = wp_parse_args( $args, $default_args );
1051
	$args['before_widget'] = sprintf( $args['before_widget'], $widget_obj->widget_options['classname'] );
1052
1053
	$instance = wp_parse_args($instance);
1054
1055
	/**
1056
	 * Fires before rendering the requested widget.
1057
	 *
1058
	 * @since 3.0.0
1059
	 *
1060
	 * @param string $widget   The widget's class name.
1061
	 * @param array  $instance The current widget instance's settings.
1062
	 * @param array  $args     An array of the widget's sidebar arguments.
1063
	 */
1064
	do_action( 'the_widget', $widget, $instance, $args );
1065
1066
	$widget_obj->_set(-1);
1067
	$widget_obj->widget($args, $instance);
1068
}
1069
1070
/**
1071
 * Retrieves the widget ID base value.
1072
 *
1073
 * @since 2.8.0
1074
 *
1075
 * @param string $id Widget ID.
1076
 * @return string Widget ID base.
1077
 */
1078
function _get_widget_id_base( $id ) {
1079
	return preg_replace( '/-[0-9]+$/', '', $id );
1080
}
1081
1082
/**
1083
 * Handle sidebars config after theme change
1084
 *
1085
 * @access private
1086
 * @since 3.3.0
1087
 *
1088
 * @global array $sidebars_widgets
1089
 */
1090
function _wp_sidebars_changed() {
1091
	global $sidebars_widgets;
1092
1093
	if ( ! is_array( $sidebars_widgets ) )
1094
		$sidebars_widgets = wp_get_sidebars_widgets();
1095
1096
	retrieve_widgets(true);
1097
}
1098
1099
/**
1100
 * Look for "lost" widgets, this has to run at least on each theme change.
1101
 *
1102
 * @since 2.8.0
1103
 *
1104
 * @global array $wp_registered_sidebars
1105
 * @global array $sidebars_widgets
1106
 * @global array $wp_registered_widgets
1107
 *
1108
 * @param string|bool $theme_changed Whether the theme was changed as a boolean. A value
1109
 *                                   of 'customize' defers updates for the Customizer.
1110
 * @return array|void
0 ignored issues
show
Consider making the return type a bit more specific; maybe use null|array<string,array>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
1111
 */
1112
function retrieve_widgets( $theme_changed = false ) {
1113
	global $wp_registered_sidebars, $sidebars_widgets, $wp_registered_widgets;
1114
1115
	$registered_sidebar_keys = array_keys( $wp_registered_sidebars );
1116
	$orphaned = 0;
1117
1118
	$old_sidebars_widgets = get_theme_mod( 'sidebars_widgets' );
1119
	if ( is_array( $old_sidebars_widgets ) ) {
1120
		// time() that sidebars were stored is in $old_sidebars_widgets['time']
1121
		$_sidebars_widgets = $old_sidebars_widgets['data'];
1122
1123
		if ( 'customize' !== $theme_changed ) {
1124
			remove_theme_mod( 'sidebars_widgets' );
1125
		}
1126
1127
		foreach ( $_sidebars_widgets as $sidebar => $widgets ) {
1128
			if ( 'wp_inactive_widgets' === $sidebar || 'orphaned_widgets' === substr( $sidebar, 0, 16 ) ) {
1129
				continue;
1130
			}
1131
1132
			if ( !in_array( $sidebar, $registered_sidebar_keys ) ) {
1133
				$_sidebars_widgets['orphaned_widgets_' . ++$orphaned] = $widgets;
1134
				unset( $_sidebars_widgets[$sidebar] );
1135
			}
1136
		}
1137
	} else {
1138
		if ( empty( $sidebars_widgets ) )
1139
			return;
1140
1141
		unset( $sidebars_widgets['array_version'] );
1142
1143
		$old = array_keys($sidebars_widgets);
1144
		sort($old);
1145
		sort($registered_sidebar_keys);
1146
1147
		if ( $old == $registered_sidebar_keys )
1148
			return;
1149
1150
		$_sidebars_widgets = array(
1151
			'wp_inactive_widgets' => !empty( $sidebars_widgets['wp_inactive_widgets'] ) ? $sidebars_widgets['wp_inactive_widgets'] : array()
1152
		);
1153
1154
		unset( $sidebars_widgets['wp_inactive_widgets'] );
1155
1156
		foreach ( $wp_registered_sidebars as $id => $settings ) {
1157
			if ( $theme_changed ) {
1158
				$_sidebars_widgets[$id] = array_shift( $sidebars_widgets );
1159
			} else {
1160
				// no theme change, grab only sidebars that are currently registered
1161
				if ( isset( $sidebars_widgets[$id] ) ) {
1162
					$_sidebars_widgets[$id] = $sidebars_widgets[$id];
1163
					unset( $sidebars_widgets[$id] );
1164
				}
1165
			}
1166
		}
1167
1168
		foreach ( $sidebars_widgets as $val ) {
1169
			if ( is_array($val) && ! empty( $val ) )
1170
				$_sidebars_widgets['orphaned_widgets_' . ++$orphaned] = $val;
1171
		}
1172
	}
1173
1174
	// discard invalid, theme-specific widgets from sidebars
1175
	$shown_widgets = array();
1176
1177
	foreach ( $_sidebars_widgets as $sidebar => $widgets ) {
1178
		if ( !is_array($widgets) )
1179
			continue;
1180
1181
		$_widgets = array();
1182
		foreach ( $widgets as $widget ) {
1183
			if ( isset($wp_registered_widgets[$widget]) )
1184
				$_widgets[] = $widget;
1185
		}
1186
1187
		$_sidebars_widgets[$sidebar] = $_widgets;
1188
		$shown_widgets = array_merge($shown_widgets, $_widgets);
1189
	}
1190
1191
	$sidebars_widgets = $_sidebars_widgets;
1192
	unset($_sidebars_widgets, $_widgets);
1193
1194
	// find hidden/lost multi-widget instances
1195
	$lost_widgets = array();
1196
	foreach ( $wp_registered_widgets as $key => $val ) {
1197
		if ( in_array($key, $shown_widgets, true) )
1198
			continue;
1199
1200
		$number = preg_replace('/.+?-([0-9]+)$/', '$1', $key);
1201
1202
		if ( 2 > (int) $number )
1203
			continue;
1204
1205
		$lost_widgets[] = $key;
1206
	}
1207
1208
	$sidebars_widgets['wp_inactive_widgets'] = array_merge($lost_widgets, (array) $sidebars_widgets['wp_inactive_widgets']);
1209
	if ( 'customize' !== $theme_changed ) {
1210
		wp_set_sidebars_widgets( $sidebars_widgets );
1211
	}
1212
1213
	return $sidebars_widgets;
1214
}
1215
1216
/**
1217
 * Display the RSS entries in a list.
1218
 *
1219
 * @since 2.5.0
1220
 *
1221
 * @param string|array|object $rss RSS url.
1222
 * @param array $args Widget arguments.
1223
 */
1224
function wp_widget_rss_output( $rss, $args = array() ) {
1225
	if ( is_string( $rss ) ) {
1226
		$rss = fetch_feed($rss);
1227
	} elseif ( is_array($rss) && isset($rss['url']) ) {
1228
		$args = $rss;
1229
		$rss = fetch_feed($rss['url']);
1230
	} elseif ( !is_object($rss) ) {
1231
		return;
1232
	}
1233
1234
	if ( is_wp_error($rss) ) {
1235
		if ( is_admin() || current_user_can('manage_options') )
1236
			echo '<p><strong>' . __( 'RSS Error:' ) . '</strong> ' . $rss->get_error_message() . '</p>';
1237
		return;
1238
	}
1239
1240
	$default_args = array( 'show_author' => 0, 'show_date' => 0, 'show_summary' => 0, 'items' => 0 );
1241
	$args = wp_parse_args( $args, $default_args );
1242
1243
	$items = (int) $args['items'];
1244
	if ( $items < 1 || 20 < $items )
1245
		$items = 10;
1246
	$show_summary  = (int) $args['show_summary'];
1247
	$show_author   = (int) $args['show_author'];
1248
	$show_date     = (int) $args['show_date'];
1249
1250
	if ( !$rss->get_item_quantity() ) {
1251
		echo '<ul><li>' . __( 'An error has occurred, which probably means the feed is down. Try again later.' ) . '</li></ul>';
1252
		$rss->__destruct();
1253
		unset($rss);
1254
		return;
1255
	}
1256
1257
	echo '<ul>';
1258
	foreach ( $rss->get_items( 0, $items ) as $item ) {
1259
		$link = $item->get_link();
1260
		while ( stristr( $link, 'http' ) != $link ) {
1261
			$link = substr( $link, 1 );
1262
		}
1263
		$link = esc_url( strip_tags( $link ) );
1264
1265
		$title = esc_html( trim( strip_tags( $item->get_title() ) ) );
1266
		if ( empty( $title ) ) {
1267
			$title = __( 'Untitled' );
1268
		}
1269
1270
		$desc = @html_entity_decode( $item->get_description(), ENT_QUOTES, get_option( 'blog_charset' ) );
1271
		$desc = esc_attr( wp_trim_words( $desc, 55, ' [&hellip;]' ) );
1272
1273
		$summary = '';
1274
		if ( $show_summary ) {
1275
			$summary = $desc;
1276
1277
			// Change existing [...] to [&hellip;].
1278
			if ( '[...]' == substr( $summary, -5 ) ) {
1279
				$summary = substr( $summary, 0, -5 ) . '[&hellip;]';
1280
			}
1281
1282
			$summary = '<div class="rssSummary">' . esc_html( $summary ) . '</div>';
1283
		}
1284
1285
		$date = '';
1286
		if ( $show_date ) {
1287
			$date = $item->get_date( 'U' );
1288
1289
			if ( $date ) {
1290
				$date = ' <span class="rss-date">' . date_i18n( get_option( 'date_format' ), $date ) . '</span>';
1291
			}
1292
		}
1293
1294
		$author = '';
1295
		if ( $show_author ) {
1296
			$author = $item->get_author();
1297
			if ( is_object($author) ) {
1298
				$author = $author->get_name();
1299
				$author = ' <cite>' . esc_html( strip_tags( $author ) ) . '</cite>';
1300
			}
1301
		}
1302
1303
		if ( $link == '' ) {
1304
			echo "<li>$title{$date}{$summary}{$author}</li>";
1305
		} elseif ( $show_summary ) {
1306
			echo "<li><a class='rsswidget' href='$link'>$title</a>{$date}{$summary}{$author}</li>";
1307
		} else {
1308
			echo "<li><a class='rsswidget' href='$link'>$title</a>{$date}{$author}</li>";
1309
		}
1310
	}
1311
	echo '</ul>';
1312
	$rss->__destruct();
1313
	unset($rss);
1314
}
1315
1316
/**
1317
 * Display RSS widget options form.
1318
 *
1319
 * The options for what fields are displayed for the RSS form are all booleans
1320
 * and are as follows: 'url', 'title', 'items', 'show_summary', 'show_author',
1321
 * 'show_date'.
1322
 *
1323
 * @since 2.5.0
1324
 *
1325
 * @param array|string $args Values for input fields.
1326
 * @param array $inputs Override default display options.
0 ignored issues
show
Should the type for parameter $inputs not be array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
1327
 */
1328
function wp_widget_rss_form( $args, $inputs = null ) {
1329
	$default_inputs = array( 'url' => true, 'title' => true, 'items' => true, 'show_summary' => true, 'show_author' => true, 'show_date' => true );
1330
	$inputs = wp_parse_args( $inputs, $default_inputs );
0 ignored issues
show
It seems like $inputs can also be of type null; however, wp_parse_args() does only seem to accept string|array|object, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1331
1332
	$args['title'] = isset( $args['title'] ) ? $args['title'] : '';
1333
	$args['url'] = isset( $args['url'] ) ? $args['url'] : '';
1334
	$args['items'] = isset( $args['items'] ) ? (int) $args['items'] : 0;
1335
1336
	if ( $args['items'] < 1 || 20 < $args['items'] ) {
1337
		$args['items'] = 10;
1338
	}
1339
1340
	$args['show_summary']   = isset( $args['show_summary'] ) ? (int) $args['show_summary'] : (int) $inputs['show_summary'];
1341
	$args['show_author']    = isset( $args['show_author'] ) ? (int) $args['show_author'] : (int) $inputs['show_author'];
1342
	$args['show_date']      = isset( $args['show_date'] ) ? (int) $args['show_date'] : (int) $inputs['show_date'];
1343
1344
	if ( ! empty( $args['error'] ) ) {
1345
		echo '<p class="widget-error"><strong>' . __( 'RSS Error:' ) . '</strong> ' . $args['error'] . '</p>';
1346
	}
1347
1348
	$esc_number = esc_attr( $args['number'] );
1349 View Code Duplication
	if ( $inputs['url'] ) :
1350
?>
1351
	<p><label for="rss-url-<?php echo $esc_number; ?>"><?php _e( 'Enter the RSS feed URL here:' ); ?></label>
1352
	<input class="widefat" id="rss-url-<?php echo $esc_number; ?>" name="widget-rss[<?php echo $esc_number; ?>][url]" type="text" value="<?php echo esc_url( $args['url'] ); ?>" /></p>
1353
<?php endif; if ( $inputs['title'] ) : ?>
1354
	<p><label for="rss-title-<?php echo $esc_number; ?>"><?php _e( 'Give the feed a title (optional):' ); ?></label>
1355
	<input class="widefat" id="rss-title-<?php echo $esc_number; ?>" name="widget-rss[<?php echo $esc_number; ?>][title]" type="text" value="<?php echo esc_attr( $args['title'] ); ?>" /></p>
1356
<?php endif; if ( $inputs['items'] ) : ?>
1357
	<p><label for="rss-items-<?php echo $esc_number; ?>"><?php _e( 'How many items would you like to display?' ); ?></label>
1358
	<select id="rss-items-<?php echo $esc_number; ?>" name="widget-rss[<?php echo $esc_number; ?>][items]">
1359
	<?php
1360
	for ( $i = 1; $i <= 20; ++$i ) {
1361
		echo "<option value='$i' " . selected( $args['items'], $i, false ) . ">$i</option>";
1362
	}
1363
	?>
1364
	</select></p>
1365 View Code Duplication
<?php endif; if ( $inputs['show_summary'] ) : ?>
1366
	<p><input id="rss-show-summary-<?php echo $esc_number; ?>" name="widget-rss[<?php echo $esc_number; ?>][show_summary]" type="checkbox" value="1" <?php checked( $args['show_summary'] ); ?> />
1367
	<label for="rss-show-summary-<?php echo $esc_number; ?>"><?php _e( 'Display item content?' ); ?></label></p>
1368
<?php endif; if ( $inputs['show_author'] ) : ?>
1369
	<p><input id="rss-show-author-<?php echo $esc_number; ?>" name="widget-rss[<?php echo $esc_number; ?>][show_author]" type="checkbox" value="1" <?php checked( $args['show_author'] ); ?> />
1370
	<label for="rss-show-author-<?php echo $esc_number; ?>"><?php _e( 'Display item author if available?' ); ?></label></p>
1371 View Code Duplication
<?php endif; if ( $inputs['show_date'] ) : ?>
1372
	<p><input id="rss-show-date-<?php echo $esc_number; ?>" name="widget-rss[<?php echo $esc_number; ?>][show_date]" type="checkbox" value="1" <?php checked( $args['show_date'] ); ?>/>
1373
	<label for="rss-show-date-<?php echo $esc_number; ?>"><?php _e( 'Display item date?' ); ?></label></p>
1374
<?php
1375
	endif;
1376
	foreach ( array_keys($default_inputs) as $input ) :
1377
		if ( 'hidden' === $inputs[$input] ) :
1378
			$id = str_replace( '_', '-', $input );
1379
?>
1380
	<input type="hidden" id="rss-<?php echo esc_attr( $id ); ?>-<?php echo $esc_number; ?>" name="widget-rss[<?php echo $esc_number; ?>][<?php echo esc_attr( $input ); ?>]" value="<?php echo esc_attr( $args[ $input ] ); ?>" />
1381
<?php
1382
		endif;
1383
	endforeach;
1384
}
1385
1386
/**
1387
 * Process RSS feed widget data and optionally retrieve feed items.
1388
 *
1389
 * The feed widget can not have more than 20 items or it will reset back to the
1390
 * default, which is 10.
1391
 *
1392
 * The resulting array has the feed title, feed url, feed link (from channel),
1393
 * feed items, error (if any), and whether to show summary, author, and date.
1394
 * All respectively in the order of the array elements.
1395
 *
1396
 * @since 2.5.0
1397
 *
1398
 * @param array $widget_rss RSS widget feed data. Expects unescaped data.
1399
 * @param bool $check_feed Optional, default is true. Whether to check feed for errors.
1400
 * @return array
1401
 */
1402
function wp_widget_rss_process( $widget_rss, $check_feed = true ) {
1403
	$items = (int) $widget_rss['items'];
1404
	if ( $items < 1 || 20 < $items )
1405
		$items = 10;
1406
	$url           = esc_url_raw( strip_tags( $widget_rss['url'] ) );
1407
	$title         = isset( $widget_rss['title'] ) ? trim( strip_tags( $widget_rss['title'] ) ) : '';
1408
	$show_summary  = isset( $widget_rss['show_summary'] ) ? (int) $widget_rss['show_summary'] : 0;
1409
	$show_author   = isset( $widget_rss['show_author'] ) ? (int) $widget_rss['show_author'] :0;
1410
	$show_date     = isset( $widget_rss['show_date'] ) ? (int) $widget_rss['show_date'] : 0;
1411
1412
	if ( $check_feed ) {
1413
		$rss = fetch_feed($url);
1414
		$error = false;
1415
		$link = '';
1416
		if ( is_wp_error($rss) ) {
1417
			$error = $rss->get_error_message();
0 ignored issues
show
The method get_error_message does only exist in WP_Error, but not in SimplePie.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
1418
		} else {
1419
			$link = esc_url(strip_tags($rss->get_permalink()));
0 ignored issues
show
The method get_permalink does only exist in SimplePie, but not in WP_Error.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
1420
			while ( stristr($link, 'http') != $link )
1421
				$link = substr($link, 1);
1422
1423
			$rss->__destruct();
0 ignored issues
show
The method __destruct does only exist in SimplePie, but not in WP_Error.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
1424
			unset($rss);
1425
		}
1426
	}
1427
1428
	return compact( 'title', 'url', 'link', 'items', 'error', 'show_summary', 'show_author', 'show_date' );
1429
}
1430
1431
/**
1432
 * Registers all of the default WordPress widgets on startup.
1433
 *
1434
 * Calls {@see 'widgets_init'} action after all of the WordPress widgets have been registered.
1435
 *
1436
 * @since 2.2.0
1437
 */
1438
function wp_widgets_init() {
1439
	if ( ! is_blog_installed() ) {
1440
		return;
1441
	}
1442
1443
	register_widget( 'WP_Widget_Pages' );
1444
1445
	register_widget( 'WP_Widget_Calendar' );
1446
1447
	register_widget( 'WP_Widget_Archives' );
1448
1449
	if ( get_option( 'link_manager_enabled' ) ) {
1450
		register_widget( 'WP_Widget_Links' );
1451
	}
1452
1453
	register_widget( 'WP_Widget_Media_Audio' );
1454
1455
	register_widget( 'WP_Widget_Media_Image' );
1456
1457
	register_widget( 'WP_Widget_Media_Video' );
1458
1459
	register_widget( 'WP_Widget_Meta' );
1460
1461
	register_widget( 'WP_Widget_Search' );
1462
1463
	register_widget( 'WP_Widget_Text' );
1464
1465
	register_widget( 'WP_Widget_Categories' );
1466
1467
	register_widget( 'WP_Widget_Recent_Posts' );
1468
1469
	register_widget( 'WP_Widget_Recent_Comments' );
1470
1471
	register_widget( 'WP_Widget_RSS' );
1472
1473
	register_widget( 'WP_Widget_Tag_Cloud' );
1474
1475
	register_widget( 'WP_Nav_Menu_Widget' );
1476
1477
	register_widget( 'WP_Widget_HTML_Code' );
1478
1479
	/**
1480
	 * Fires after all default WordPress widgets have been registered.
1481
	 *
1482
	 * @since 2.2.0
1483
	 */
1484
	do_action( 'widgets_init' );
1485
}
1486