Make WordPress Core


Ignore:
Timestamp:
01/29/2026 01:05:19 AM (6 weeks ago)
Author:
peterwilsoncc
Message:

Feeds: Fix backward compatibility of fetch_feed().

In simplepie/simplepie#795 handling of multiple feed requests was deprecated, triggering the message Fetching multiple feeds with single SimplePie instance is deprecated since SimplePie 1.9.0, create one SimplePie instance per feed and use SimplePie::merge_items to get a single list of items.

This updates fetch_feed() to handle multiple requests using seperate SimplePie instances in order to retain backward compatibility.

A PHP 8.5 deprecation was throwing notices in the event an empty URL was passed to fetch_feed(), Using null as an array offset is deprecated, use an empty string instead.

This includes a workaround pending the release of a SimplePie version including simplepie/simplepie#949.

Reviewed by jorbin.
Merges [61551] to the 6.9 branch.

Fixes #64136.
Props audrasjb, jorbin, muryam, oglekler, ozgursar, presskopp, swissspidy, westonruter, wildworks, peterwilsoncc.

Location:
branches/6.9
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • branches/6.9

  • branches/6.9/tests/phpunit/tests/feed/fetchFeed.php

    r60524 r61553  
    3535
    3636    /**
     37     * Ensure WP_Error object returned for 404 response.
     38     *
     39     * @ticket 64136
     40     */
     41    public function test_fetch_feed_returns_error_for_404_response() {
     42        // Priority 15 to ensure this runs after the mocked_rss_response filter.
     43        add_filter( 'pre_http_request', array( $this, 'mocked_rss_404_error_response' ), 15 );
     44
     45        $feed = fetch_feed( 'https://example.org/news/feed/' );
     46
     47        $this->assertWPError( $feed, 'A WP_Error object is expected for failing requests.' );
     48        $this->assertSame( 'simplepie-error', $feed->get_error_code() );
     49    }
     50
     51    /**
     52     * Ensure fetch_feed() returns WP_Error if any feed errors.
     53     *
     54     * @ticket 64136
     55     */
     56    public function test_fetch_feed_multiple_returns_error_if_any_feed_errors() {
     57        // Priority 15 to ensure this runs after the mocked_rss_response filter.
     58        add_filter( 'pre_http_request', array( $this, 'mocked_rss_404_error_response' ), 15 );
     59        add_filter(
     60            'pre_http_request',
     61            /**
     62             * Remove the 404 error response after the first call.
     63             */
     64            function ( $response ) {
     65                remove_filter( 'pre_http_request', array( $this, 'mocked_rss_404_error_response' ), 15 );
     66
     67                return $response;
     68            },
     69            20 // Priority 20 to ensure it runs after the 404 error response.
     70        );
     71
     72        $feed = fetch_feed( array( 'https://example.org/news/feed/', 'https://wordpress.org/news/feed/' ) );
     73
     74        $this->assertWPError( $feed, 'A WP_Error object is expected for any failing requests.' );
     75        $this->assertSame( 'simplepie-error', $feed->get_error_code() );
     76        $this->assertCount( 1, $feed->get_error_messages()[0], 'There should be one error message for the failed feed.' );
     77    }
     78
     79    /**
     80     * Ensure fetch_feed() includes messages for all feeds that error.
     81     *
     82     * @ticket 64136
     83     */
     84    public function test_fetch_feed_multiple_returns_error_if_all_feeds_error() {
     85        // Priority 15 to ensure this runs after the mocked_rss_response filter.
     86        add_filter( 'pre_http_request', array( $this, 'mocked_rss_404_error_response' ), 15 );
     87        $feed = fetch_feed( array( 'https://example.org/news/feed/', 'https://example.com/news/feed/' ) );
     88
     89        $this->assertWPError( $feed, 'A WP_Error object is expected for failing requests.' );
     90        $this->assertSame( 'simplepie-error', $feed->get_error_code() );
     91        $this->assertCount( 2, $feed->get_error_messages()[0], 'There should be two error messages, one for each failed feed.' );
     92    }
     93
     94    /**
     95     * Ensure fetch_feed() returns a SimplePie object for an empty URL (string).
     96     *
     97     * @ticket 64136
     98     */
     99    public function test_fetch_feed_returns_a_simplepie_object_for_unspecified_url_string() {
     100        $feed = fetch_feed( '' );
     101
     102        $this->assertInstanceOf( 'SimplePie\\SimplePie', $feed );
     103    }
     104
     105    /**
     106     * Ensure fetch_feed() returns a SimplePie object for an empty URL (array).
     107     *
     108     * @ticket 64136
     109     */
     110    public function test_fetch_feed_returns_a_simplepie_object_for_unspecified_url_array() {
     111        $feed = fetch_feed( array() );
     112
     113        $this->assertInstanceOf( 'SimplePie\\SimplePie', $feed );
     114    }
     115
     116    /**
     117     * Ensure fetch_feed() accepts multiple feeds.
     118     *
     119     * The main purpose of this test is to ensure that the SimplePie deprecation warning
     120     * is not thrown when requesting multiple feeds.
     121     *
     122     * Secondly it confirms that the markup of the first two items match as they will
     123     * both be from the same feed URL as the array contains the WordPress News feed twice.
     124     *
     125     * @ticket 64136
     126     */
     127    public function test_fetch_feed_supports_multiple_feeds() {
     128        $feed    = fetch_feed( array( 'https://wordpress.org/news/feed/', 'https://wordpress.org/news/feed/atom/' ) );
     129        $content = array();
     130
     131        foreach ( $feed->get_items( 0, 2 ) as $item ) {
     132            $content[] = $item->get_content();
     133        }
     134
     135        $this->assertEqualHTML( $content[0], $content[1], null, 'The contents of the first two items should be identical.' );
     136        $this->assertCount( 20, $feed->get_items(), 'The feed should contain 20 items.' );
     137    }
     138
     139    /**
    37140     * Ensure that fetch_feed() is cached on second and subsequent calls.
    38141     *
     
    112215        );
    113216    }
     217
     218    /**
     219     * Mock 404 error response for `fetch_feed()`.
     220     *
     221     * This simulates a 404 response to test error handling in `fetch_feed()`.
     222     *
     223     * @return array Mocked 404 error response data.
     224     */
     225    public function mocked_rss_404_error_response() {
     226        return array(
     227            'headers'  => new WpOrg\Requests\Utility\CaseInsensitiveDictionary(),
     228            'body'     => '',
     229            'response' => array(
     230                'code'    => 404,
     231                'message' => 'Not Found',
     232            ),
     233            'cookies'  => array(),
     234            'filename' => null,
     235        );
     236    }
    114237}
Note: See TracChangeset for help on using the changeset viewer.