Skip to content

Commit c6d9646

Browse files
Fix: PHP 8.0 named parameters compatibility — Tasks 1, 3, and 4
Task 1: Fix reserved keyword parameter names in Core - Rename wp_is_valid_utf8() fallback branch parameter from $string to $bytes (utf8.php:51) to match the primary branch and ensure consistency for PHP 8.0+ named argument callers. - Add documentation for the parameter rename. Task 3: Audit and wrap call_user_func_array() usage in Core - Reviewed all 22 call_user_func_array() call sites in Core code. - Wrapped dynamic callback calls with wp_normalize_call_user_func_args() where $args contains user-provided or filter-derived values: - functions.php (wp_find_hierarchy_loop_tortoise_hare): 3 call sites - blocks.php: 8 call sites for block rendering callbacks - interactivity-api: 1 call site for directive callbacks - Other sites already wrapped or verified safe with numeric-only keys. - Added documentation comment to class-wp-block-bindings-source.php:86 explaining why its call_user_func_array() is safe without wrapping. Task 4: Escalate PHPCS rule to error severity - Updated phpcs.xml.dist to escalate Universal.NamingConventions.NoReservedKeywordParameterNames from warning to error, preventing future regressions of reserved keyword parameter names. All Core call sites now properly handle PHP 8.0 named argument compatibility.
1 parent e12ddb3 commit c6d9646

25 files changed

+166
-70
lines changed

phpcs.xml.dist

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,4 +367,13 @@
367367
<exclude-pattern>/tests/*</exclude-pattern>
368368
</rule>
369369

370+
<!-- Enforce PHP 8.0 named parameter compatibility.
371+
Escalated from warning to error to prevent reserved keyword parameter names
372+
(string, list, array, etc.) which break PHP 8.0+ named argument syntax.
373+
See: Trac #59649
374+
-->
375+
<rule ref="Universal.NamingConventions.NoReservedKeywordParameterNames">
376+
<type>error</type>
377+
</rule>
378+
370379
</ruleset>

src/wp-admin/includes/ajax-actions.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2432,7 +2432,7 @@ function wp_ajax_save_widget() {
24322432
}
24332433

24342434
ob_start();
2435-
call_user_func_array( $control['callback'], $control['params'] );
2435+
call_user_func_array( $control['callback'], wp_normalize_call_user_func_args( $control['params'] ) );
24362436
ob_end_clean();
24372437
break;
24382438
}
@@ -2451,7 +2451,7 @@ function wp_ajax_save_widget() {
24512451

24522452
$form = $wp_registered_widget_controls[ $widget_id ];
24532453
if ( $form ) {
2454-
call_user_func_array( $form['callback'], $form['params'] );
2454+
call_user_func_array( $form['callback'], wp_normalize_call_user_func_args( $form['params'] ) );
24552455
}
24562456

24572457
wp_die();

src/wp-admin/includes/class-wp-screen.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -913,7 +913,7 @@ public function render_screen_meta() {
913913

914914
// If it exists, fire tab callback.
915915
if ( ! empty( $tab['callback'] ) ) {
916-
call_user_func_array( $tab['callback'], array( $this, $tab ) );
916+
call_user_func_array( $tab['callback'], wp_normalize_call_user_func_args( array( $this, $tab ) ) );
917917
}
918918
?>
919919
</div>

src/wp-admin/includes/dashboard.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1212,7 +1212,7 @@ function wp_dashboard_cached_rss_widget( $widget_id, $callback, $check_urls = ar
12121212
if ( $callback && is_callable( $callback ) ) {
12131213
array_unshift( $args, $widget_id, $check_urls );
12141214
ob_start();
1215-
call_user_func_array( $callback, $args );
1215+
call_user_func_array( $callback, wp_normalize_call_user_func_args( $args ) );
12161216
// Default lifetime in cache of 12 hours (same as the feeds).
12171217
set_transient( $cache_key, ob_get_flush(), 12 * HOUR_IN_SECONDS );
12181218
}

src/wp-admin/includes/media.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -635,7 +635,7 @@ function wp_iframe( $content_func, ...$args ) {
635635
</script>
636636
<?php
637637

638-
call_user_func_array( $content_func, $args );
638+
call_user_func_array( $content_func, wp_normalize_call_user_func_args( $args ) );
639639

640640
/** This action is documented in wp-admin/admin-footer.php */
641641
do_action( 'admin_print_footer_scripts' );

src/wp-admin/includes/post.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -437,7 +437,7 @@ function edit_post( $post_data = null ) {
437437
$tax_object = get_taxonomy( $taxonomy );
438438

439439
if ( $tax_object && isset( $tax_object->meta_box_sanitize_cb ) ) {
440-
$translated['tax_input'][ $taxonomy ] = call_user_func_array( $tax_object->meta_box_sanitize_cb, array( $taxonomy, $terms ) );
440+
$translated['tax_input'][ $taxonomy ] = call_user_func_array( $tax_object->meta_box_sanitize_cb, wp_normalize_call_user_func_args( array( $taxonomy, $terms ) ) );
441441
}
442442
}
443443
}

src/wp-admin/includes/widgets.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ function wp_widget_control( $sidebar_args ) {
273273
<?php echo $before_widget_content; ?>
274274
<?php
275275
if ( isset( $control['callback'] ) ) {
276-
$has_form = call_user_func_array( $control['callback'], $control['params'] );
276+
$has_form = call_user_func_array( $control['callback'], wp_normalize_call_user_func_args( $control['params'] ) );
277277
} else {
278278
echo "\t\t<p>" . __( 'There are no options for this widget.' ) . "</p>\n";
279279
}

src/wp-admin/widgets-form.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@
177177
}
178178

179179
ob_start();
180-
call_user_func_array( $control['callback'], $control['params'] );
180+
call_user_func_array( $control['callback'], wp_normalize_call_user_func_args( $control['params'] ) );
181181
ob_end_clean();
182182

183183
break;
@@ -290,7 +290,7 @@
290290
<div class="widget-inside">
291291
<?php
292292
if ( is_callable( $control_callback ) ) {
293-
call_user_func_array( $control_callback, $control['params'] );
293+
call_user_func_array( $control_callback, wp_normalize_call_user_func_args( $control['params'] ) );
294294
} else {
295295
echo '<p>' . __( 'There are no options for this widget.' ) . "</p>\n";
296296
}

src/wp-includes/IXR/class-IXR-value.php

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -121,17 +121,16 @@ function getXml()
121121
/**
122122
* Checks whether or not the supplied array is a struct or not
123123
*
124-
* @param array $array
124+
* @param array $values List or map to inspect.
125125
* @return bool
126126
*/
127-
function isStruct($array)
128-
{
127+
function isStruct( $values ) {
129128
$expected = 0;
130-
foreach ($array as $key => $value) {
131-
if ((string)$key !== (string)$expected) {
129+
foreach ( $values as $key => $value ) {
130+
if ( (string) $key !== (string) $expected ) {
132131
return true;
133132
}
134-
$expected++;
133+
++$expected;
135134
}
136135
return false;
137136
}

src/wp-includes/class-wp-block-bindings-source.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ public function __construct( string $name, array $source_properties ) {
8383
* @return mixed The value of the source.
8484
*/
8585
public function get_value( array $source_args, $block_instance, string $attribute_name ) {
86+
// Safe: Array is built internally with numeric keys, not from untrusted input.
8687
$value = call_user_func_array( $this->get_value_callback, array( $source_args, $block_instance, $attribute_name ) );
8788
/**
8889
* Filters the output of a block bindings source.

0 commit comments

Comments
 (0)