Make WordPress Core

Changeset 61656


Ignore:
Timestamp:
02/17/2026 05:48:04 AM (6 weeks ago)
Author:
westonruter
Message:

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 https://github.com/WordPress/wordpress-develop/pull/10953

Follow-up to [60650].

Props westonruter, justlevine, peterwilsoncc.
See #64238, #61175.

Location:
trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/user.php

    r61644 r61656  
    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
     
    22452283    }
    22462284
    2247     $sanitized_user_login = sanitize_user( $userdata['user_login'], true );
     2285    $sanitized_user_login = sanitize_user( $userdata['user_login'] ?? '', true );
    22482286
    22492287    /**
  • trunk/tests/phpunit/tests/user.php

    r61210 r61656  
    984984
    985985    /**
     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
     1124    /**
    9861125     * @ticket 27317
    9871126     * @dataProvider data_illegal_user_logins
Note: See TracChangeset for help on using the changeset viewer.