Skip to content

Handle multiple feeds in fetch_feed().#10631

Closed
peterwilsoncc wants to merge 18 commits intoWordPress:trunkfrom
peterwilsoncc:fix/64136-fetch-feed-deprecation
Closed

Handle multiple feeds in fetch_feed().#10631
peterwilsoncc wants to merge 18 commits intoWordPress:trunkfrom
peterwilsoncc:fix/64136-fetch-feed-deprecation

Conversation

@peterwilsoncc
Copy link
Copy Markdown
Contributor

Handles passing an array to fetch_feed() by requesting each feed individually and then calling SimplePie\SimplePie::merge_items(); on the result before passing the data back in to a SimplePie object.

I don't love this so would love some suggestions of anything I am missing.

Trac ticket: https://core.trac.wordpress.org/ticket/64136


This Pull Request is for code review only. Please keep all other discussion in the Trac ticket. Do not merge this Pull Request. See GitHub Pull Requests for Code Review in the Core Handbook for more details.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Dec 15, 2025

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

Core Committers: Use this line as a base for the props when committing in SVN:

Props peterwilsoncc, wildworks, westonruter, jorbin, audrasjb, tobiasbg, swissspidy.

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@peterwilsoncc peterwilsoncc force-pushed the fix/64136-fetch-feed-deprecation branch from b73e186 to 3ec52a0 Compare December 15, 2025 03:35
@github-actions
Copy link
Copy Markdown

Test using WordPress Playground

The changes in this pull request can previewed and tested using a WordPress Playground instance.

WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser.

Some things to be aware of

  • The Plugin and Theme Directories cannot be accessed within Playground.
  • All changes will be lost when closing a tab with a Playground instance.
  • All changes will be lost when refreshing the page.
  • A fresh instance is created each time the link below is clicked.
  • Every time this pull request is updated, a new ZIP file containing all changes is created. If changes are not reflected in the Playground instance,
    it's possible that the most recent build failed, or has not completed. Check the list of workflow runs to be sure.

For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation.

Test this pull request with WordPress Playground.

@peterwilsoncc peterwilsoncc force-pushed the fix/64136-fetch-feed-deprecation branch from 3ec52a0 to 1e227d2 Compare December 15, 2025 23:06
Comment thread src/wp-includes/feed.php Outdated
@t-hamano
Copy link
Copy Markdown
Contributor

t-hamano commented Jan 4, 2026

If we simply want to handle multiple feeds, the following approach might work:

diff --git a/src/wp-includes/feed.php b/src/wp-includes/feed.php
index 421bee7173..74f0ee77cd 100644
--- a/src/wp-includes/feed.php
+++ b/src/wp-includes/feed.php
@@ -832,7 +832,20 @@ function fetch_feed( $url ) {
 
        $feed->get_registry()->register( SimplePie\File::class, 'WP_SimplePie_File', true );
 
-       $feed->set_feed_url( $url );
+       if ( is_array( $url ) && count( $url ) > 1 ) {
+               $feed->multifeed_url = array();
+               foreach ( $url as $value ) {
+                       $feed->multifeed_url[] = $feed->get_registry()->call(
+                               SimplePie\Misc::class, 'fix_protocol',
+                               array( $value, 1 )
+                       );
+               }
+       } elseif ( is_array( $url ) && count( $url ) === 1 ) {
+               $feed->set_feed_url( array_shift( $url ) );
+       } else {
+               $feed->set_feed_url( $url );
+       }
+
        /** This filter is documented in wp-includes/class-wp-feed-cache-transien
t.php */
        $feed->set_cache_duration( apply_filters( 'wp_feed_cache_transient_lifeti
me', 12 * HOUR_IN_SECONDS, $url ) );

However, this may not be the recommended approach.

@peterwilsoncc
Copy link
Copy Markdown
Contributor Author

@t-hamano The hook will still run for each individual URL but it won't run with all of them.

IE fetch_feed( [ 'one.example.com', 'two.example.com' ] ) will fire the hook twice, once for each URL. At the moment it fires once with both URLs as the second parameter.

I'll take a look at your suggested diff. I don't know their plans but I wouldn't be surprised if $feed->multifeed_url[] is removed if/when SimplePie hard deprecate an array of URLs.

@peterwilsoncc peterwilsoncc force-pushed the fix/64136-fetch-feed-deprecation branch from 3300255 to c8f11ac Compare January 12, 2026 00:22
Comment thread src/wp-includes/feed.php Outdated
$simplepie_instance->set_output_encoding( get_bloginfo( 'charset' ) );

if ( $simplepie_instance->error() ) {
return new WP_Error( 'simplepie-error', $simplepie_instance->error() );
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If an error occurs in one feed, the other feeds will be ignored. As I understand it, SimplePie will continue processing even if an error occurs and merge successful feeds:

https://github.com/t-hamano/wordpress-develop/blob/b47e1c7c5985fff02153cd89cbba912aa85b8a02/src/wp-includes/SimplePie/src/SimplePie.php#L1704-L1713

I haven't tested it, but how about an approach like this:

foreach ( (array) $url as $feed_url ) {
	$simplepie_instance = clone $feed;
	$simplepie_instance->set_feed_url( $feed_url );
	$simplepie_instance->init();
	$simplepie_instance->set_output_encoding( get_bloginfo( 'charset' ) );

	if ( ! $simplepie_instance->error() ) {
		$feeds[] = $simplepie_instance;
	} else {
		$errors[] = $simplepie_instance->error();
	}
}

if ( empty( $feeds ) ) {
	return new WP_Error( 'simplepie-error', $errors );
}

We may also need unit tests to ensure that if an error occurs, the rest of the feed still gets processed.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the 6.8 behavior for when a feed encounters an error? We should try to replicate that

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the 6.8 behavior for when a feed encounters an error? We should try to replicate that

An error in any feed triggers WP_Error object. If multiple feeds error then the errors are reported for all the affected feeds.

$ wp eval "var_dump( fetch_feed( [ 'http://example.com/news/feed/', 'https://wordpress.org/news/feed/' ] ) );"
phar:///usr/local/src/wp-cli/bin/wp-cli.phar/vendor/wp-cli/eval-command/src/Eval_Command.php(39) : eval()'d code:1:
class WP_Error#2462 (3) {
  public $errors =>
  array(1) {
    'simplepie-error' =>
    array(1) {
      [0] =>
      array(1) {
        [0] =>
        string(118) "A feed could not be found at `http://example.com/news/feed/`; the status code is `404` and content-type is `text/html`"
      }
    }
  }
  public $error_data =>
  array(0) {
  }
  protected $additional_data =>
  array(0) {
  }
}




$ wp eval "var_dump( fetch_feed( [ 'http://example.com/news/feed/', 'https://example.org/news/feed/' ] ) );"
phar:///usr/local/src/wp-cli/bin/wp-cli.phar/vendor/wp-cli/eval-command/src/Eval_Command.php(39) : eval()'d code:1:
class WP_Error#2462 (3) {
  public $errors =>
  array(1) {
    'simplepie-error' =>
    array(1) {
      [0] =>
      array(2) {
        [0] =>
        string(118) "A feed could not be found at `http://example.com/news/feed/`; the status code is `404` and content-type is `text/html`"
        [1] =>
        string(119) "A feed could not be found at `https://example.org/news/feed/`; the status code is `404` and content-type is `text/html`"
      }
    }
  }
  public $error_data =>
  array(0) {
  }
  protected $additional_data =>
  array(0) {
  }
}

I'll try to replicate that,

@t-hamano
Copy link
Copy Markdown
Contributor

@swissspidy @TobiasBg This is my first time looking at SimplePie code, so I may have missed something, and I'd appreciate any feedback if there is anything.

@TobiasBg
Copy link
Copy Markdown

This is my first time looking at SimplePie code

Sorry, I'm not familiar with this specific part either :-(

@swissspidy swissspidy requested a review from Copilot January 12, 2026 10:45
@swissspidy
Copy link
Copy Markdown
Member

Same here

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds support for passing an array of feed URLs to fetch_feed() to handle multiple feeds. When multiple URLs are provided, the function fetches each feed individually, merges their items using SimplePie's merge_items() method, and returns a single SimplePie object with the combined items.

Changes:

  • Modified fetch_feed() to detect and handle array URLs by fetching each feed individually and merging items
  • Moved set_feed_url() call to after array processing to avoid setting URL prematurely
  • Added test coverage to verify multiple feeds are supported without triggering deprecation warnings

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 7 comments.

File Description
src/wp-includes/feed.php Implements multi-feed support by cloning SimplePie instance for each URL, fetching items, and merging them via SimplePie::merge_items()
tests/phpunit/tests/feed/fetchFeed.php Adds test to verify fetch_feed() accepts array of URLs and doesn't trigger SimplePie deprecation warning

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/wp-includes/feed.php Outdated
Comment thread src/wp-includes/feed.php
Comment thread src/wp-includes/feed.php Outdated
Comment thread src/wp-includes/feed.php
Comment thread src/wp-includes/feed.php Outdated
Comment thread src/wp-includes/feed.php
Comment thread tests/phpunit/tests/feed/fetchFeed.php Outdated
Comment thread src/wp-includes/feed.php Outdated
Comment thread tests/phpunit/tests/feed/fetchFeed.php Outdated
Comment thread src/wp-includes/feed.php Outdated
$simplepie_instance->set_output_encoding( get_bloginfo( 'charset' ) );

if ( $simplepie_instance->error() ) {
return new WP_Error( 'simplepie-error', $simplepie_instance->error() );
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the 6.8 behavior for when a feed encounters an error? We should try to replicate that

@peterwilsoncc peterwilsoncc force-pushed the fix/64136-fetch-feed-deprecation branch 2 times, most recently from fcc6c73 to a2089a2 Compare January 27, 2026 20:54
@peterwilsoncc
Copy link
Copy Markdown
Contributor Author

peterwilsoncc commented Jan 27, 2026

@aaronjorbin @t-hamano I've added a bunch of tests for various use cases for both multiple and single feed requests.

On the 6.8 branch there are two different results for unspecified feeds, $url = '' and $url = []:

// 6.8 branch results
There were 2 failures:

1) Tests_Feed_FetchFeed::test_fetch_feed_returns_an_error_for_unspecified_url
A WP_Error object is expected when no URL is provided.
Failed asserting that SimplePie\SimplePie Object (...) is an instance of class "WP_Error".

/vagrant/wordpress-develop/tests/phpunit/includes/abstract-testcase.php:838
/vagrant/wordpress-develop/tests/phpunit/tests/feed/fetchFeed.php:102

2) Tests_Feed_FetchFeed::test_fetch_feed_returns_an_error_for_unspecified_url_array
A WP_Error object is expected when no URL is provided.
Failed asserting that SimplePie\SimplePie Object (...) is an instance of class "WP_Error".

/vagrant/wordpress-develop/tests/phpunit/includes/abstract-testcase.php:838
/vagrant/wordpress-develop/tests/phpunit/tests/feed/fetchFeed.php:115

Changing the first part of the if elseifs to the following results in the same result on both 6.8 and this branch but I think a WP_Error is the best response. What would you prefer?

	if ( empty( $url ) ) {
		$url = ''; // Allow SP to do it's thing with an unspecified URL.
	} elseif ( is_array( $url ) && count( $url ) === 1 ) {

Copy link
Copy Markdown
Contributor

@t-hamano t-hamano left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the update! This PR looks very stable to me.

On the 6.8 branch there are two different results for unspecified feeds, $url = '' and $url = []:
Changing the first part of the if elseifs to the following results in the same result on both 6.8 and this branch but I think a WP_Error is the best response. What would you prefer?

I agree that the ideal behavior would be to return a WP_Error if $url is a falsy value. However, I think doing so could cause confusion for consumers by unintentionally returning a WP_Error for code that previously didn't cause an error.

For now, I'm leaning towards maintaining the behavior of the 6.8 branch.

Comment thread src/wp-includes/feed.php
@peterwilsoncc
Copy link
Copy Markdown
Contributor Author

For now, I'm leaning towards maintaining the behavior of the 6.8 branch.

Done in e1c3bcb

Copy link
Copy Markdown
Contributor

@t-hamano t-hamano left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

Comment thread src/wp-includes/feed.php
if ( empty( $url ) ) {
// Ensure $url is an empty string, even if passed as an empty array.
$url = '';
} elseif ( is_array( $url ) && count( $url ) === 1 ) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing yoda here, but not a problem on my side it is ;)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

count() is a function call so yoda isn't required as it's impossible to assign a value to it

Copy link
Copy Markdown
Contributor

@audrasjb audrasjb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM as well, just added a small comment. Probably not a blocker.

@peterwilsoncc peterwilsoncc force-pushed the fix/64136-fetch-feed-deprecation branch from 4514883 to c50c9f7 Compare January 28, 2026 21:57
@peterwilsoncc
Copy link
Copy Markdown
Contributor Author

I've pushed c50c9f7 to work around the PHP 8.5 deprecation error.

Once simplepie/simplepie#949 is released the commit can be reverted to avoid the duplicate code.

@github-actions
Copy link
Copy Markdown

A commit was made that fixes the Trac ticket referenced in the description of this pull request.

SVN changeset: 61551
GitHub commit: 779509f

This PR will be closed, but please confirm the accuracy of this and reopen if there is more work to be done.

@github-actions github-actions Bot closed this Jan 28, 2026
@peterwilsoncc peterwilsoncc deleted the fix/64136-fetch-feed-deprecation branch January 28, 2026 22:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants