Learn how to record the date & time when users log in to your WordPress website, and display these login dates in the admin area so you can find inactive accounts. We’ll do this with a small amount of copy-and-paste PHP code without installing any new plugins.
importantMake sure you’re using a custom child theme so you can edit functions.php.
How It’s Going to Work
WordPress is driven by a series of action and filter “hooks”, which can be thought of as events. When a page request comes in to the server, the server loads a file such as /index.php or /wp-admin/index.php, then triggers a series of hooks like “init“, “wp_body_open” and “wp_footer… until it gets to the end of the page. We’re going to pick up on the standard WordPress “authenticate” filter, which is used to process login attempts. The filter returns a WP_User object if a login is successful, or null/WP_Error if the login attempt is unsuccessful.
info The difference between a filter and an action is that a filter modifies some data and returns it. An action simply doesn’t return any data.
// The "authenticate" filter, as stated in the WordPress documentation apply_filters( 'authenticate', null|WP_User|WP_Error $user, string $username, string $password )
Our logic will work like this:
- In our “authenticate” filter handler:
- If $user is a WP_User object, the user has successfully logged-in, so…
- Get the user id of the newly logged-in user
- Get the current date/time as a string (with timezone)
- Update the user’s “last logged-in” meta data to hold the date/time
- If $user is a WP_User object, the user has successfully logged-in, so…
The “last logged-in” meta data is a custom user meta data, which is just a fancy way of saying it’s a variable we can save against a user. We can pick up this user meta data and display it in the “Users” table in the back-end by hooking two more filters:
- manage_users_columns: Modify the list of visible columns in the Users admin area. This filter is called once, before the column headers are rendered.
- manage_users_custom_column: Called for each record in the table and for each custom column. This is where we render the per-user custom column data (the date a user last logged-in).
Let’s Write the Code
In your custom child theme, create a file called wpt-record-user-logins.php and paste the following into it:
<?php /** * WP Tutorials : Record User Login times (WPTRUL) * * https://wp-tutorials.tech/refine-wordpress/record-when-users-log-in-to-wordpress/ */ defined('WPINC') || die(); const WPTRUL_META_LAST_LOGIN_WHEN = '_last_login_when'; const WPTRUL_ADMIN_LOGIN_DATE_FORMAT = 'd/m/Y @ H:i T'; /** * Record the current date and time against a user, if they've just * suvccessfully logged-in/ */ function wptrul_authenticate($user, $username, $password) { if (!is_a($user, 'WP_User')) { // ... } elseif ($user->ID <= 0) { // ... } else { // Get the current date and time. $when = new DateTime('now', wp_timezone()); // Uncomment this to put some userful diagnostics into the log, // if you run into any problems. // $diagnostics = sprintf( // 'WPTRUL login : %s (%d) %s', // $user->user_login, // $user->ID, // $when->format('Y-m-d H:i:s T') // ); // error_log($diagnostics); update_user_meta($user->ID, WPTRUL_META_LAST_LOGIN_WHEN, $when->format('Y-m-d H:i:s T')); } return $user; } add_filter('authenticate', 'wptrul_authenticate', 99, 3); /** * Add our custom column(s) to the back-end Users table. */ function wptrul_manage_users_columns($columns) { $columns[WPTRUL_META_LAST_LOGIN_WHEN] = 'Last Login'; // Add more custom columns here, if you want... // $columns[META_SOME_OTHER_CUSTOM_COLUMN] = 'My fancy custom column'; // $columns[META_YET_OTHER_CUSTOM_COLUMN] = 'Another custom column'; // ... return $columns; } add_filter('manage_users_columns', 'wptrul_manage_users_columns'); /** * Display the per-user custom culumn data. */ function wptrul_manage_users_custom_column($output, $column_name, $user_id) { switch ($column_name) { case WPTRUL_META_LAST_LOGIN_WHEN: if (empty($when = get_user_meta($user_id, WPTRUL_META_LAST_LOGIN_WHEN, true))) { $output = '--'; } else { try { // Recreate the login date time and format it human-friendly. $date_time = new DateTime($when); $output = $date_time->format(WPTRUL_ADMIN_LOGIN_DATE_FORMAT); } catch (Exception $e) { // If we end up in here, the contents of the user meta data is // probably an invalid date/time string. // Give ourselves of detecting and fixing the error. error_log(__FUNCTION__ . ' : ' . $e->getMessage()); // Let the (admin) user know that we have a last login date/time, // but there's a problem with it. $output = '??'; } } break; // Look for other custom columns here... // case META_SOME_OTHER_CUSTOM_COLUMN: // $output = get_user_meta($user_id, META_SOME_OTHER_CUSTOM_COLUMN, true); // break; default: // Unknown custom column. // Let $output pass-through without changing it. break; } return $output; } add_filter('manage_users_custom_column', 'wptrul_manage_users_custom_column', 10, 3);
You can see we’ve got three functions in there – one for each of the hooks we’re connecting to:
- authenticate
- manage_users_columns
- manage_users_custom_column
We’ve also made it easy to add more custom columns. You can add the custom column headers into wptrul_manage_users_columns()
– just make sure each column has a unique name. I like to use constants like WPTRUL_META_LAST_LOGIN_WHEN
to hold column names, because it reduces the risk of bug-by-typo.
Other Key Notes…
When we connect to a hook, the default is to use a priority of 10. If we use a lower number, our handler will be called earlier. If we use a higher number, our handler will be called later. We usually use priorities from 0 up to 100, but we can actually use anything form -2147483648 (PHP_INT_MIN
) to 2147483647 (PHP_INT_MAX
). When we connect to the authenticate filter, we use a priority of 99 to make sure our function is called very late, in case there are handlers at 10 or 20 that do some useful authentication (or rejection) stuff. We want to let all the usual authentication provider hooks run first, and run our code at the end of the authentication sequence.
Notice how we store the last login date/time using the unambiguous format, “Y-m-d H:i:s T
” (see PHP Date Formats). When we render this date/time in wptrul_manage_users_custom_column()
, we convert this back into a DateTime object, then use format()
to render it in a friendlier format, with whatever formatting is in WPTRUL_ADMIN_LOGIN_DATE_FORMAT
.
Integrate and Test
To make it all work, open your child theme’s functions.php file and add the following couple of lines:
// Record User Logins require_once dirname(__FILE__) . '/wpt-record-user-logins.php';
Save everything, log out of your site, log back in and have a look at the Users table in the admin area. You should see… your recent login date/time column.
Happy logging the log-ins 😎 👍