benecaster_feed_xml
Filters the complete compiled RSS XML string. This filter fires at two points in the feed pipeline, distinguished by the type of its third argument.
When the third argument is a string, the filter is running on a per-tier compiled feed — it fires once per tier, immediately after that tier’s episode list has been serialized to XML. This call fires for both single-tier and multi-tier feed requests. When the third argument is an array, the filter is running on the assembled multi-tier feed — it fires once after all tier feeds have been merged into a single XML document for a multi-tier subscriber. The assembled call fires only for multi-tier subscribers; single-tier requests never reach the assembler.
For per-channel or per-episode customization, prefer [benecaster_feed_channel_data](/hooks/benecaster_feed_channel_data/) or [benecaster_feed_episode_data](/hooks/benecaster_feed_episode_data/) — both operate on structured data before serialization and are more maintainable. Use [benecaster_feed_xml](/hooks/benecaster_feed_xml/) when you need to post-process the raw XML string after serialization, for example to inject a custom namespace declaration or processing instruction.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
$xml |
string |
— | Complete compiled RSS XML string |
$show_id |
int |
— | ID of the show |
$tier_slug_or_slugs |
string|string[] |
— | Tier slug (string) for per-tier call; array of tier slugs for assembled multi-tier call |
Returns:
string
Examples
Add timestamp comment to per-tier feed
add_filter( 'benecaster_feed_xml', function( $xml, $show_id, $tier_slug_or_slugs ) {
if ( is_string( $tier_slug_or_slugs ) ) {
$xml = '<!-- Feed generated: ' . gmdate( 'c' ) . ' -->' . "\n" . $xml;
}
return $xml;
}, 10, 3 );
Inject custom namespace on per-tier feed
add_filter( 'benecaster_feed_xml', function( string $xml, int $show_id, string|array $tier_arg ): string {
if ( is_array( $tier_arg ) ) {
return $xml; // Skip the assembled feed — handle per-tier only.
}
// Inject a custom namespace declaration on the root element.
$xml = str_replace(
'<rss version="2.0"',
'<rss version="2.0" xmlns:myns="https://example.com/ns"',
$xml
);
// Inject a custom channel element before the closing tag.
$xml = str_replace(
'</channel>',
'<myns:showId>' . (int) $show_id . '</myns:showId></channel>',
$xml
);
return $xml;
}, 10, 3 );
Log merged tier count on assembled feed
add_filter( 'benecaster_feed_xml', function( string $xml, int $show_id, string|array $tier_arg ): string {
if ( ! is_array( $tier_arg ) ) {
return $xml; // Skip per-tier calls.
}
error_log( sprintf(
'[Benecaster] Assembled feed for show %d merging %d tiers: %s',
$show_id,
count( $tier_arg ),
implode( ', ', $tier_arg )
) );
return $xml;
}, 10, 3 );
Notes
Distinguish call sites by type. Both call sites share the same hook name. Use is_array() on the third argument to branch your logic:
``php
add_filter( 'benecaster_feed_xml', function( string $xml, int $show_id, string|array $tier_arg ): string {
if ( is_array( $tier_arg ) ) {
// Multi-tier assembled feed — $tier_arg is string[].
} else {
// Per-tier compiled feed — $tier_arg is a tier slug string.
}
return $xml;
}, 10, 3 );
``
String manipulation is fragile. Injecting XML elements via string replacement risks malformed output if the feed structure changes. Where possible, use benecaster_feed_channel_data to inject channel-level elements as structured data before serialization.