Why does Simplepie return feed items in a wrong order?

All we need is an easy explanation of the problem, so here it is.

I have this feed from picasa (correct but arbitrary, desired order).

The result is messed up

Also, with this feed, for example.

It’s fine in regular, sorted by date feeds, but not these. In the object I get from Simplepie it has a messed up order. I don’t do anything fancy just loop over the results and display them in the found order using my commercial gallery plugin.

$rss_items = $rss->get_items(0, $rss->get_item_quantity($limit));

I have tried this (hooked into wp_feed_options) but it doesn’t change anything:
$rss->enable_order_by_date(false);

Also, when I do var_dump($rss_items); I get an array that is represented by 546503 lines of text. I don’t believe that’s normal, maybe it also hogs the memory, but I can’t even look through that data manually to see if the order is inherently bad or just gets mixed up somewhere.

Also I can’t tell if it’s Simplepie’s or WordPress’ wrapper’s fault.

How to solve :

I know you bored from this bug, So we are here to help you! Take a deep breath and look at the explanation of your problem. We have many solutions to this problem, But we recommend you to use the first method because it is tested & true method that will 100% work for you.

Method 1

At the end of class-simplepie.php there is this line:

usort($items, array(get_class($urls[0]), 'sort_items'));

Which force-sorts feed elements, in case of multifeed.

The $feed->enable_order_by_date(false); is indeed very important and the key to things, BUT it gets disregarded (what I was experiencing) if I use multifeed. Source: in get_items() of class-simplepie.php there is this:

// If we want to order it by date, check if all items have a date, and then sort it
if ($this->order_by_date && empty($this->multifeed_objects))

I used multifeed accidentally for single URLs too. Remember it’s a plugin where I didn’t know if it was going to be single or multiple URLs so it seemed convenient to always pass an array to fetch_feed().

So it now looks something like this:

$rss_url = explode(',',$rss_url); // Accept multiple URLs

if(count($rss_url) == 1){ // New code
    $rss_url = $rss_url[0];
}

add_action('wp_feed_options', array($this, 'jig_add_force_rss'), 10,2);
$rss = fetch_feed($rss_url);
remove_action('wp_feed_options', array($this, 'jig_add_force_rss'), 10);

And the action is:

function jig_add_force_rss($feed,$url){
    $feed->force_feed(true);
    $feed->enable_order_by_date(false);
}

Your solutions were working becuase you passed the RSS url as a string and not as a one-element array. I compared them with how I was using the methods and this was the difference. Now it works with and without query string in the Picasa feed URL, always same as how it looks in the browser.

Method 2

The simplest fix for someone who is experiencing this issue is this:

function my_add_force_rss($feed,$url){
   $feed->force_feed(true);
   $feed->enable_order_by_date(false);
}
add_action('wp_feed_options', 'my_add_force_rss', 10,2);

Placing this in your functions.php will force any feed to be displayed in the order that it is received.

Method 3

I think you simply have to replace:

$feed->enable_order_by_date(false);

with

$rss->enable_order_by_date(false);

to disable the descending date ordering.

In the case of a feed error, the fetch_feed() function returns an instance of the \WP_Error class. Otherwise the output is an instance of the \SimplePie class and this class contains the enable_order_by_date() public method.

So you should use it like this:

// Fetch the feed:
$rss = fetch_feed( $feed_url );

// Make sure we don't have and WP_Error instance:
if ( ! is_wp_error( $rss ) )   
{
    // Disable the date ordering:
    $rss->enable_order_by_date( false );

    // ... etc ...
}

where we (implicitly) make sure we have an instance of the \SimplePie class.

Method 4

I have no idea what’s happening with this feed but with this code

include_once( ABSPATH . WPINC . '/feed.php' );
$rss = fetch_feed( 'https://picasaweb.google.com/data/feed/base/user/110981376645512804522/albumid/5638371435917293985' );

foreach ($rss->get_items() as $item) {
    if ($enclosure = $item->get_enclosure()) {
        echo '<img src="'.$enclosure->get_link().'">';
    }
}

I get the same order that I get when opening that feed with my browser. If I leave the ?alt=rss&kind=photo&hl=en_US in the url, it gives the problems you described and the items are in the wrong order.

No idea why, but maybe you can work forward from here.

Edit: So in short, maybe the problem was the url of the feed?


Here’s a hacky way to get the right feed:

$url = 'https://picasaweb.google.com/data/feed/base/user/110981376645512804522/albumid/5638371435917293985?alt=json';
// or https://picasaweb.google.com/data/feed/base/user/117541338986679044195/albumid/6129832223894216561?alt=json&kind=photo&hl=en_US&imgmax=800
$json = json_decode(file_get_contents($url));
$feed = (array)$json->feed;
foreach ($feed['entry'] as $entry) {
    $i = 0;
    foreach ($entry->content as $image) {
        if($i % 2 != 0) { echo '<img src="'.$image.'" />'; }
        $i++;
    }
}

So I switched to json feed and parse it kinda uglily. But it gets the job done 😀
the $entry->content gives both image type and source which is why there’s that extra if($i % 2 != 0) to get only every other node.

Still, I have no clue why the RSS does that, but here’s alternative way anyway.

edit: to add again you said you really couldn’t parse the rss manually so I’ll just say that you can add &prettyprint=true to the url to get a prettier feed if you’d like to read it.


edit number thousand: Alternative way!

include_once( ABSPATH . WPINC . '/feed.php' );
$rss = fetch_feed( 'https://picasaweb.google.com/data/feed/base/user/117541338986679044195/albumid/6129832223894216561?alt=rss&fields=item(media:group(media:content(@url)))' );

foreach ($rss->get_items() as $item) {
    if ($enclosure = $item->get_enclosure()) {
        //echo $enclosure->get_link().'<br>';
      echo '<img src="'.$enclosure->get_link().'">';
    }
}

by adding ?alt=rss&fields=item(media:group(media:content(@url))) I got the two feeds you have provided thus far to work correctly! I got all the funny information to try out from here.

Note: Use and implement method 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply