Skip to content

Commit 297771e

Browse files
committed
Users: Ensure user data supplied to wp_insert_user() is normalized to an array.
This fixes an issue where PHPStan hangs when analyzing the containing `users.php` file. Developed in #10953 Follow-up to [60650]. Props westonruter, justlevine, peterwilsoncc. See #64238, #61175. git-svn-id: https://develop.svn.wordpress.org/trunk@61656 602fd350-edb4-49c9-b593-d223f7449a82
1 parent e1d2179 commit 297771e

2 files changed

Lines changed: 178 additions & 1 deletion

File tree

src/wp-includes/user.php

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2209,6 +2209,44 @@ function wp_insert_user( $userdata ) {
22092209
$userdata = get_object_vars( $userdata );
22102210
} elseif ( $userdata instanceof WP_User ) {
22112211
$userdata = $userdata->to_array();
2212+
} elseif ( $userdata instanceof Traversable ) {
2213+
$userdata = iterator_to_array( $userdata );
2214+
} elseif ( $userdata instanceof ArrayAccess ) {
2215+
$userdata_obj = $userdata;
2216+
$userdata = array();
2217+
foreach (
2218+
array(
2219+
'ID',
2220+
'user_pass',
2221+
'user_login',
2222+
'user_nicename',
2223+
'user_url',
2224+
'user_email',
2225+
'display_name',
2226+
'nickname',
2227+
'first_name',
2228+
'last_name',
2229+
'description',
2230+
'rich_editing',
2231+
'syntax_highlighting',
2232+
'comment_shortcuts',
2233+
'admin_color',
2234+
'use_ssl',
2235+
'user_registered',
2236+
'user_activation_key',
2237+
'spam',
2238+
'show_admin_bar_front',
2239+
'role',
2240+
'locale',
2241+
'meta_input',
2242+
) as $key
2243+
) {
2244+
if ( isset( $userdata_obj[ $key ] ) ) {
2245+
$userdata[ $key ] = $userdata_obj[ $key ];
2246+
}
2247+
}
2248+
} else {
2249+
$userdata = (array) $userdata;
22122250
}
22132251

22142252
// Are we updating or creating?
@@ -2244,7 +2282,7 @@ function wp_insert_user( $userdata ) {
22442282
$user_pass = wp_hash_password( $userdata['user_pass'] );
22452283
}
22462284

2247-
$sanitized_user_login = sanitize_user( $userdata['user_login'], true );
2285+
$sanitized_user_login = sanitize_user( $userdata['user_login'] ?? '', true );
22482286

22492287
/**
22502288
* Filters a username after it has been sanitized.

tests/phpunit/tests/user.php

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -982,6 +982,145 @@ public function test_illegal_user_logins_single( $user_login ) {
982982
$this->assertInstanceOf( 'WP_User', $user );
983983
}
984984

985+
/**
986+
* @ticket 61175
987+
* @covers ::wp_insert_user
988+
*/
989+
public function test_wp_insert_user_with_null() {
990+
// Note: $this->expectWarning() is deprecated and will be removed in PHPUnit 10.
991+
$warnings = array();
992+
set_error_handler(
993+
static function ( int $errno, string $errstr ) use ( &$warnings ) {
994+
$warnings[] = compact( 'errno', 'errstr' );
995+
return true;
996+
},
997+
E_USER_WARNING
998+
);
999+
$user = wp_insert_user( null );
1000+
restore_error_handler();
1001+
1002+
$this->assertCount( 1, $warnings, 'Expected one warning.' );
1003+
$this->assertWPError( $user );
1004+
$this->assertSame( 'empty_user_login', $user->get_error_code() );
1005+
}
1006+
1007+
/**
1008+
* @ticket 61175
1009+
* @covers ::wp_insert_user
1010+
*/
1011+
public function test_wp_insert_user_with_stdclass() {
1012+
$data = array(
1013+
'user_login' => 'new-admin',
1014+
'user_pass' => 'better-password',
1015+
);
1016+
$user_id = wp_insert_user( (object) $data );
1017+
$this->assertIsInt( $user_id, 'Expected user to be created.' );
1018+
$user = new WP_User( $user_id );
1019+
$this->assertSame( $data['user_login'], $user->user_login );
1020+
}
1021+
1022+
/**
1023+
* @ticket 61175
1024+
* @covers ::wp_insert_user
1025+
*/
1026+
public function test_wp_insert_user_with_wp_user() {
1027+
$username = 'new-admin';
1028+
$user = new WP_User();
1029+
$user->user_login = $username;
1030+
$user->user_pass = 'better-password';
1031+
1032+
$user_id = wp_insert_user( $user );
1033+
$this->assertIsInt( $user_id, 'Expected user to be created.' );
1034+
$user = new WP_User( $user_id );
1035+
$this->assertSame( $username, $user->user_login );
1036+
}
1037+
1038+
/**
1039+
* @ticket 61175
1040+
* @covers ::wp_insert_user
1041+
*/
1042+
public function test_wp_insert_user_with_traversable() {
1043+
$internal_data = array(
1044+
'user_login' => 'new-admin',
1045+
'user_pass' => 'better-password',
1046+
);
1047+
1048+
$array_access_user = new class( $internal_data ) implements ArrayAccess, IteratorAggregate {
1049+
private array $data;
1050+
1051+
public function __construct( array $data ) {
1052+
$this->data = $data;
1053+
}
1054+
1055+
public function offsetExists( $offset ): bool {
1056+
return isset( $this->data[ $offset ] );
1057+
}
1058+
1059+
#[\ReturnTypeWillChange]
1060+
public function offsetGet( $offset ) {
1061+
return $this->data[ $offset ];
1062+
}
1063+
1064+
public function offsetSet( $offset, $value ): void {
1065+
$this->data[ $offset ] = $value;
1066+
}
1067+
1068+
public function offsetUnset( $offset ): void {
1069+
unset( $this->data[ $offset ] );
1070+
}
1071+
1072+
public function getIterator(): ArrayIterator {
1073+
return new ArrayIterator( $this->data );
1074+
}
1075+
};
1076+
1077+
$user_id = wp_insert_user( $array_access_user );
1078+
$this->assertIsInt( $user_id, 'Expected user to be created.' );
1079+
$user = new WP_User( $user_id );
1080+
$this->assertSame( $internal_data['user_login'], $user->user_login );
1081+
}
1082+
1083+
/**
1084+
* @ticket 61175
1085+
* @covers ::wp_insert_user
1086+
*/
1087+
public function test_wp_insert_user_with_only_array_access() {
1088+
$internal_data = array(
1089+
'user_login' => 'new-admin',
1090+
'user_pass' => 'better-password',
1091+
);
1092+
1093+
$array_access_user = new class( $internal_data ) implements ArrayAccess {
1094+
private array $data;
1095+
1096+
public function __construct( array $data ) {
1097+
$this->data = $data;
1098+
}
1099+
1100+
public function offsetExists( $offset ): bool {
1101+
return isset( $this->data[ $offset ] );
1102+
}
1103+
1104+
#[\ReturnTypeWillChange]
1105+
public function offsetGet( $offset ) {
1106+
return $this->data[ $offset ];
1107+
}
1108+
1109+
public function offsetSet( $offset, $value ): void {
1110+
$this->data[ $offset ] = $value;
1111+
}
1112+
1113+
public function offsetUnset( $offset ): void {
1114+
unset( $this->data[ $offset ] );
1115+
}
1116+
};
1117+
1118+
$user_id = wp_insert_user( $array_access_user );
1119+
$this->assertIsInt( $user_id, 'Expected user to be created.' );
1120+
$user = new WP_User( $user_id );
1121+
$this->assertSame( $internal_data['user_login'], $user->user_login );
1122+
}
1123+
9851124
/**
9861125
* @ticket 27317
9871126
* @dataProvider data_illegal_user_logins

0 commit comments

Comments
 (0)