为什么print_r没有显示WP_User对象的所有属性?

If I do this I get many properties shown, including display_name, but the first and last names are NOT shown

$user = get_userdata( 4 );
print_r( $user );

However they clearly exist because if I immediately afterwards do this, I am shown the proper last name. The Wordpress docs also mention last_name as being a property.

echo $user->last_name;

So why doesn't print_r show all of the properties? This casts a lot of doubt on being able to use print_r to discover information.

last_name is not a real property of WP_User, but it's made available via magic methods for backwards compatibility.

It's listed as a public property in the docs, but this is a little misleading. More accurately, you can access it as a public property. But when you look at the code, it's being retrieved and set using magic methods.

Proof from the Code

Here's an excerpt of the most relevant code, showing how Wordpress actually keeps a list of several of these backward-compatibility properties, including last_name, in a private, static property called back_compat_keys. When the user requests one of these properties, the magic method __get is called. The magic method uses get_user_meta() to actually retrieve the data for that property. In other words, the data isn't actually stored in the WP_User object; Wordpress just lets you pretend that it is, and it only fetches it when explicitly requested. Here's the code:

<?php
class WP_User {
    // ...
        /**
         * @static
         * @since 3.3.0
         * @access private
         * @var array
         */
        private static $back_compat_keys;

        public function __construct( $id = 0, $name = '', $blog_id = '' ) {
                if ( ! isset( self::$back_compat_keys ) ) {
                        $prefix = $GLOBALS['wpdb']->prefix;
                        self::$back_compat_keys = array(
                                'user_firstname' => 'first_name',
                                'user_lastname' => 'last_name',
                                'user_description' => 'description',
                                'user_level' => $prefix . 'user_level',
                                $prefix . 'usersettings' => $prefix . 'user-settings',
                                $prefix . 'usersettingstime' => $prefix . 'user-settings-time',
                        );
                }

                // ...
        }

        // ...

        /**
         * Magic method for accessing custom fields.
         *
         * @since 3.3.0
         * @access public
         *
         * @param string $key User meta key to retrieve.
         * @return mixed Value of the given user meta key (if set). If `$key` is 'id', the user ID.
         */
        public function __get( $key ) {
                // ...

                if ( isset( $this->data->$key ) ) {
                        $value = $this->data->$key;
                } else {
                        if ( isset( self::$back_compat_keys[ $key ] ) )
                                $key = self::$back_compat_keys[ $key ];
                        $value = get_user_meta( $this->ID, $key, true );
                }

                // ...

                return $value;
        }

        /**
         * Magic method for setting custom user fields.
         *
         * This method does not update custom fields in the database. It only stores
         * the value on the WP_User instance.
         *
         * @since 3.3.0
         * @access public
         *
         * @param string $key   User meta key.
         * @param mixed  $value User meta value.
         */
        public function __set( $key, $value ) {
                if ( 'id' == $key ) {
                        _deprecated_argument( 'WP_User->id', '2.1.0',
                                sprintf(
                                        /* translators: %s: WP_User->ID */
                                        __( 'Use %s instead.' ),
                                        '<code>WP_User->ID</code>'
                                )
                        );
                        $this->ID = $value;
                        return;
                }

                $this->data->$key = $value;
        }

        /**
         * Magic method for unsetting a certain custom field.
         *
         * @since 4.4.0
         * @access public
         *
         * @param string $key User meta key to unset.
         */
        public function __unset( $key ) {
                // ...

                if ( isset( $this->data->$key ) ) {
                        unset( $this->data->$key );
                }

                if ( isset( self::$back_compat_keys[ $key ] ) ) {
                        unset( self::$back_compat_keys[ $key ] );
                }
        }

        // ...
}