0 Day Analytics – Transients Module Developer Documentation

Table of Contents

1. Overview & Architecture

The Transients Module provides a complete WordPress transients management system within the admin area. It allows administrators to view, search, create, edit, and delete transients stored in both the database and external object caches (Redis).

Key Capabilities

  • View all transients from the wp_options table with expiration and value preview
  • Detect and display transients stored in Redis object cache
  • Create, edit, and delete transients via the admin UI
  • Filter transients by type: persistent, with expiration, expired, WordPress core
  • Search transients by name
  • Bulk delete operations
  • CSV export with progress indicator
  • REST API endpoint for asynchronous transient detail retrieval
  • Modal viewer with copy-to-clipboard and native share support
  • Multisite support with site-wide transient handling

Technology Stack

  • PHP 7.4+ (strict types)
  • WordPress WP_List_Table API
  • WordPress Transient API (get_transient(), set_transient(), etc.)
  • WordPress REST API for modal data fetching
  • Direct database queries for listing and expiration resolution
  • Redis PHP extension for object cache integration

2. File Map

advanced-analytics/
└── classes/
    └── vendor/
        ├── helpers/
        │   └── class-transients-helper.php    — Core transient operations & object cache detection
        ├── controllers/
        │   └── class-endpoints.php            — REST API endpoint registration (shared)
        ├── lists/
        │   ├── class-transients-list.php      — WP_List_Table for transients
        │   └── views/
        │       └── class-transients-view.php  — Page rendering, edit/add forms, modal
        └── settings/
            └── settings-options/
                └── transient-list.php         — Module settings fields

 

3. Admin Screens

3.1 Transients List

URL wp-admin/admin.php?page=advan_transients
Menu Position Sub-menu under “Error Logs” (position 5)
Capability manage_options (or read if menu_admins_only setting is disabled)
Controller Transients_View::analytics_transients_page()
List Table Transients_List (extends Abstract_List / WP_List_Table)

UI Components

  • “Add New Transient” button in page header
  • Filter views – All | Expired | Persistent | With Expiration | Core
  • Search box – searches transient names
  • Row actions – Delete | View | Edit (database) or Delete (Redis)
  • Bulk actions – Delete
  • CSV Export button with progress bar
  • Object cache detection notice (automatic)
The list table uses AJAX-based deletion — single transient deletes happen without a full page reload, with a fade-out animation and toast notification.

3.2 Edit Transient

URL wp-admin/admin.php?page=advan_transients&action=edit_transient&trans_id={id}&_wpnonce={nonce}
Form Action admin_post_advan_transients_update
Handler Transients_View::update_transient()
Nonce advana_transients_manager

Form Fields

Field Type Name Description
Transient name hidden transient Original transient key
Option ID text (disabled) name Database option_id (display only)
Name text name Editable transient name
Expiration date date cron_next_run_custom_date Date part of expiration (local time)
Expiration time time cron_next_run_custom_time Time part of expiration (local time)
Value textarea value Raw transient value (serialized if applicable)
Note: Persistent transients (those with no expiration) have their date/time fields rendered as hidden inputs with empty values. The expiration section is not shown to the user.

3.3 Add New Transient

URL wp-admin/admin.php?page=advan_transients&action=new_transient&_wpnonce={nonce}
Form Action admin_post_advan_transients_new
Handler Transients_View::new_transient()
Nonce advana_transients_manager

Form Fields

Field Type Name Description
Name text name New transient key (required)
Site Wide checkbox side-wide Create as site-wide transient (multisite only)
Expiration date date cron_next_run_custom_date Date part of expiration (local time)
Expiration time time cron_next_run_custom_time Time part of expiration (local time)
Value textarea value Transient value (serialized if applicable)

3.4 Module Settings

URL wp-admin/admin.php?page=advan_logs_settings#aadvana-options-tab-options
Settings File classes/vendor/settings/settings-options/transient-list.php

Available Settings

Setting ID Type Description
Enable Transients module transients_module_enabled checkbox Enables/disables the entire Transients module. When disabled, the Transients viewer sub-menu is hidden and all management UI is removed.

4. Core Classes

4.1 ADVAN\Helpers\Transients_Helper

Core helper class for all transient operations — CRUD, object cache detection, value type detection, and database queries.

Constants

PHP
<?php
// WordPress core transients that are auto-detected and flagged in the UI
public const WP_CORE_TRANSIENTS = array(
    'update_themes',
    'update_plugins',
    'update_core',
    'theme_roots',
    'poptags_',
    'doing_cron',
    'wp_theme_files_patterns-',
    'wp_plugin_dependencies_plugin_data',
    'wp_styles_for_blocks',
    'wp_core_block_css_files',
    'health-check-site-status-result',
);

Object Cache Detection Methods

Method Return Description
is_object_cache_active() bool Checks if WordPress is using an external persistent object cache via wp_using_ext_object_cache()
get_object_cache_backend() string Returns the detected backend: 'redis', 'memcached', 'memcache', 'apcu', 'unknown', or 'none'
get_object_cache_description() string Human-readable label for the active backend (e.g., “Redis”, “Memcached”)

Transient Query Methods

Method Return Description
get_transient_items(array $args) array|int Main query method — retrieves transients from the database with optional search, pagination, type filtering. Merges Redis transients when applicable. Pass 'count' => true for count only.
get_transient_by_id(?WP_REST_Request $request, $id) array|WP_Error Retrieves a single transient by its option_id. When called via REST (with $request), returns a formatted REST response with HTML table.
get_redis_transients(array $args) array Scans Redis for transients matching WordPress key patterns. Supports search and type filtering.

Transient Name & Value Methods

Method Return Description
get_transient_name(string $transient) string Extracts the clean transient key from the full option_name. Strips _transient_ (offset 11) or _site_transient_ (offset 16) prefix.
clear_transient_name(string $transient) string Alias for get_transient_name() — used in display contexts.
get_transient_expiration_time(string $transient) int Returns the Unix timestamp of the transient’s expiration by looking up the corresponding _transient_timeout_ option. Returns 0 for persistent transients.
get_transient_value(string $transient) string Formats a transient value for list display — truncates to 100 characters, wraps in <code>, and appends a type detection badge.
is_site_wide(string $transient_name) bool Returns true if the option name contains _site_transient.

CRUD Methods

Method Return Description
update_transient(string $transient, bool $site_wide) bool|WP_Error Updates an existing transient with values from $_POST. Handles name changes (deletes old + creates new), date/time conversion to GMT.
create_transient(string $transient, bool $site_wide) bool|WP_Error Creates a new transient. Delegates to update_transient() internally.
delete_transient(int $id) bool|WP_Error Deletes a transient by its option_id. Resolves the transient name from the database, determines if site-wide, and uses the appropriate delete_transient() or delete_site_transient() API.

Value Type Detection

The private method get_transient_value_type() classifies transient values into these types:

Type Detection Logic
array Value unserializes to a PHP array
object Value unserializes to a PHP object
serialized Value is a serialized string (is_serialized())
html Value contains HTML tags (strip_tags() differs from original)
json Value is a valid JSON string
numeric Value is numeric (not timestamp-length, not 0/1)
timestamp? Value is numeric and exactly 10 digits long
boolean? Value is "0", "1", "yes", "no", "true", or "false"
scalar Any other scalar value
empty Value is empty

4.2 ADVAN\Lists\Transients_List

Extends Abstract_List (which extends WP_List_Table). Handles the transients admin list table rendering, column management, filtering, and bulk operations.

Constants

PHP
<?php
public const SCREEN_OPTIONS_SLUG  = 'advanced_analytics_transients_list';
public const PAGE_SLUG            = ADVAN_INNER_SLUG . '_page_advan_transients';
public const UPDATE_ACTION        = 'advan_transients_update';
public const NEW_ACTION           = 'advan_transients_new';
public const NONCE_NAME           = 'advana_transients_manager';
public const SEARCH_INPUT         = 'sgp';
public const TRANSIENTS_MENU_SLUG = 'advan_transients';

Key Methods

Method Description
hooks_init() Registers admin_post action handlers for update and new transient, page load hook, and styles
menu_add() Registers the admin submenu page under Error Logs at position 5
process_actions_load() Handles bulk actions during early page load (before output)
prepare_items() Queries transients, applies search/pagination/filters, sets up table
fetch_table_data(array $args) Delegates to Transients_Helper::get_transient_items()
manage_columns($columns) Defines table columns: cb, transient_name, schedule, value
format_column_value($item, $column_name) Renders individual column content including row actions and badges
get_views() Returns filter view links: All, Expired, Persistent, With Expiration, Core
get_bulk_actions() Returns available bulk actions (Delete)
handle_table_actions() Processes bulk delete action with nonce verification
single_row($item) Overrides row rendering to add status CSS classes (persistent, late, on-time)

Filter View Logic

PHP
<?php
// The get_views() method builds filter links using get_filtered_transients()
// which classifies all transients into these categories:
$filtered = array(
    'persistent'      => /* transients where schedule === 0 */,
    'core'            => /* transients matching WP_CORE_TRANSIENTS names */,
    'expired'         => /* transients where schedule > 0 and time has passed */,
    'with_expiration' => /* transients where schedule > 0 */,
);

4.3 ADVAN\Lists\Views\Transients_View

Handles page rendering for all transient screens (list, edit, add) and processes form submissions.

Public Static Methods

Method Description
analytics_transients_page() Main page renderer — checks capabilities, routes to list/edit/add form based on action parameter
update_transient() Form handler for editing — validates nonce, delegates to Transients_Helper::update_transient(), redirects
new_transient() Form handler for creating — validates nonce, delegates to Transients_Helper::create_transient(), redirects
page_load() Cleanup hook — removes _wp_http_referer and bulk_action query args
add_help_content_transients() Returns contextual help text for the transients screen

Page Routing Logic

PHP
<?php
// analytics_transients_page() routes based on the 'action' parameter:
$action = sanitize_key( $_REQUEST['action'] ?? '' );

if ( 'edit_transient' === $action ) {
    // Render edit form with pre-filled values from database
} elseif ( 'new_transient' === $action ) {
    // Render blank new transient form
} else {
    // Render the list table (default view)
}

The list page includes an inline modal built with WordPress media modal styles. Transient data is loaded via the REST API:

JavaScript
// Triggered by clicking the "View" row action
jQuery(document).on('click', '.aadvana-tablerow-view', function(e) {
    e.preventDefault();
    var id = jQuery(this).data('details-id');

    wp.apiFetch({
        path: '/0-day/v1/get_transient_record/' + encodeURIComponent(id) + '/',
        method: 'GET',
        cache: 'no-cache'
    }).then(function(response) {
        // response.mail_body contains the formatted HTML table
        // response.transient_name contains the transient key
        jQuery('.media-modal .http-request-args').html(response.mail_body);
        jQuery('.media-modal .transient-name').html(response.transient_name);
    });

    jQuery('.media-modal').addClass('open');
    jQuery('.media-modal-backdrop').addClass('open');
});

5. Data Model & Storage

Transients are stored in the standard WordPress wp_options table. The module queries this table directly for listing and joins the timeout option for expiration data.

Database Query Structure

SQL
SELECT go.option_id, go.option_name, go.option_value, go.autoload,
       d.option_value AS schedule
FROM wp_options AS go
LEFT JOIN wp_options d
    ON d.option_name = CONCAT('_transient_timeout_',
       SUBSTRING(go.option_name, LENGTH('_transient_') + 1))
WHERE (go.option_name LIKE '_transient_%' OR go.option_name LIKE '_site_transient_%')
  AND go.option_name NOT LIKE '%_transient_timeout_%'
ORDER BY option_id DESC
LIMIT {offset}, {per_page}

Normalized Transient Data Array

Both database and Redis transients are normalized to this format before display:

PHP
<?php
$transient = array(
    'transient_name' => 'my_custom_data',    // Clean name (no prefix)
    'value'          => '<code>...</code>',  // Truncated HTML for display
    'schedule'       => 1711900800,           // Unix timestamp or 0 (persistent)
    'id'             => 42,                   // option_id (positive = DB, negative = Redis)
    'source'         => 'database',           // 'database' or 'redis'
);
Redis transients use negative IDs (decremented counter) to distinguish them from database record IDs. This allows the list to display both sources without ID conflicts.

6. Object Cache Integration

The module automatically detects whether WordPress is using an external object cache and adapts its behavior accordingly.

Detection Flow

  1. is_object_cache_active() calls wp_using_ext_object_cache()
  2. If active, get_object_cache_backend() inspects PHP extensions and global objects:
    • Redis: Checks for \Redis or \Predis\Client classes, then verifies via $wp_object_cache properties/methods or WP_REDIS_HOST / WP_REDIS_SERVERS constants
    • Memcached: Checks for \Memcached class + $wp_object_cache->mc property
    • Memcache: Checks for \Memcache class
    • APCu: Checks for apcu_cache_info function + $wp_object_cache->apcu_get method

Redis Key Scanning

When Redis is detected, get_redis_transients() scans for keys matching these patterns:

PHP
<?php
$patterns = array(
    $prefix . $blog_id . ':transient:*',
    $prefix . $blog_id . ':site-transient:*',
    $prefix . 'transient:*',
    $prefix . 'site-transient:*',
    $prefix . 'wp_' . $blog_id . ':transient:*',
    $prefix . 'wp_' . $blog_id . ':site-transient:*',
);
// Uses Redis SCAN command with count=200 for efficient iteration

Redis Connection Resolution

The module tries these methods to obtain a Redis connection (in order):

  1. $wp_object_cache->connection() — redis-cache plugin by Till Krüss
  2. $wp_object_cache->redis property — wp-redis plugin by Pantheon
  3. $wp_object_cache->redis_instance() — generic method
  4. New \Redis() connection using WP_REDIS_HOST, WP_REDIS_PORT, WP_REDIS_PASSWORD, WP_REDIS_DATABASE constants

De-duplication

When merging Redis and database transients, duplicates are removed by checking transient names. Database records take priority — Redis-only transients are appended to the end of the list.

7. REST API Endpoint

Get Transient Record GET

Endpoint /wp-json/0-day/v1/get_transient_record/{transient_id}/
Method GET
Permission manage_options capability required
Callback Transients_Helper::get_transient_by_id()
Show in Index No

Parameters

Parameter Type Required Description
transient_id string (digits only) Yes The option_id of the transient in the wp_options table

Success Response

JSON
{
    "success": true,
    "mail_body": "<table class=\"widefat striped\">...</table>",
    "transient_name": "my_custom_transient"
}

Error Response

JSON
{
    "code": "empty_row",
    "message": "No record found.",
    "data": {
        "status": 400
    }
}

Usage Example

JavaScript
wp.apiFetch({
    path: '/0-day/v1/get_transient_record/42/',
    method: 'GET',
    cache: 'no-cache'
}).then(function(response) {
    console.log('Transient:', response.transient_name);
    console.log('HTML:', response.mail_body);
}).catch(function(error) {
    console.error('Error:', error.message);
});

8. Hooks & Filters

Admin Post Actions ACTION

Action Handler Description
admin_post_advan_transients_update Transients_View::update_transient Save/update an existing transient
admin_post_advan_transients_new Transients_View::new_transient Create a new transient

WordPress Hooks Used ACTION

Hook Callback Description
admin_print_styles-{page_slug} Settings::print_styles Registers plugin styles on the transients page
load-{page_slug} Transients_View::page_load Cleans up query args on page load
load-{page_slug} Transients_List::process_actions_load Processes bulk/table actions before any output
load-{page_slug} Settings::aadvana_common_help Registers contextual help content
manage_{page_hook}_columns Transients_List::manage_columns Defines default column headers

AJAX Actions AJAX

Action Description
wp_ajax_{ADVAN_PREFIX}delete_transient Handles inline (AJAX-based) transient deletion from the list table. Uses nonce verification and returns JSON response.

9. Settings Reference

Settings are on the Transients Options tab within the main settings page.

PHP
<?php
// Check if transients module is enabled
$enabled = Settings::get_option( 'transients_module_enabled' );

Settings Builder Usage

PHP
<?php
// How the settings are defined in transient-list.php
Settings::build_option(
    array(
        'title' => esc_html__( 'Transients Options', '0-day-analytics' ),
        'id'    => 'options-settings-tab',
        'type'  => 'tab-title',
    )
);

Settings::build_option(
    array(
        'name'    => esc_html__( 'Enable transients module', '0-day-analytics' ),
        'id'      => 'transients_module_enabled',
        'type'    => 'checkbox',
        'hint'    => esc_html__( 'If you disable this, the entire plugin transients module will be disabled...', '0-day-analytics' ),
        'default' => Settings::get_option( 'transients_module_enabled' ),
    )
);

10. List Table Columns & Filtering

Columns

Key Header Sortable Description
cb No Bulk selection checkbox
transient_name Name No Transient name with WP core icon, Redis badge, and row actions
schedule Expiration ({timezone}) No Expiration timestamp or “Persistent” badge
value Value No Value preview (100 chars) with type badge
Sorting is disabled for transients columns due to the storage format constraints (transients are stored across wp_options rows with joined timeout rows). The list is ordered by option_id DESC by default.

The search box filters by transient name (the option_name column in wp_options). Query parameter: sgp

Filter Views

  • All transients (no filters) – All transients including Redis
  • Expired transients – Transients past their expiration
  • Persistent transients – Transients with no expiration (schedule = 0)
  • Transients with expiration – Transients with an expiration set
  • Core transients – Transients matching WP_CORE_TRANSIENTS names

Row Styling CSS Classes

CSS
/* Applied to <tr> elements based on transient status */
.persistent th:nth-child(1) {
    border-left: 7px solid #d2ab0e !important;  /* Yellow */
}
.late th:nth-child(1) {
    border-left: 7px solid #dd9192 !important;  /* Red */
}
.on-time th:nth-child(1) {
    border-left: 7px solid rgb(49, 179, 45) !important;  /* Green */
}

11. Bulk & Row Actions

Row Actions (Database Transients)

Action Method Description
Delete AJAX (inline) Deletes the transient via AJAX with fade-out animation and toast notification
View Modal (REST API) Opens media modal and fetches transient data via REST endpoint
Edit Page redirect Navigates to the edit form with pre-filled values

Row Actions (Redis Transients)

Action Method Description
Delete Page redirect Deletes the Redis transient via WordPress Transient API with a confirmation prompt

Bulk Actions

Action Description
Delete Permanently delete all selected transients from the database

AJAX Delete Flow

JavaScript
// Single transient deletion (AJAX, no page reload)
jQuery('.aadvana-transient-delete').on('click', function(e) {
    e.preventDefault();
    if (confirm('You sure you want to delete this transient?')) {
        jQuery.get(ajaxurl, {
            action: '{ADVAN_PREFIX}delete_transient',
            post_type: 'GET',
            _wpnonce: jQuery(this).data('nonce'),
            id: jQuery(this).data('id'),
        }, function(response) {
            if (response.success) {
                // Fade out and remove the row
                jQuery(that).closest('tr').animate({ opacity: 0 }, 1000, function() {
                    jQuery(this).remove();
                });
            }
        }, 'json');
    }
});

12. Code Examples

Example 1: Check if the Transients Module is Enabled

PHP
<?php
use ADVAN\Helpers\Settings;

$enabled = Settings::get_option( 'transients_module_enabled' );

if ( $enabled ) {
    echo 'Transients module is active.';
} else {
    echo 'Transients module is disabled.';
}

Example 2: Detect the Active Object Cache Backend

PHP
<?php
use ADVAN\Helpers\Transients_Helper;

if ( Transients_Helper::is_object_cache_active() ) {
    $backend = Transients_Helper::get_object_cache_backend();
    $label   = Transients_Helper::get_object_cache_description();

    echo 'Object cache is active: ' . esc_html( $label );
    // Output: "Object cache is active: Redis"
} else {
    echo 'No external object cache detected. Transients use the database.';
}

Example 3: Get All Transients with Search Filter

PHP
<?php
use ADVAN\Helpers\Transients_Helper;

// Get all transients matching "woo" in name
$transients = Transients_Helper::get_transient_items( array(
    'search'   => 'woo',
    'offset'   => 0,
    'per_page' => 50,
) );

foreach ( $transients as $transient ) {
    printf(
        "Name: %s | Expires: %s | Source: %s\n",
        $transient['transient_name'],
        $transient['schedule'] ? gmdate( 'Y-m-d H:i:s', $transient['schedule'] ) : 'Persistent',
        $transient['source']
    );
}

Example 4: Count Transients by Type

PHP
<?php
use ADVAN\Helpers\Transients_Helper;

// Count all transients
$total = Transients_Helper::get_transient_items( array( 'count' => true ) );

// Count only persistent transients
$persistent = Transients_Helper::get_transient_items( array(
    'count' => true,
    'type'  => 'persistent',
) );

// Count expired transients
$expired = Transients_Helper::get_transient_items( array(
    'count' => true,
    'type'  => 'expired',
) );

echo "Total: $total | Persistent: $persistent | Expired: $expired";

Example 5: Retrieve a Transient by Option ID

PHP
<?php
use ADVAN\Helpers\Transients_Helper;

$transient = Transients_Helper::get_transient_by_id( null, 42 );

if ( ! empty( $transient ) ) {
    $name       = Transients_Helper::get_transient_name( $transient['option_name'] );
    $expiration = Transients_Helper::get_transient_expiration_time( $transient['option_name'] );
    $is_site    = Transients_Helper::is_site_wide( $transient['option_name'] );

    echo 'Name: ' . esc_html( $name );
    echo 'Value: ' . esc_html( $transient['option_value'] );
    echo 'Expires: ' . ( $expiration ? gmdate( 'Y-m-d H:i:s', $expiration ) : 'Never' );
    echo 'Site-wide: ' . ( $is_site ? 'Yes' : 'No' );
}

Example 6: Delete a Transient Programmatically

PHP
<?php
use ADVAN\Helpers\Transients_Helper;

$option_id = 42;
$result    = Transients_Helper::delete_transient( $option_id );

if ( true === $result ) {
    echo 'Transient deleted successfully.';
} elseif ( is_wp_error( $result ) ) {
    echo 'Error: ' . $result->get_error_message();
} else {
    echo 'Transient not found or already deleted.';
}

Example 7: Get Redis Transients Directly

PHP
<?php
use ADVAN\Helpers\Transients_Helper;

// Only works when Redis object cache is active
if ( 'redis' === Transients_Helper::get_object_cache_backend() ) {
    $redis_transients = Transients_Helper::get_redis_transients( array(
        'search' => 'session',
    ) );

    foreach ( $redis_transients as $t ) {
        printf(
            "Redis transient: %s | TTL expires: %s\n",
            $t['transient_name'],
            $t['schedule'] > 0 ? gmdate( 'Y-m-d H:i:s', $t['schedule'] ) : 'Persistent'
        );
    }
}

Example 8: Check if a Transient is a WordPress Core Transient

PHP
<?php
use ADVAN\Helpers\Transients_Helper;

$name = 'update_plugins';

$is_core = in_array( $name, Transients_Helper::WP_CORE_TRANSIENTS, true );

if ( ! $is_core ) {
    // Also check prefix matches (e.g., "poptags_" matches "poptags_40")
    foreach ( Transients_Helper::WP_CORE_TRANSIENTS as $core_name ) {
        if ( str_starts_with( $name, $core_name ) ) {
            $is_core = true;
            break;
        }
    }
}

echo $is_core ? 'This is a WP core transient.' : 'This is a custom transient.';
Need User Guide documentation?
See Transients Module User Guide for more details about configuration, practical usage and information.
← Transients Module — User Guide Table Module — User Guide →
Share this page
Back to top