Last active
June 5, 2020 15:19
-
-
Save RadGH/8fb0c56eddb4975e43946ebea4d35476 to your computer and use it in GitHub Desktop.
WordPress Plugin: Loops through all users on your site, spread out throughout the day for efficiency. Provides a hook to do any sort of maintenance on those users. Does not actually modify any users, this just an API of sorts.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/* | |
Plugin Name: RS Process Users Daily | |
Description: Provides an API action for developers which iterates all users once per day, based on cofigurable settings. Usage: <code class="code">add_action( 'aa_process_all_users_daily/user', 'example_process_user' );</code> | |
Author: Radley Sustaire | |
Version: 1.1.0 | |
*/ | |
/* | |
// EXAMPLE | |
// Add an action that will process a single user. Will be called numerous times throughout the day until all users have been processed. | |
function example_process_user( WP_User $user ) { | |
$checked_count = (int) get_user_meta( $user->ID, 'checked_count', true ); | |
$checked_count += 1; | |
update_user_meta( $user->ID, 'checked_count', $checked_count ); | |
} | |
add_action( 'aa_process_all_users_daily/user', 'example_process_user' ); | |
// When all users are done for one iteration | |
add_action( 'aa_process_all_users_daily/iteration_complete', 'something' ); | |
// When an entire batch is completed | |
add_action( 'aa_process_all_users_daily/restart_batch', 'something' ); | |
*/ | |
class AA_Process_All_Users_Daily { | |
// Interval settings | |
// The more frequent these schedules, the fewer users need to be queried at a time. | |
public $schedule = array( | |
'interval' => 5 * MINUTE_IN_SECONDS, | |
'key' => '5min', | |
'label' => '5 Minutes', | |
); | |
public function __construct() { | |
// Start and stop cron schedule when plugin is enabled or disabled | |
register_activation_hook( __FILE__, array( $this, 'start_cron' ) ); | |
register_deactivation_hook( __FILE__, array( $this, 'clear_cron' ) ); | |
// Add a 5 minute cron schedule | |
add_filter( 'cron_schedules', array( $this, 'add_custom_schedule' ) ); | |
// Run the process on normal cron schedule | |
add_action( 'aa_process_all_users_daily/schedule', array( $this, 'process_users' ) ); | |
// Run manually with: ?process_all_users_daily=1 | |
// Start over with: ?process_all_users_daily=1&start_over=1 | |
if ( !empty($_GET['process_all_users_daily']) ) { | |
add_action( 'init', array( $this, 'process_manually' ) ); | |
} | |
} | |
/** | |
* Returns total number of users from the wp_users table | |
* | |
* @return int | |
*/ | |
public function get_total_users() { | |
global $wpdb; | |
$sql = "SELECT COUNT(*) FROM {$wpdb->users};"; | |
return (int) $wpdb->get_var( $sql ); | |
} | |
/** | |
* Save details from the last batch process in the options, and optionally send an email. | |
* | |
* @param $debug | |
*/ | |
public function maybe_stash_last_batch( $debug ) { | |
// Get the previous process | |
$last_process = get_option( 'aapaud-batch-settings' ); | |
if ( empty($last_process) ) return; | |
$started = $last_process['start_time']; | |
$ended = time(); | |
$user_index = (int) get_option( 'aapaud-user-index' ); | |
$last_batch = array( | |
'started' => $started, | |
'ended' => $ended, | |
'time_diff' => $started ? human_time_diff( $started, $ended ) : "N/A first run", | |
'users_processed' => $user_index, | |
); | |
// save the details of the last batch | |
update_option( 'aapaud-last-batch', $last_batch, false ); | |
} | |
/** | |
* Setup the process to start from the beginning. If a previous batch was in progress, save some information from that batch for review and debugging. | |
* | |
* @param $debug | |
*/ | |
public function restart_process( $debug ) { | |
// Check if we should start a new batch now (option may be 0 first run, that's ok) | |
$next_batch_time = (int) get_option( 'aapaud-next-batch-time' ); | |
if ( $next_batch_time > time() ) { | |
// Do not restart the process now, wait until next batch time has been reached. | |
if ( $debug ) { | |
echo 'Batch complete, will start next day\'s batch in '. human_time_diff( $next_batch_time, time() ) .'.<br><br>Last batch:<br>'; | |
echo '<pre>'; | |
var_dump(get_option('aapaud-last-batch')); | |
echo '</pre>'; | |
exit; | |
} | |
return; | |
} | |
// Store details from the last batch before we reset all the settings. | |
$this->maybe_stash_last_batch( $debug ); | |
// Clear previous settings | |
delete_option( 'aapaud-batch-settings' ); | |
// Let plugins know a new batch is starting now | |
do_action( 'aa_process_all_users_daily/restart_batch' ); | |
// Get the total number of users at this time. | |
$total_users = $this->get_total_users(); | |
// How many queries will occur per day? Based on schedule interval (in seconds). Final values might look like: | |
// 5min = 288 | |
// hourly = 24 | |
// twicedaily = 2 | |
// daily = 1 | |
$queries_per_day = ceil(DAY_IN_SECONDS / $this->schedule['interval']); | |
// How many users should we get per query? | |
$users_per_query = ceil( $total_users / $queries_per_day ); | |
// Save the settings, these will be used until the end of the entire batch. | |
$settings = array( | |
'total_users' => $total_users, | |
'users_per_query' => $users_per_query, | |
'start_time' => time(), | |
); | |
update_option( 'aapaud-batch-settings', $settings, false ); | |
// reset user index | |
update_option( 'aapaud-user-index', 0, false ); | |
// Save when the next batch is allowed to start (so we do not process too often) | |
update_option( 'aapaud-next-batch-time', strtotime('+24 hours'), false ); | |
if ( $debug ) { | |
echo 'Batch complete. Ready to start a new batch, refresh the page to continue.<br><br>Last batch:<br>'; | |
echo '<pre>'; | |
var_dump(get_option('aapaud-last-batch')); | |
echo '</pre>'; | |
exit; | |
} | |
} | |
/** | |
* Check expiration date for a set of users. | |
* | |
* @param bool $debug | |
*/ | |
public function process_users( $debug = false ) { | |
// pluggable.php is required for get_user_count to work during cron | |
if ( !function_exists('wp_get_current_user')) { | |
include_once(ABSPATH . "wp-includes/pluggable.php"); | |
} | |
// Get settings that we previously calculated | |
$settings = get_option( 'aapaud-batch-settings' ); | |
if ( empty($settings['start_time']) ) { | |
// First run, or settings got reset | |
$this->restart_process( $debug ); | |
return; | |
}else{ | |
// Allow admins to manually restart a batch | |
if ( !empty($_GET['start_over']) && current_user_can('administrator') ) { | |
$this->restart_process( $debug ); | |
return; | |
} | |
} | |
// Total users (at start of batch) | |
$total_users = $settings['total_users']; | |
// How many users to get per query | |
$users_per_query = $settings['users_per_query']; | |
// Get the user index -- our progress in the batch. An index of 15 means that we start with the 16th user, and get $users_per_query more | |
$user_index = (int) get_option( 'aapaud-user-index' ); | |
if ( !$user_index ) $user_index = 0; | |
// start over if the user index exceeds the total number of users | |
if ( $user_index > $total_users ) { | |
$this->restart_process( $debug ); | |
return; | |
} | |
// Get users | |
$args = array( | |
// how many | |
'number' => $users_per_query, | |
// starting at | |
'offset' => $user_index, | |
// order by id. this means new users don't cause repeats | |
'orderby' => 'ID', | |
'order' => 'ASC', | |
); | |
$users = new WP_User_Query( $args ); | |
// if no users were returned, restart and skip the rest of this query | |
if ( empty($users->get_results()) ) { | |
$this->restart_process( $debug ); | |
return; | |
} | |
// loop through found users | |
foreach( $users->get_results() as $user ) { | |
/** | |
* Hook into this action to process a single user. Will be called multiple times in one PHP session. | |
* | |
* @hook "aa_process_all_users_daily/user" | |
* @param WP_User $user | |
* @param int $user_index | |
* @param array $settings | |
*/ | |
do_action( 'aa_process_all_users_daily/user', $user, $user_index, $settings ); | |
// increase the user index | |
$user_index++; | |
} | |
// When complete | |
do_action( 'aa_process_all_users_daily/iteration_complete', $user_index, $settings ); | |
// save the user index so we can continue from where we left off next cron event | |
update_option( 'aapaud-user-index', $user_index, false ); | |
// echo debugging stuff | |
if ( $debug ) { | |
ob_start(); | |
echo '<pre>'; | |
echo '$total_users: ', $total_users, '<br>'; | |
echo '$users_per_query: ', $users_per_query, '<br>'; | |
echo '$user_index: ', $user_index, '<br>'; | |
echo '% progress: ', round(100 * ($user_index / $total_users), 2), '%<br>'; | |
echo '$args: <br>'; | |
var_dump($args); | |
echo '$users this query: ', count($users->get_results()), '<br>'; | |
echo '$users: <br>'; | |
foreach( $users->get_results() as $u ) { | |
if ( !$u instanceof WP_User ) continue; | |
echo "\t", $u->get('display_name'), ' (#', $u->get('ID'), ')<br>'; | |
} | |
echo 'this batch duration: <br>'; | |
var_dump( human_time_diff( $settings['start_time'], time() ) ); | |
echo 'last batch: <br>'; | |
var_dump( get_option('aapaud-last-batch') ); | |
echo '</pre>'; | |
exit; | |
} | |
} | |
// Allow to run manually in the browser with debug info | |
// Run manually with: ?process_all_users_daily=1 | |
// Start over with: ?process_all_users_daily=1&start_over=1 | |
public function process_manually() { | |
if ( current_user_can('administrator') ) { | |
$this->process_users( true ); // true = debug mode | |
}else{ | |
echo 'You must be an administrator to process all users manually.'; | |
exit; | |
} | |
} | |
// Schedule a wp_cron event | |
public function start_cron() { | |
if ( !wp_next_scheduled ( 'aa_process_all_users_daily/schedule' ) ) { | |
wp_schedule_event(time(), $this->schedule['key'], 'aa_process_all_users_daily/schedule'); | |
} | |
} | |
// Clear the scheduled event | |
public function clear_cron() { | |
wp_clear_scheduled_hook( 'aa_process_all_users_daily/schedule' ); | |
} | |
// Add a custom cron event schedule | |
public function add_custom_schedule( $schedules ) { | |
$schedules[ $this->schedule['key'] ] = array( | |
'interval' => $this->schedule['interval'], | |
'display' => __( $this->schedule['label'] ) | |
); | |
return $schedules; | |
} | |
} | |
global $AA_Process_All_Users_Daily; | |
$AA_Process_All_Users_Daily = new AA_Process_All_Users_Daily(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment