Users API

WP_User, get_user_by(), wp_insert_user() and user meta: reading and writing WordPress users from PHP.

Syntaxget_user_by( string $field, int|string $value ): WP_User|false

A WordPress user is one row in wp_users plus any number of rows in wp_usermeta. The Users API wraps both in the WP_User class so you never write that join by hand, and so that hooks fire when a user changes.

Reading

get_user_by( $field, $value ) accepts 'id', 'ID', 'slug', 'email' or 'login' and returns a WP_User or false. get_userdata( $user_id ) is shorthand for get_user_by( 'id', $user_id ) — the same call with the same return type. wp_get_current_user() gives the user for the current request, but it always returns an object: when nobody is logged in you get a WP_User whose ID is 0, so branch on is_user_logged_in(), not on the return value.

Writing

wp_insert_user( $userdata ) returns an int ID or a WP_Error. Pass an ID key and it updates instead of creating. wp_create_user( $username, $password, $email ) is shorthand for the same function with just those three keys; wp_update_user() is the same call again, requiring ID, and it hashes a plain-text user_pass for you.

Meta

Anything not a column in wp_users is meta: get_user_meta( $user_id, $key, $single ), update_user_meta( $user_id, $key, $value ). The meta_input array on wp_insert_user() writes meta in the same call. WP_User falls through to meta for any property it does not recognise, so a typo like $user->frist_name returns an empty string rather than a warning.

Example

Example · php
<?php
/**
 * Create a customer account and store the CRM id against it.
 *
 * @return WP_User|WP_Error
 */
function acme_create_customer( string $email, string $name, string $crm_id ) {
	if ( email_exists( $email ) ) {
		return new WP_Error( 'acme_exists', 'That email already has an account.' );
	}

	// For a NEW user, user_pass is plain text and wp_insert_user() hashes it.
	$user_id = wp_insert_user( array(
		'user_login'   => sanitize_user( current( explode( '@', $email ) ), true ),
		'user_email'   => $email,
		'user_pass'    => wp_generate_password( 24 ),
		'display_name' => $name,
		'role'         => 'customer',   // the role must already exist
		'meta_input'   => array(        // written to wp_usermeta in the same call
			'acme_crm_id' => $crm_id,
		),
	) );

	// wp_insert_user() returns int|WP_Error, never false.
	if ( is_wp_error( $user_id ) ) {
		return $user_id;
	}

	// Shorthand for get_user_by( 'id', $user_id ) — identical result.
	return get_userdata( $user_id );
}

When to use it

  • Provision accounts from an external CRM or billing system on webhook.
  • Store per-user preferences that do not belong in wp_options.
  • Build a front-end profile editor without touching wp_users directly.
  • Look up a user by email before deciding to create or update them.

More examples

Reading the current user safely

Shows the fall-through from column to meta on WP_User, and the shorthand/longhand pair for a single meta value. Reach for the explicit get_user_meta() call in code others will read: the magic property gives no clue whether the value is a column or meta, and returns an empty string for keys that do not exist.

Example · php
$user = wp_get_current_user();

// wp_get_current_user() always returns a WP_User object. A logged-out
// visitor gets one with ID 0, so this test is the reliable one.
if ( ! is_user_logged_in() ) {
	return;
}

// Columns of wp_users come straight off the object.
$email = $user->user_email;
$login = $user->user_login;

// So does meta, via the same magic property — these two lines are equal:
$region = $user->acme_region;
$region = get_user_meta( $user->ID, 'acme_region', true ); // $single = true

// $single = false returns an array of every stored value for that key.
$badges = get_user_meta( $user->ID, 'acme_badge', false );

Updating an existing user

The normal path for editing a user from a front-end profile form or a sync job. Use it rather than a direct wpdb UPDATE so that profile_update and the password-change hooks fire, and so caches are invalidated.

Example · php
// wp_update_user() takes the same keys as wp_insert_user() but REQUIRES an ID.
// Unlike wp_insert_user(), it hashes a plain-text user_pass for you.
$result = wp_update_user( array(
	'ID'          => $user_id,
	'user_email'  => '[email protected]',
	'description' => 'Handles EMEA accounts.',
	'user_pass'   => $plain_text_password, // omit the key to leave it unchanged
) );

if ( is_wp_error( $result ) ) {
	error_log( 'acme: ' . $result->get_error_message() );
	return;
}

// Meta is not part of the update payload unless you pass meta_input.
update_user_meta( $user_id, 'acme_last_synced', gmdate( 'Y-m-d H:i:s' ) );

Deleting a user and reassigning their posts

Account closure and GDPR erasure flows. The require_once matters: wp_delete_user() is an admin-only include, and calling it from a REST route or a WP-Cron job without loading the file is a fatal error.

Example · php
// wp_delete_user() lives in wp-admin, so load it on the front end or in cron.
if ( ! function_exists( 'wp_delete_user' ) ) {
	require_once ABSPATH . 'wp-admin/includes/user.php';
}

// Second argument reassigns the user's posts and links to another user ID.
// Omit it and the posts are deleted with the user.
wp_delete_user( $user_id, $archive_user_id );

Discussion

  • Be the first to comment on this lesson.