Token Expiry Cleaner
Removes expired password reset, email verification, and CSRF records from storage.
Function signature
ogCleanExpiredSecurityTokens(conn, table, expiry_column = 'expires_at', batch_limit = 500, current_time = 0, execute = false)
Categories
- Security
Parameters
connProcedural mysqli connection.tableToken table name.expiry_columnExpiration column name containing a Unix timestamp.batch_limitMaximum rows to delete.current_timeCurrent Unix timestamp. Uses time() when empty.executeWhether to execute the delete or only return a plan.Return value
Short public-safe status message.
- table
- expiry_column
- expired_count
- deleted_count
- execute
Compatibility
Existing function name and call order preserved; metadata signature corrected to source.
Minimum PHP version: 7.4
Security notes
Validate request method, identity, permissions, and caller-owned allowlists before use; keep secrets out of public output.
Code
<?php
/*
* Copyright (c) 2026 Jeffery L. Paris <jparis@phpog.com>.
* Free for personal and internal use. Paid project use requires visible credit
* to Jeffery L. Paris. Corporate use requires a paid license fee unless a
* separate written license states otherwise.
*/
/**
* Removes expired password reset, email verification, and CSRF records from storage.
*
* Primary use case: Maintenance jobs and login cleanup.
* Typical inputs: token table, expiry column, batch limit.
* Typical output: deleted count.
*
* Implementation note: Use batched deletes and indexes on expiry fields.
*
* @param mysqli $conn Procedural mysqli connection.
* @param string $table Token table name.
* @param string $expiry_column Expiration column name containing a Unix timestamp.
* @param int $batch_limit Maximum rows to delete.
* @param int $current_time Current Unix timestamp. Uses time() when empty.
* @param bool $execute Whether to execute the delete or only return a plan.
* @return array Structured result data with success, message, and data keys.
*/
function ogCleanExpiredSecurityTokens($conn, $table, $expiry_column = 'expires_at', $batch_limit = 500, $current_time = 0, $execute = false) {
$result = array(
'success' => false,
'message' => '',
'data' => array()
);
$table = trim((string)$table);
$expiry_column = trim((string)$expiry_column);
$batch_limit = (int)$batch_limit;
$current_time = (int)$current_time;
if (!preg_match('/^[A-Za-z0-9_]+$/', $table)) {
$result['message'] = 'Invalid token table name.';
return $result;
}
if (!preg_match('/^[A-Za-z0-9_]+$/', $expiry_column)) {
$result['message'] = 'Invalid expiration column name.';
return $result;
}
if ($batch_limit < 1) {
$batch_limit = 1;
}
if ($batch_limit > 5000) {
$batch_limit = 5000;
}
if ($current_time <= 0) {
$current_time = time();
}
$query = 'DELETE FROM `' . $table . '` WHERE `' . $expiry_column . '` < ? LIMIT ' . $batch_limit;
$result['data'] = array(
'query' => $query,
'current_time' => $current_time,
'batch_limit' => $batch_limit,
'deleted' => 0
);
if (!$execute) {
$result['success'] = true;
$result['message'] = 'Expired token cleanup plan prepared.';
return $result;
}
if (!is_object($conn)) {
$result['message'] = 'A mysqli connection is required to execute cleanup.';
return $result;
}
$stmt = mysqli_prepare($conn, $query);
if (!$stmt) {
$result['message'] = 'Expired token cleanup statement could not be prepared.';
return $result;
}
mysqli_stmt_bind_param($stmt, 'i', $current_time);
if (!mysqli_stmt_execute($stmt)) {
mysqli_stmt_close($stmt);
$result['message'] = 'Expired token cleanup failed.';
return $result;
}
$deleted = mysqli_stmt_affected_rows($stmt);
mysqli_stmt_close($stmt);
$result['success'] = true;
$result['message'] = 'Expired security tokens cleaned.';
$result['data']['deleted'] = $deleted;
return $result;
}