Post-process a multi-tier assembled feed as a whole
A subscriber who holds more than one membership at once — say a “Newsletter” tier and a “Podcast” tier — does not get two feeds. Benecaster builds each tier’s feed separately and then merges them into a single document, so the subscriber sees one combined feed containing everything they are entitled to.
That merge is the moment this recipe cares about. benecaster_feed_xml runs on each individual tier feed and again on the merged result, and most of the time you only want the second. This shows how to tell the two apart and act only on the combined document.
When to Use This
Use the assembled-feed call site when:
-
A subscriber holds multiple concurrent active memberships (e.g. “Newsletter + Podcast”) and you want to inject a channel element reflecting all their tiers
-
You need to log or audit merged-feed requests as a distinct event from single-tier feed requests
-
You’re post-processing XML that only makes sense after all tiers are merged — for example, computing a total episode count across all tiers
For channel-level customization that applies to all feeds (single-tier and multi-tier), prefer benecaster_feed_channel_data — it operates on structured data before serialization and is less fragile.
Key Behavior
The filter runs at two different points, and the third argument tells you which one you are in:
-
Per-tier — once for every tier’s own feed. The third argument is a single tier slug, as a string.
-
Merged — once more, after all the tiers have been combined. The third argument is an array of every tier slug that went into the merge. This pass does not happen for a subscriber on a single tier.
There is no separate hook name or priority for the two passes, so checking whether the third argument is an array is how you distinguish them.
How It Works
The recipe filters benecaster_feed_xml, returns the XML untouched unless the third argument is a non-empty array, then declares a custom XML namespace on the rss opening tag and injects a channel element listing the merged tier slugs before the closing channel tag.
Notes
is_array() is the correct type check. A tier slug string will never pass is_array(), so the check reliably routes per-tier and multi-tier calls to separate branches. There is no separate hook name or priority mechanism to distinguish them.
The merge never happens for a single-tier subscriber. With exactly one active mapped tier there is nothing to combine, so that tier’s feed is served directly and the merged pass never runs. Anything you attach to the merged pass is invisible to the majority of subscribers — do not put something there that everyone needs to see.
String manipulation on XML is fragile. The recipe uses str_replace, which is concise but brittle — whitespace, attribute ordering, or a future structural change to the feed can break it silently. For anything beyond a trivial insertion, parse the XML with DOMDocument or SimpleXML, manipulate the tree, and serialize it back.
Related
- Feeds Overview — how tier feeds are compiled and assembled
Code
<?php
add_filter( 'benecaster_feed_xml', function ( string $xml, int $show_id, string|array $tier_arg ): string {
// Only act on the assembled multi-tier feed.
if ( ! is_array( $tier_arg ) ) {
return $xml;
}
// Example: insert a custom channel element after <channel>.
$badge = '<!-- Assembled from tiers: ' . implode( ', ', $tier_arg ) . ' -->';
return str_replace( '<channel>', '<channel>' . $badge, $xml );
}, 10, 3 );
Hooks Used
Need this built rather than just documented? See our services →