From 8875febb4d367834f980dd8a610faefb85314726 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Mon, 16 Feb 2026 17:55:05 -0800 Subject: [PATCH 1/4] Fix wp_insert_user() so PHPStan won't hang --- src/wp-includes/user.php | 8 +- tests/phpunit/tests/user.php | 139 +++++++++++++++++++++++++++++++++++ 2 files changed, 145 insertions(+), 2 deletions(-) diff --git a/src/wp-includes/user.php b/src/wp-includes/user.php index 41c6241ab067d..d8d1f347d74dd 100644 --- a/src/wp-includes/user.php +++ b/src/wp-includes/user.php @@ -2209,6 +2209,10 @@ function wp_insert_user( $userdata ) { $userdata = get_object_vars( $userdata ); } elseif ( $userdata instanceof WP_User ) { $userdata = $userdata->to_array(); + } elseif ( $userdata instanceof Traversable ) { + $userdata = iterator_to_array( $userdata ); + } elseif ( ! ( $userdata instanceof ArrayAccess ) ) { + $userdata = (array) $userdata; } // Are we updating or creating? @@ -2244,7 +2248,7 @@ function wp_insert_user( $userdata ) { $user_pass = wp_hash_password( $userdata['user_pass'] ); } - $sanitized_user_login = sanitize_user( $userdata['user_login'], true ); + $sanitized_user_login = sanitize_user( $userdata['user_login'] ?? '', true ); /** * Filters a username after it has been sanitized. @@ -2560,7 +2564,7 @@ function wp_insert_user( $userdata ) { $meta = apply_filters( 'insert_user_meta', $meta, $user, $update, $userdata ); $custom_meta = array(); - if ( array_key_exists( 'meta_input', $userdata ) && is_array( $userdata['meta_input'] ) && ! empty( $userdata['meta_input'] ) ) { + if ( ! empty( $userdata['meta_input'] ) && is_array( $userdata['meta_input'] ) ) { $custom_meta = $userdata['meta_input']; } diff --git a/tests/phpunit/tests/user.php b/tests/phpunit/tests/user.php index 756f600f1ab52..004b9c9898105 100644 --- a/tests/phpunit/tests/user.php +++ b/tests/phpunit/tests/user.php @@ -982,6 +982,145 @@ public function test_illegal_user_logins_single( $user_login ) { $this->assertInstanceOf( 'WP_User', $user ); } + + /** + * @ticket 61175 + * @covers ::wp_insert_user + */ + public function test_wp_insert_user_with_null() { + // Note: $this->expectWarning() is deprecated and will be removed in PHPUnit 10. + $warnings = array(); + set_error_handler( + static function ( int $errno, string $errstr ) use ( &$warnings ) { + $warnings[] = compact( 'errno', 'errstr' ); + return true; + }, + E_USER_WARNING + ); + $user = wp_insert_user( null ); + restore_error_handler(); + + $this->assertWPError( $user ); + $this->assertSame( 'empty_user_login', $user->get_error_code() ); + } + + /** + * @ticket 61175 + * @covers ::wp_insert_user + */ + public function test_wp_insert_user_with_stdclass() { + $data = array( + 'user_login' => 'new-admin', + 'user_pass' => 'better-password', + ); + $user_id = wp_insert_user( (object) $data ); + $this->assertIsInt( $user_id, 'Expected user to be created.' ); + $user = new WP_User( $user_id ); + $this->assertSame( $data['user_login'], $user->user_login ); + } + + /** + * @ticket 61175 + * @covers ::wp_insert_user + */ + public function test_wp_insert_user_with_wp_user() { + $username = 'new-admin'; + $user = new WP_User(); + $user->user_login = $username; + $user->user_pass = 'better-password'; + + $user_id = wp_insert_user( $user ); + $this->assertIsInt( $user_id, 'Expected user to be created.' ); + $user = new WP_User( $user_id ); + $this->assertSame( $username, $user->user_login ); + } + + /** + * @ticket 61175 + * @covers ::wp_insert_user + */ + public function test_wp_insert_user_with_traversable() { + $internal_data = array( + 'user_login' => 'new-admin', + 'user_pass' => 'better-password', + ); + + $array_access_user = new class( $internal_data ) implements ArrayAccess, IteratorAggregate { + private array $data; + + public function __construct( array $data ) { + $this->data = $data; + } + + public function offsetExists( $offset ): bool { + return isset( $this->data[ $offset ] ); + } + + #[\ReturnTypeWillChange] + public function offsetGet( $offset ) { + return $this->data[ $offset ]; + } + + public function offsetSet( $offset, $value ): void { + $this->data[ $offset ] = $value; + } + + public function offsetUnset( $offset ): void { + unset( $this->data[ $offset ] ); + } + + public function getIterator(): ArrayIterator { + return new ArrayIterator( $this->data ); + } + }; + + $user_id = wp_insert_user( $array_access_user ); + $this->assertIsInt( $user_id, 'Expected user to be created.' ); + $user = new WP_User( $user_id ); + $this->assertSame( $internal_data['user_login'], $user->user_login ); + } + + /** + * @ticket 61175 + * @covers ::wp_insert_user + */ + public function test_wp_insert_user_with_only_array_access() { + $internal_data = array( + 'user_login' => 'new-admin', + 'user_pass' => 'better-password', + ); + + $array_access_user = new class( $internal_data ) implements ArrayAccess { + private array $data; + + public function __construct( array $data ) { + $this->data = $data; + } + + public function offsetExists( $offset ): bool { + return isset( $this->data[ $offset ] ); + } + + #[\ReturnTypeWillChange] + public function offsetGet( $offset ) { + return $this->data[ $offset ]; + } + + public function offsetSet( $offset, $value ): void { + $this->data[ $offset ] = $value; + } + + public function offsetUnset( $offset ): void { + unset( $this->data[ $offset ] ); + } + }; + + $user_id = wp_insert_user( $array_access_user ); + $this->assertIsInt( $user_id, 'Expected user to be created.' ); + $user = new WP_User( $user_id ); + $this->assertSame( $internal_data['user_login'], $user->user_login ); + } + /** * @ticket 27317 * @dataProvider data_illegal_user_logins From 226f46d7b5c3cd22b010778f46579b66837848a3 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Mon, 16 Feb 2026 18:04:49 -0800 Subject: [PATCH 2/4] Fix handling of ArrayAccess since PHPStan still would hang --- src/wp-includes/user.php | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/src/wp-includes/user.php b/src/wp-includes/user.php index d8d1f347d74dd..5bc4d00dfb1df 100644 --- a/src/wp-includes/user.php +++ b/src/wp-includes/user.php @@ -2211,7 +2211,41 @@ function wp_insert_user( $userdata ) { $userdata = $userdata->to_array(); } elseif ( $userdata instanceof Traversable ) { $userdata = iterator_to_array( $userdata ); - } elseif ( ! ( $userdata instanceof ArrayAccess ) ) { + } elseif ( $userdata instanceof ArrayAccess ) { + $userdata_obj = $userdata; + $userdata = array(); + foreach ( + array( + 'ID', + 'user_pass', + 'user_login', + 'user_nicename', + 'user_url', + 'user_email', + 'display_name', + 'nickname', + 'first_name', + 'last_name', + 'description', + 'rich_editing', + 'syntax_highlighting', + 'comment_shortcuts', + 'admin_color', + 'use_ssl', + 'user_registered', + 'user_activation_key', + 'spam', + 'show_admin_bar_front', + 'role', + 'locale', + 'meta_input', + ) as $key + ) { + if ( isset( $userdata_obj[ $key ] ) ) { + $userdata[ $key ] = $userdata_obj[ $key ]; + } + } + } else { $userdata = (array) $userdata; } From 659a502ce155c67d9f044f18039bdb93ba2398bc Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Mon, 16 Feb 2026 18:06:00 -0800 Subject: [PATCH 3/4] Revert now-unnecessary change since $userdata is always an array --- src/wp-includes/user.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/user.php b/src/wp-includes/user.php index 5bc4d00dfb1df..327981bd173a0 100644 --- a/src/wp-includes/user.php +++ b/src/wp-includes/user.php @@ -2598,7 +2598,7 @@ function wp_insert_user( $userdata ) { $meta = apply_filters( 'insert_user_meta', $meta, $user, $update, $userdata ); $custom_meta = array(); - if ( ! empty( $userdata['meta_input'] ) && is_array( $userdata['meta_input'] ) ) { + if ( array_key_exists( 'meta_input', $userdata ) && is_array( $userdata['meta_input'] ) && ! empty( $userdata['meta_input'] ) ) { $custom_meta = $userdata['meta_input']; } From 90f779937212d7ea838befbb20be247ef811b83f Mon Sep 17 00:00:00 2001 From: Dovid Levine Date: Tue, 17 Feb 2026 04:14:38 +0200 Subject: [PATCH 4/4] Update tests/phpunit/tests/user.php Co-authored-by: Weston Ruter --- tests/phpunit/tests/user.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/phpunit/tests/user.php b/tests/phpunit/tests/user.php index 004b9c9898105..cf2c11113050a 100644 --- a/tests/phpunit/tests/user.php +++ b/tests/phpunit/tests/user.php @@ -982,7 +982,6 @@ public function test_illegal_user_logins_single( $user_login ) { $this->assertInstanceOf( 'WP_User', $user ); } - /** * @ticket 61175 * @covers ::wp_insert_user