Skip to content

Instantly share code, notes, and snippets.

@RadGH
Created February 24, 2020 23:56
Show Gist options
  • Save RadGH/378b2013d432c27b2c96897c1b23e72f to your computer and use it in GitHub Desktop.
Save RadGH/378b2013d432c27b2c96897c1b23e72f to your computer and use it in GitHub Desktop.
WordPress Bulk process users on hourly cron event
<?php
/**
* Check expiration date for a set of users.
*
* @return string
*/
function dtl_course_expiry_check( $debug = false ) {
// option to start over when used manually via query string
if ( isset($_REQUEST['startover']) ) update_option( 'dtl-ce-user-index', 0, false );
// Pluggable required for get_user_count to work.
// Sometimes it was undefined, not sure why. Maybe fixed already but just in case
if ( !function_exists('wp_get_current_user')) {
include_once(ABSPATH . "wp-includes/pluggable.php");
}
// total number of users
$count = count_users();
$total_users = $count['total_users'];
// get cached number of users per query. this resets at the end of each batch, see below.
$users_per_query = (int) get_option( 'dtl-ce-users-per-query' );
if ( !$users_per_query ) $users_per_query = 20;
// user index to start at from the database.
// this is like pagination but uses an offset like in MySQL
// so $user_index = 2 means the query will start at the third user.
$user_index = (int) get_option( 'dtl-ce-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 ) $user_index = 0;
// when we're starting at the beginning, process the completed "batch"
// this means we finished, and are starting over.
if ( $user_index === 0 ) {
$last_batch_start_time = get_option( 'dtl-ce-batch-start-time' );
$last_batch_end_time = time();
$last_batch_emails_sent = get_option( 'dtl-ce-batch-emails-sent' );
$last_batch = array(
'start' => $last_batch_start_time,
'end' => $last_batch_end_time,
'time_diff' => $last_batch_start_time ? human_time_diff( $last_batch_start_time, $last_batch_end_time ) : "N/A first run",
'emails_sent' => $last_batch_emails_sent,
);
// debug to see when cron triggers via email response
/*
$complete_time_local = get_date_from_gmt( date('Y-m-d H:i:s', $last_batch['end']), 'F jS, Y H:i' );
wp_mail(
'[email protected]',
'BLI Debug - Course Expiry Batch Completed ' . date('H:i:s'),
'Hi myself, a batch is finished processing. ' . "\n\n<br>\n\n" . '<p>The process took '. $last_batch['time_diff'] . ' and ' . $last_batch['emails_sent'] . ' email(s) were sent. The process completed on ' . $complete_time_local . '.</p>'
);
*/
// to process all users daily this number should be X, where:
// X = ( total users / queries per day )
// For hourly cron with 1000 users:
// X = ( 1000 / 24 )
// X = 41.6 users per hour
$queries_per_day = 24; // 24 = hourly, 1 = daily, etc.
// this would give 42 users per query with the above example
$users_per_query = ceil( $total_users / $queries_per_day );
update_option( 'dtl-ce-users-per-query', $users_per_query, false );
// save the duration of the last batch
update_option( 'dtl-ce-last-batch', $last_batch, false );
// save the start time for this batch
update_option( 'dtl-ce-batch-start-time', time(), false );
// reset email sent counter for this new batch
update_option( 'dtl-ce-batch-emails-sent', 0, false );
}
// search for users who haven't been checked
$args = array(
// this many users
'number' => $users_per_query,
// starting at this position
'offset' => $user_index,
// order by, doesn't actually matter
'orderby' => 'ID',
'order' => 'ASC',
);
// get users
$users = new WP_User_Query( $args );
// if no users were returned, start over by setting the user index beyond total users.
// this will reset to 0 on the next iteration, going into "if ( $user_index === 0 )" above.
if ( empty($users->get_results()) ) {
update_option( 'dtl-ce-user-index', $total_users + 1 );
return dtl_course_expiry_check( $debug );
}
// loop through users
foreach( $users->get_results() as $user ) {
// do something with WP_User $user or int $user->ID
// increase the user index
$user_index++;
}
// save the user index so we can continue from where we left off next cron event
update_option( 'dtl-ce-user-index', $user_index, false );
// save the current progress
update_option( 'dtl-ce-current-progress', $user_index / $total_users, false );
if ( $debug ) {
// debugging stuff
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 * (get_option('dtl-ce-current-progress')), 2), '%<br>';
echo '$args: <br>';
var_dump($args);
echo '$users this query: ', count($users->get_results()), '<br>';
echo '$users display names: <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( get_option('dtl-ce-batch-start-time'), time() ) );
echo 'last batch: <br>';
var_dump( get_option('dtl-ce-last-batch') );
echo '</pre>';
}
}
// run this function during our scheduled cron event
add_action( 'wp_cron_dtl_course_expiry_check', 'dtl_course_expiry_check' );
// allow to run manually in the browser, with debug mode, by adding ?manual_expiry_check
if ( isset($_GET['manual_expiry_check']) ) {
add_action( 'init', function() {
dtl_course_expiry_check( true ); // true = debug
exit;
} );
}
// Schedule an event every hour to handle cron events
function dtlce_start_cron() {
if ( !wp_next_scheduled ( 'wp_cron_dtl_course_expiry_check' ) ) {
wp_schedule_event(time(), 'hourly', 'wp_cron_dtl_course_expiry_check');
}
}
register_activation_hook( __FILE__, 'dtlce_start_cron' );
// Clear the scheduled cron event if plugin gets deactivated
function dtlce_clear_cron() {
wp_clear_scheduled_hook('wp_cron_dtl_course_expiry_check');
}
register_deactivation_hook( __FILE__, 'dtlce_clear_cron' );
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment