Staging Mode Internals — StagingManager
Reference for the Benecaster\Staging\StagingManager class, the feed cache TTL override, and the is_staging field in the daily validation payload. Intended for add-on developers who need to be staging-aware or want to interact with the staging active set.
StagingManager Class
Location: src/Staging/StagingManager.php
The entry point for all staging mode detection and subscriber set management. Available via the service container.
$staging = benecaster()->container()->make( \Benecaster\Staging\StagingManager::class );
is_staging(): bool
Returns true if the current site is operating in staging mode.
Detection is performed once per request and cached in a static property. Subsequent calls within the same request return the cached result without re-running the URL checks.
Two conditions activate staging mode:
- The site URL (
get_site_url()) matches a known staging domain pattern — see URL patterns below. define('BENECASTER_STAGING', true)is present inwp-config.php.
Either condition is sufficient. The constant takes precedence over URL detection and allows forcing staging mode on a domain that would not otherwise be detected.
if ( $staging->is_staging() ) {
// Running on a staging environment.
}
get_staging_cap(): int|null
Returns the staging subscriber cap for this license.
Reads the staging_cap field from the benecaster_license_cache WordPress option, which is populated by the daily validation response from the license server.
- Returns
int(e.g.25) — Free, Starter, and Growth plans have a 25-subscriber staging cap. - Returns
null— Pro, Multi-Show, and Studio plans have no staging cap. An operator-set null override also returnsnull.
When null, add_to_set() will never return a cap error and rebuild_default_set() includes all subscribers.
get_active_set(): array
Returns the current staging active set as an array of WordPress user IDs.
Reads from the benecaster_staging_tokens WordPress option. Returns an empty array when staging is not active or the set has not yet been built.
$active_user_ids = $staging->get_active_set();
// → [17, 42, 88, ...] or []
Use this to check whether a specific user has an active feed token on this staging site:
$is_active = in_array( $user_id, $staging->get_active_set(), true );
add_to_set(int $user_id): bool|WP_Error
Adds a user to the staging active set.
Parameters:
| Parameter | Type | Description |
|---|---|---|
$user_id |
int |
WordPress user ID to add. |
Returns:
true— user was added successfully.WP_Error— staging cap is set and the set is already at capacity (cap !== null && count >= cap). The caller is responsible for presenting a swap UI before retrying.
This method does not auto-swap. If the set is at cap, the caller must first call remove_from_set() to free a slot, then call add_to_set() again.
After a successful add, add_to_set() fires do_action('benecaster_clear_feed_cache', $show_id, $tier_slug) once per distinct (show, tier) pair derived from the user’s active tokens. This prevents stale cache entries from serving the new staging-active subscriber an empty feed on their first poll.
$result = $staging->add_to_set( $user_id );
if ( is_wp_error( $result ) ) {
// At cap — present the swap modal.
} else {
// Successfully added.
}
remove_from_set(int $user_id): void
Removes a user from the staging active set.
Silently succeeds if the user is not currently in the set — safe to call without checking first.
$staging->remove_from_set( $user_id );
// Always void — no return value to check.
rebuild_default_set(): void
Regenerates the staging active set using the default selection algorithm.
Algorithm: For each configured membership tier, selects at least 2 subscribers. Remaining cap slots are filled by join date (oldest subscribers first) until the cap is reached. When get_staging_cap() returns null, all subscribers are included.
Called automatically on first staging detection (when the set is empty) and when the admin triggers “Reset to defaults.” Overwrites the current set entirely — any manual additions are discarded.
$staging->rebuild_default_set();
// New set is saved to benecaster_staging_tokens option.
Feed Cache TTL in Staging
FeedCache::get_ttl(int $show_id) is staging-aware:
public function get_ttl( int $show_id ): int {
if ( $this->staging_manager->is_staging() ) {
return defined( 'BENECASTER_STAGING_CACHE_TTL' )
? (int) BENECASTER_STAGING_CACHE_TTL
: FeedCache::STAGING_CACHE_TTL_DEFAULT; // 45
}
return $this->get_production_ttl( $show_id );
}
Constants:
| Constant | Default | Notes |
|---|---|---|
FeedCache::STAGING_CACHE_TTL_DEFAULT |
45 |
Default TTL in seconds when staging is active. |
BENECASTER_STAGING_CACHE_TTL |
(not defined) | Set in wp-config.php to override the default staging TTL. Value is in seconds. |
To extend the staging cache TTL (for testing caching behavior itself):
// In wp-config.php on your staging site:
define( 'BENECASTER_STAGING_CACHE_TTL', 300 ); // 5 minutes
To effectively disable the staging cache TTL reduction (not recommended):
define( 'BENECASTER_STAGING_CACHE_TTL', PHP_INT_MAX );
is_staging In the Validation Payload
When StagingManager::is_staging() returns true, the daily POST /validate request body sent to the Benecaster license server includes:
{
"is_staging": true,
...
}
The is_staging field is omitted entirely in production. Absence of the field is the canonical signal that a ping comes from a production site — the license server does not treat absence as false; it treats it as “production.”
Two additional staging-specific adjustments apply to the validation payload:
subscriber_countis capped:min(actual_paying_count, staging_cap). This prevents a staging site with a full production subscriber database from triggering a tier upgrade or overage counter.- When
staging_capisnull(Pro, Multi-Show, Studio), the actual count is sent uncapped — consistent with these plans already having no subscriber ceiling in production.
The license server excludes staging pings from tier upgrade logic, benchmark aggregation, and the estimated-reach preview for announcements. See License REST API for the full validation payload reference.
URL Patterns
The following URL patterns trigger automatic staging detection. Matching is case-insensitive and checks only the host portion of get_site_url().
| Pattern | Example match |
|---|---|
staging.* subdomain |
staging.mypodcast.com |
*.local TLD |
mypodcast.local |
localhost |
http://localhost |
*.test TLD |
mypodcast.test |
*.wpengine.com |
mypodcast.staging.wpengine.com |
*.kinsta.cloud |
mypodcast.kinsta.cloud |
*.flywheelstaging.com |
mypodcast.flywheelstaging.com |
*.ngrok.io / *.ngrok.app |
abc123.ngrok.io |
Domains not matching these patterns require define('BENECASTER_STAGING', true) in wp-config.php.
WordPress Option
The staging active set is stored in the benecaster_staging_tokens WordPress option as a serialized array of user IDs. Read it via $staging->get_active_set() rather than calling get_option() directly — the method handles empty-state edge cases and is the stable API surface.