Completed
Push — gm-17/payment-widget ( cb2702...55e70e )
by
unknown
13:32 queued 03:19
created

Jetpack_Widgets::update_widget()   D

Complexity

Conditions 10
Paths 35

Size

Total Lines 33
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 10
eloc 23
nc 35
nop 4
dl 0
loc 33
rs 4.8196
c 0
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Widgets and Sidebars Library
4
 *
5
 * Helper functions for manipulating widgets on a per-blog basis.
6
 * Only helpful on `wp_loaded` or later (currently requires widgets to be registered and the theme context to already be loaded).
7
 *
8
 * Used by the REST API
9
 *
10
 * @autounit api widgets
11
 */
12
13
class Jetpack_Widgets {
14
15
	/**
16
	 * Returns the `sidebars_widgets` option with the `array_version` element removed.
17
	 *
18
	 * @return array The current value of sidebars_widgets
19
	 */
20
	public static function get_sidebars_widgets() {
21
		$sidebars = get_option( 'sidebars_widgets', array() );
22
		if ( isset( $sidebars['array_version'] ) ) {
23
			unset( $sidebars['array_version'] );
24
		}
25
		return $sidebars;
26
	}
27
28
	/**
29
	 * Format widget data for output and for use by other widget functions.
30
	 *
31
	 * The output looks like:
32
	 *
33
	 * array(
34
	 *	'id' => 'text-3',
35
	 *	'sidebar' => 'sidebar-1',
36
	 *	'position' => '0',
37
	 *	'settings' => array(
38
	 *		'title' => 'hello world'
39
	 *	)
40
	 * )
41
	 *
42
	 *
43
	 * @param string|integer $position The position of the widget in its sidebar.
44
	 * @param string $widget_id The widget's id (eg: 'text-3').
45
	 * @param string $sidebar The widget's sidebar id (eg: 'sidebar-1').
46
	 * @param array (Optional) $settings The settings for the widget.
47
	 *
48
	 * @return array A normalized array representing this widget.
49
	 */
50
	public static function format_widget( $position, $widget_id, $sidebar, $settings = null ) {
51
		if ( ! $settings ) {
52
			$all_settings = get_option( self::get_widget_option_name( $widget_id ) );
53
			$instance = self::get_widget_instance_key( $widget_id );
54
			$settings = $all_settings[$instance];
55
		}
56
		$widget = array();
57
58
		$widget['id']       = $widget_id;
59
		$widget['id_base']  = self::get_widget_id_base( $widget_id );
60
		$widget['settings'] = $settings;
61
		$widget['sidebar']  = $sidebar;
62
		$widget['position'] = $position;
63
64
		return $widget;
65
	}
66
67
	/**
68
	 * Return a widget's id_base from its id.
69
	 *
70
	 * @param string $widget_id The id of a widget. (eg: 'text-3')
71
	 *
72
	 * @return string The id_base of a widget (eg: 'text').
73
	 */
74
	public static function get_widget_id_base( $widget_id ) {
75
		// Grab what's before the hyphen.
76
		return substr( $widget_id, 0, strrpos( $widget_id, '-' ) );
77
	}
78
79
	/**
80
	 * Determine a widget's option name (the WP option where the widget's settings
81
	 * are stored - generally `widget_` + the widget's id_base).
82
	 *
83
	 * @param string $widget_id The id of a widget. (eg: 'text-3')
84
	 *
85
	 * @return string The option name of the widget's settings. (eg: 'widget_text')
86
	 */
87
	public static function get_widget_option_name( $widget_id ) {
88
		return 'widget_' . self::get_widget_id_base( $widget_id );
89
	}
90
91
	/**
92
	 * Determine a widget instance key from its ID. (eg: 'text-3' becomes '3').
93
	 * Used to access the widget's settings.
94
	 *
95
	 * @param string $widget_id The id of a widget.
96
	 *
97
	 * @return integer The instance key of that widget.
98
	 */
99
	public static function get_widget_instance_key( $widget_id ) {
100
		// Grab all numbers from the end of the id.
101
		preg_match('/(\d+)$/', $widget_id, $matches );
102
103
		return intval( $matches[0] );
104
	}
105
106
	/**
107
	 * Return a widget by ID (formatted for output) or null if nothing is found.
108
	 *
109
	 * @param string $widget_id The id of a widget to look for.
110
	 *
111
	 * @return array|null The matching formatted widget (see format_widget).
112
	 */
113
	public static function get_widget_by_id( $widget_id ) {
114
		$found = null;
115
		foreach ( self::get_all_widgets() as $widget ) {
116
			if ( $widget['id'] === $widget_id ) {
117
				$found = $widget;
118
			}
119
		}
120
		return $found;
121
	}
122
123
	/**
124
	 * Return an array of all widgets (active and inactive) formatted for output.
125
	 *
126
	 * @return array An array of all widgets (see format_widget).
127
	 */
128
	public static function get_all_widgets() {
129
		$all_widgets = array();
130
		$sidebars_widgets = self::get_all_sidebars();
131
132
		foreach ( $sidebars_widgets as $sidebar => $widgets ) {
133
			if ( ! is_array( $widgets ) ) {
134
				continue;
135
			}
136
			foreach ( $widgets as $key => $widget_id ) {
137
				array_push( $all_widgets, self::format_widget( $key, $widget_id, $sidebar ) );
138
			}
139
		}
140
141
		return $all_widgets;
142
	}
143
144
	/**
145
	 * Return an array of all active widgets formatted for output.
146
	 *
147
	 * @return array An array of all active widgets (see format_widget).
148
	 */
149
	public static function get_active_widgets() {
150
		$active_widgets = array();
151
		$all_widgets = self::get_all_widgets();
152
		foreach( $all_widgets as $widget ) {
153
			if ( 'wp_inactive_widgets' === $widget['sidebar'] ) {
154
				continue;
155
			}
156
			array_push( $active_widgets, $widget );
157
		}
158
		return $active_widgets;
159
	}
160
161
	/**
162
	 * Return an array of all widget IDs (active and inactive)
163
	 *
164
	 * @return array An array of all widget IDs.
165
	 */
166
	public static function get_all_widget_ids() {
167
		$all_widgets = array();
168
		$sidebars_widgets = self::get_all_sidebars();
169
		foreach ( array_values( $sidebars_widgets ) as $widgets ) {
170
			if ( ! is_array( $widgets ) ) {
171
				continue;
172
			}
173
			foreach ( array_values( $widgets ) as $widget_id ) {
174
				array_push( $all_widgets, $widget_id );
175
			}
176
		}
177
		return $all_widgets;
178
	}
179
180
	/**
181
	 * Return an array of widgets with a specific id_base (eg: `text`).
182
	 *
183
	 * @param string $id_base The id_base of a widget type.
184
	 *
185
	 * @return array All the formatted widgets matching that widget type (see format_widget).
186
	 */
187
	public static function get_widgets_with_id_base( $id_base ) {
188
		$matching_widgets = array();
189
		foreach ( self::get_all_widgets() as $widget ) {
190
			if ( self::get_widget_id_base( $widget['id'] ) === $id_base ) {
191
				array_push( $matching_widgets, $widget );
192
			}
193
		}
194
		return $matching_widgets;
195
	}
196
197
	/**
198
	 * Return the array of widget IDs in a sidebar or null if that sidebar does
199
	 * not exist. Will return an empty array for an existing empty sidebar.
200
	 *
201
	 * @param string $sidebar The id of a sidebar.
202
	 *
203
	 * @return array|null The array of widget IDs in the sidebar.
204
	 */
205
	public static function get_widgets_in_sidebar( $sidebar ) {
206
		$sidebars = self::get_all_sidebars();
207
208
209
		if ( ! $sidebars || ! is_array( $sidebars ) ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $sidebars of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
210
			return null;
211
		}
212
		if ( ! $sidebars[ $sidebar ] && array_key_exists( $sidebar, $sidebars ) ) {
213
			return array();
214
		}
215
		return $sidebars[ $sidebar ];
216
	}
217
218
	/**
219
	 * Return an associative array of all registered sidebars for this theme,
220
	 * active and inactive, including the hidden disabled widgets sidebar (keyed
221
	 * by `wp_inactive_widgets`). Each sidebar is keyed by the ID of the sidebar
222
	 * and its value is an array of widget IDs for that sidebar.
223
	 *
224
	 * @return array An associative array of all sidebars and their widget IDs.
225
	 */
226
	public static function get_all_sidebars() {
227
		$sidebars_widgets = self::get_sidebars_widgets();
228
229
		if ( ! is_array( $sidebars_widgets ) ) {
230
			return array();
231
		}
232
		return $sidebars_widgets;
233
	}
234
235
	/**
236
	 * Return an associative array of all active sidebars for this theme, Each
237
	 * sidebar is keyed by the ID of the sidebar and its value is an array of
238
	 * widget IDs for that sidebar.
239
	 *
240
	 * @return array An associative array of all active sidebars and their widget IDs.
241
	 */
242
	public static function get_active_sidebars() {
243
		$sidebars = array();
244
		foreach ( self::get_all_sidebars() as $sidebar => $widgets ) {
245
			if ( 'wp_inactive_widgets' === $sidebar || ! isset( $widgets ) || ! is_array( $widgets ) ) {
246
				continue;
247
			}
248
			$sidebars[ $sidebar ] = $widgets;
249
		}
250
		return $sidebars;
251
	}
252
253
	/**
254
	 * Activates a widget in a sidebar. Does not validate that the sidebar exists,
255
	 * so please do that first. Also does not save the widget's settings. Please
256
	 * do that with `set_widget_settings`.
257
	 *
258
	 * If position is not set, it will be set to the next available position.
259
	 *
260
	 * @param string         $widget_id The newly-formed id of the widget to be added.
261
	 * @param string         $sidebar   The id of the sidebar where the widget will be added.
262
	 * @param string|integer $position  (Optional) The position within the sidebar where the widget will be added.
263
	 *
264
	 * @return bool
265
	 */
266
	public static function add_widget_to_sidebar( $widget_id, $sidebar, $position ) {
267
		return self::move_widget_to_sidebar( array( 'id' => $widget_id ), $sidebar, $position );
268
	}
269
270
	/**
271
	 * Removes a widget from a sidebar. Does not validate that the sidebar exists
272
	 * or remove any settings from the widget, so please do that separately.
273
	 *
274
	 * @param array $widget The widget to be removed.
275
	 */
276
	public static function remove_widget_from_sidebar( $widget ) {
277
		$sidebars_widgets = self::get_sidebars_widgets();
278
		// Remove the widget from its old location and reflow the positions of the remaining widgets.
279
		array_splice( $sidebars_widgets[ $widget['sidebar'] ], $widget['position'], 1 );
280
281
		update_option( 'sidebars_widgets', $sidebars_widgets );
282
	}
283
284
	/**
285
	 * Moves a widget to a sidebar. Does not validate that the sidebar exists,
286
	 * so please do that first. Also does not save the widget's settings. Please
287
	 * do that with `set_widget_settings`. The first argument should be a
288
	 * widget as returned by `format_widget` including `id`, `sidebar`, and
289
	 * `position`.
290
	 *
291
	 * If $position is not set, it will be set to the next available position.
292
	 *
293
	 * Can be used to add a new widget to a sidebar if
294
	 * $widget['sidebar'] === NULL
295
	 *
296
	 * Can be used to move a widget within a sidebar as well if
297
	 * $widget['sidebar'] === $sidebar.
298
	 *
299
	 * @param array          $widget   The widget to be moved (see format_widget).
300
	 * @param string         $sidebar  The sidebar where this widget will be moved.
301
	 * @param string|integer $position (Optional) The position where this widget will be moved in the sidebar.
302
	 *
303
	 * @return bool
304
	 */
305
	public static function move_widget_to_sidebar( $widget, $sidebar, $position ) {
306
		$sidebars_widgets = self::get_sidebars_widgets();
307
308
		// If a position is passed and the sidebar isn't empty,
309
		// splice the widget into the sidebar, update the sidebar option, and return the result
310
		if ( isset( $widget['sidebar'] ) && isset( $widget['position'] ) ) {
311
			array_splice( $sidebars_widgets[ $widget['sidebar'] ], $widget['position'], 1 );
312
		}
313
314
		// Sometimes an existing empty sidebar is NULL, so initialize it.
315
		if ( array_key_exists( $sidebar, $sidebars_widgets ) && ! is_array( $sidebars_widgets[ $sidebar ] ) ) {
316
			$sidebars_widgets[ $sidebar ] = array();
317
		}
318
319
		// If no position is passed, set one from items in sidebar
320
		if ( ! isset( $position ) ) {
321
			$position = 0;
322
			$last_position = self::get_last_position_in_sidebar( $sidebar );
323
			if ( isset( $last_position ) && is_numeric( $last_position ) ) {
324
				$position = $last_position + 1;
325
			}
326
		}
327
328
		// Add the widget to the sidebar and reflow the positions of the other widgets.
329
		if ( empty( $sidebars_widgets[ $sidebar ] ) ) {
330
			$sidebars_widgets[ $sidebar ][] = $widget['id'];
331
		} else {
332
			array_splice( $sidebars_widgets[ $sidebar ], (int)$position, 0, $widget['id'] );
333
		}
334
335
		set_theme_mod( 'sidebars_widgets', array( 'time' => time(), 'data' => $sidebars_widgets ) );
336
		return update_option( 'sidebars_widgets', $sidebars_widgets );
337
	}
338
339
	/**
340
	 * Return an integer containing the largest position number in a sidebar or
341
	 * null if there are no widgets in that sidebar.
342
	 *
343
	 * @param string $sidebar The id of a sidebar.
344
	 *
345
	 * @return integer|null The last index position of a widget in that sidebar.
346
	 */
347
	public static function get_last_position_in_sidebar( $sidebar ) {
348
		$widgets = self::get_widgets_in_sidebar( $sidebar );
349
		if ( ! $widgets ) {
350
			return null;
351
		}
352
		$last_position = 0;
353
		foreach ( $widgets as $widget_id ) {
354
			$widget = self::get_widget_by_id( $widget_id );
355
			if ( intval( $widget['position'] ) > intval( $last_position ) ) {
356
				$last_position = intval( $widget['position'] );
357
			}
358
		}
359
		return $last_position;
360
	}
361
362
	/**
363
	 * Saves settings for a widget. Does not add that widget to a sidebar. Please
364
	 * do that with `move_widget_to_sidebar` first. Will merge the settings of
365
	 * any existing widget with the same `$widget_id`.
366
	 *
367
	 * @param string $widget_id The id of a widget.
368
	 * @param array $settings An associative array of settings to merge with any existing settings on this widget.
369
	 *
370
	 * @return boolean|WP_Error True if update was successful.
371
	 */
372
	public static function set_widget_settings( $widget_id, $settings ) {
373
		$widget_option_name = self::get_widget_option_name( $widget_id );
374
		$widget_settings = get_option( $widget_option_name );
375
		$instance_key = self::get_widget_instance_key( $widget_id );
376
		$old_settings = $widget_settings[ $instance_key ];
377
378
		if ( ! $settings = self::sanitize_widget_settings( $widget_id, $settings, $old_settings ) ) {
379
			return new WP_Error( 'invalid_data', 'Update failed.', 500 );
380
		}
381
		if ( is_array( $old_settings ) ) {
382
			// array_filter prevents empty arguments from replacing existing ones
383
			$settings = wp_parse_args( array_filter( $settings ), $old_settings );
384
		}
385
386
		$widget_settings[ $instance_key ] = $settings;
387
388
		return update_option( $widget_option_name, $widget_settings );
389
	}
390
391
	/**
392
	 * Sanitize an associative array for saving.
393
	 *
394
	 * @param string $widget_id The id of a widget.
395
	 * @param array $settings A widget settings array.
396
	 * @param array $old_settings The existing widget settings array.
397
	 *
398
	 * @return array|false The settings array sanitized by `WP_Widget::update` or false if sanitization failed.
399
	 */
400
	private static function sanitize_widget_settings( $widget_id, $settings, $old_settings ) {
401
		if ( ! $widget = self::get_registered_widget_object( self::get_widget_id_base( $widget_id ) ) ) {
402
			return false;
403
		}
404
		$new_settings = $widget->update( $settings, $old_settings );
405
		if ( ! is_array( $new_settings ) ) {
406
			return false;
407
		}
408
		return $new_settings;
409
	}
410
411
	/**
412
	 * Deletes settings for a widget. Does not remove that widget to a sidebar. Please
413
	 * do that with `remove_widget_from_sidebar` first.
414
	 *
415
	 * @param array $widget The widget which will have its settings removed (see format_widget).
416
	 */
417
	public static function remove_widget_settings( $widget ) {
418
		$widget_option_name = self::get_widget_option_name( $widget['id'] );
419
		$widget_settings = get_option( $widget_option_name );
420
		unset( $widget_settings[ self::get_widget_instance_key( $widget['id'] ) ] );
421
		update_option( $widget_option_name, $widget_settings );
422
	}
423
424
	/**
425
	 * Update a widget's settings, sidebar, and position. Returns the (updated)
426
	 * formatted widget if successful or a WP_Error if it fails.
427
	 *
428
	 * @param string $widget_id The id of a widget to update.
429
	 * @param string $sidebar (Optional) A sidebar to which this widget will be moved.
430
	 * @param string|integer (Optional) A new position to which this widget will be moved within its new or existing sidebar.
431
	 * @param array|object|string $settings Settings to merge with the existing settings of the widget (will be passed through `decode_settings`).
432
	 *
433
	 * @return array|WP_Error The newly added widget as an associative array with all the above properties.
434
	 */
435
	public static function update_widget( $widget_id, $sidebar, $position, $settings ) {
436
		$settings = self::decode_settings( $settings );
437
		if ( isset( $settings ) && ! is_array( $settings ) ) {
438
			return new WP_Error( 'invalid_data', 'Invalid settings', 400 );
439
		}
440
		// Default to an empty array if nothing is specified.
441
		if ( ! is_array( $settings ) ) {
442
			$settings = array();
443
		}
444
		$widget = self::get_widget_by_id( $widget_id );
445
		if ( ! $widget ) {
446
			return new WP_Error( 'not_found', 'No widget found.', 400 );
447
		}
448
		if ( ! $sidebar ) {
449
			$sidebar = $widget['sidebar'];
450
		}
451
		if ( ! isset( $position ) ) {
452
			$position = $widget['position'];
453
		}
454
		if ( ! is_numeric( $position ) ) {
455
			return new WP_Error( 'invalid_data', 'Invalid position', 400 );
456
		}
457
		$widgets_in_sidebar = self::get_widgets_in_sidebar( $sidebar );
458
		if ( ! isset( $widgets_in_sidebar ) ) {
459
			return new WP_Error( 'invalid_data', 'No such sidebar exists', 400 );
460
		}
461
		self::move_widget_to_sidebar( $widget, $sidebar, $position );
462
		$widget_save_status = self::set_widget_settings( $widget_id, $settings );
463
		if ( is_wp_error( $widget_save_status ) ) {
464
			return $widget_save_status;
465
		}
466
		return self::get_widget_by_id( $widget_id );
467
	}
468
469
	/**
470
	 * Deletes a widget entirely including all its settings. Returns a WP_Error if
471
	 * the widget could not be found. Otherwise returns an empty array.
472
	 *
473
	 * @param string $widget_id The id of a widget to delete. (eg: 'text-2')
474
	 *
475
	 * @return array|WP_Error An empty array if successful.
476
	 */
477
	public static function delete_widget( $widget_id ) {
478
		$widget = self::get_widget_by_id( $widget_id );
479
		if ( ! $widget ) {
480
			return new WP_Error( 'not_found', 'No widget found.', 400 );
481
		}
482
		self::remove_widget_from_sidebar( $widget );
483
		self::remove_widget_settings( $widget );
484
		return array();
485
	}
486
487
	/**
488
	 * Return an array of settings. The input can be either an object, a JSON
489
	 * string, or an array.
490
	 *
491
	 * @param array|string|object $settings The settings of a widget as passed into the API.
492
	 *
493
	 * @return array Decoded associative array of settings.
494
	 */
495
	public static function decode_settings( $settings ) {
496
		// Treat as string in case JSON was passed
497
		if ( is_object( $settings ) && property_exists( $settings, 'scalar' ) ) {
498
			$settings = $settings->scalar;
499
		}
500
		if ( is_object( $settings ) ) {
501
			$settings = (array) $settings;
502
		}
503
		// Attempt to decode JSON string
504
		if ( is_string( $settings ) ) {
505
			$settings = (array) json_decode( $settings );
506
		}
507
		return $settings;
508
	}
509
510
	/**
511
	 * Activate a new widget.
512
	 *
513
	 * @param string $id_base The id_base of the new widget (eg: 'text')
514
	 * @param string $sidebar The id of the sidebar where this widget will go. Dependent on theme. (eg: 'sidebar-1')
515
	 * @param string|integer $position (Optional) The position of the widget in the sidebar. Defaults to the last position.
516
	 * @param array|object|string $settings (Optional) An associative array of settings for this widget (will be passed through `decode_settings`). Varies by widget.
517
	 *
518
	 * @return array|WP_Error The newly added widget as an associative array with all the above properties except 'id_base' replaced with the generated 'id'.
519
	 */
520
	public static function activate_widget( $id_base, $sidebar, $position, $settings ) {
521
		if ( ! isset( $id_base ) || ! self::validate_id_base( $id_base ) ) {
522
			return new WP_Error( 'invalid_data', 'Invalid ID base', 400 );
523
		}
524
525
		if ( ! isset( $sidebar ) ) {
526
			return new WP_Error( 'invalid_data', 'No sidebar provided', 400 );
527
		}
528
529
		if ( isset( $position ) && ! is_numeric( $position ) ) {
530
			return new WP_Error( 'invalid_data', 'Invalid position', 400 );
531
		}
532
533
		$settings = self::decode_settings( $settings );
534
		if ( isset( $settings ) && ! is_array( $settings ) ) {
535
			return new WP_Error( 'invalid_data', 'Invalid settings', 400 );
536
		}
537
538
		// Default to an empty array if nothing is specified.
539
		if ( ! is_array( $settings ) ) {
540
			$settings = array();
541
		}
542
543
		$widget_counter = 1 + self::get_last_widget_instance_key_with_id_base( $id_base );
544
		$widget_id = $id_base . '-' . $widget_counter;
545
		if ( 0 >= $widget_counter ) {
546
			return new WP_Error( 'invalid_data', 'Error creating widget ID' . $widget_id, 500 );
547
		}
548
		if ( self::get_widget_by_id( $widget_id ) ) {
549
			return new WP_Error( 'invalid_data', 'Widget ID already exists', 500 );
550
		}
551
552
		self::add_widget_to_sidebar( $widget_id, $sidebar, $position );
553
		$widget_save_status = self::set_widget_settings( $widget_id, $settings );
554
		if ( is_wp_error( $widget_save_status ) ) {
555
			return $widget_save_status;
556
		}
557
558
		// Add a Tracks event for non-Headstart activity.
559
		if ( ! defined( 'HEADSTART' ) ) {
560
			jetpack_require_lib( 'tracks/client' );
561
			jetpack_tracks_record_event( wp_get_current_user(), 'wpcom_widgets_activate_widget', array(
562
				'widget' => $id_base,
563
				'settings' => json_encode( $settings ),
564
			) );
565
		}
566
567
		return self::get_widget_by_id( $widget_id );
568
	}
569
570
	/**
571
	 * Activate an array of new widgets. Like calling `activate_widget` multiple times.
572
	 *
573
	 * @param array $widgets An array of widget arrays. Each sub-array must be of the format required by `activate_widget`.
574
	 *
575
	 * @return array|WP_Error The newly added widgets in the form returned by `get_all_widgets`.
576
	 */
577
	public static function activate_widgets( $widgets ) {
578
		if ( ! is_array( $widgets ) ) {
579
			return new WP_Error( 'invalid_data', 'Invalid widgets', 400 );
580
		}
581
582
		$added_widgets = array();
583
584
		foreach( $widgets as $widget ) {
585
			$added_widgets[] = self::activate_widget( $widget['id_base'], $widget['sidebar'], $widget['position'], $widget['settings'] );
586
		}
587
588
		return $added_widgets;
589
	}
590
591
	/**
592
	 * Return the last instance key (integer) of an existing widget matching
593
	 * `$id_base`. So if you pass in `text`, and there is a widget with the id
594
	 * `text-2`, this function will return `2`.
595
	 *
596
	 * @param string $id_base The id_base of a type of widget. (eg: 'rss')
597
	 *
598
	 * @return integer The last instance key of that type of widget.
599
	 */
600
	public static function get_last_widget_instance_key_with_id_base( $id_base ) {
601
		$similar_widgets = self::get_widgets_with_id_base( $id_base );
602
603
		if ( ! empty( $similar_widgets ) ) {
604
			// If the last widget with the same name is `text-3`, we want `text-4`
605
			usort( $similar_widgets, __CLASS__ . '::sort_widgets' );
606
607
			$last_widget = array_pop( $similar_widgets );
608
			$last_val = intval( self::get_widget_instance_key( $last_widget['id'] ) );
609
610
			return $last_val;
611
		}
612
613
		return 0;
614
	}
615
616
	/**
617
	 * Method used to sort widgets
618
	 *
619
	 * @since 5.4
620
	 *
621
	 * @param array $a
622
	 * @param array $b
623
	 *
624
	 * @return int
625
	 */
626
	public static function sort_widgets( $a, $b ) {
627
		$a_val = intval( self::get_widget_instance_key( $a['id'] ) );
628
		$b_val = intval( self::get_widget_instance_key( $b['id'] ) );
629
		if ( $a_val > $b_val ) {
630
			return 1;
631
		}
632
		if ( $a_val < $b_val ) {
633
			return -1;
634
		}
635
		return 0;
636
	}
637
638
	/**
639
	 * Retrieve a given widget object instance by ID base (eg. 'text' or 'archives').
640
	 *
641
	 * @param string $id_base The id_base of a type of widget.
642
	 *
643
	 * @return WP_Widget|false The found widget object or false if the id_base was not found.
644
	 */
645
	public static function get_registered_widget_object( $id_base ) {
646
		if ( ! $id_base ) {
647
			return false;
648
		}
649
650
		// Get all of the registered widgets.
651
		global $wp_widget_factory;
652
		if ( ! isset( $wp_widget_factory ) ) {
653
			return false;
654
		}
655
656
		$registered_widgets = $wp_widget_factory->widgets;
657
		if ( empty( $registered_widgets ) ) {
658
			return false;
659
		}
660
661
		foreach ( array_values( $registered_widgets ) as $registered_widget_object ) {
662
			if ( $registered_widget_object->id_base === $id_base ) {
663
				return $registered_widget_object;
664
			}
665
		}
666
		return false;
667
	}
668
669
	/**
670
	 * Validate a given widget ID base (eg. 'text' or 'archives').
671
	 *
672
	 * @param string $id_base The id_base of a type of widget.
673
	 *
674
	 * @return boolean True if the widget is of a known type.
675
	 */
676
	public static function validate_id_base( $id_base ) {
677
		return ( false !== self::get_registered_widget_object( $id_base ) );
678
	}
679
}
680