Display recent Twitter tweets using PHP
If you’ve ever wanted to display your latest Twitter tweets on a website, here is method to do that using PHP. My approach has the following features:
- Tweets are cached to avoid exceeding Twitter’s limit of 150 requests for a user’s RSS feed per hour
- A fallback is provided in case the twitter feed fails to load
- Replies (tweets beginning with @) can optionally be ignored
- A configuration parameter allows you to specify how many tweets are displayed
- Dates can optionally be displayed in “Twitter style”, e.g. “12 minutes ago”
- You can edit the HTML that wraps your tweets, tweet status and meta information
- The username which prepends each tweet in Twitter RSS feeds is automatically stripped
<?php /** * TWITTER FEED PARSER * * @version 1.1.4 * @author Jonathan Nicol * @link http://f6design.com/journal/2010/10/07/display-recent-twitter-tweets-using-php/ * * Notes: * Caching is employed because Twitter only allows their RSS feeds to be accesssed 150 * times an hour per user client. * -- * Dates can be displayed in Twitter style (e.g. "1 hour ago") by setting the * $twitter_style_dates param to true. * * Credits: * Hashtag/username parsing based on: http://snipplr.com/view/16221/get-twitter-tweets/ * Feed caching: http://www.addedbytes.com/articles/caching-output-in-php/ * Feed parsing: http://boagworld.com/forum/comments.php?DiscussionID=4639 */ function display_latest_tweets( $twitter_user_id, $cache_file = './twitter.txt', $tweets_to_display = 100, $ignore_replies = false, $twitter_wrap_open = '<h2>Latest tweets</h2><ul id="twitter">', $twitter_wrap_close = '</ul>', $tweet_wrap_open = '<li><span class="status">', $meta_wrap_open = '</span><span class="meta"> ', $meta_wrap_close = '</span>', $tweet_wrap_close = '</li>', $date_format = 'g:i A M jS', $twitter_style_dates = false){ // Seconds to cache feed (1 hour). $cachetime = 60*60; // Time that the cache was last filled. $cache_file_created = ((file_exists($cache_file))) ? filemtime($cache_file) : 0; // A flag so we know if the feed was successfully parsed. $tweet_found = false; // Show file from cache if still valid. if (time() - $cachetime < $cache_file_created) { $tweet_found = true; // Display tweets from the cache. readfile($cache_file); } else { // Cache file not found, or old. Fetch the RSS feed from Twitter. $rss = file_get_contents('https://api.twitter.com/1/statuses/user_timeline.rss?screen_name='.$twitter_user_id); if($rss) { // Parse the RSS feed to an XML object. $xml = simplexml_load_string($rss); if($xml !== false) { // Error check: Make sure there is at least one item. if (count($xml->channel->item)) { $tweet_count = 0; // Start output buffering. ob_start(); // Open the twitter wrapping element. $twitter_html = $twitter_wrap_open; // Iterate over tweets. foreach($xml->channel->item as $tweet) { // Twitter feeds begin with the username, "e.g. User name: Blah" // so we need to strip that from the front of our tweet. $tweet_desc = substr($tweet->description,strpos($tweet->description,":")+2); $tweet_desc = htmlspecialchars($tweet_desc); $tweet_first_char = substr($tweet_desc,0,1); // If we are not ignoring replies, or tweet is not a reply, process it. if ($tweet_first_char!='@' || $ignore_replies==false){ $tweet_found = true; $tweet_count++; // Add hyperlink html tags to any urls, twitter ids or hashtags in the tweet. $tweet_desc = preg_replace('/(https?:\/\/[^\s"<>]+)/','<a href="$1">$1</a>',$tweet_desc); $tweet_desc = preg_replace('/(^|[\n\s])@([^\s"\t\n\r<:]*)/is', '$1<a href="http://twitter.com/$2">@$2</a>', $tweet_desc); $tweet_desc = preg_replace('/(^|[\n\s])#([^\s"\t\n\r<:]*)/is', '$1<a href="http://twitter.com/search?q=%23$2">#$2</a>', $tweet_desc); // Convert Tweet display time to a UNIX timestamp. Twitter timestamps are in UTC/GMT time. $tweet_time = strtotime($tweet->pubDate); if ($twitter_style_dates){ // Current UNIX timestamp. $current_time = time(); $time_diff = abs($current_time - $tweet_time); switch ($time_diff) { case ($time_diff < 60): $display_time = $time_diff.' seconds ago'; break; case ($time_diff >= 60 && $time_diff < 3600): $min = floor($time_diff/60); $display_time = $min.' minutes ago'; break; case ($time_diff >= 3600 && $time_diff < 86400): $hour = floor($time_diff/3600); $display_time = 'about '.$hour.' hour'; if ($hour > 1){ $display_time .= 's'; } $display_time .= ' ago'; break; default: $display_time = date($date_format,$tweet_time); break; } } else { $display_time = date($date_format,$tweet_time); } // Render the tweet. $twitter_html .= $tweet_wrap_open.html_entity_decode($tweet_desc).$meta_wrap_open.'<a href="http://twitter.com/'.$twitter_user_id.'">'.$display_time.'</a>'.$meta_wrap_close.$tweet_wrap_close; } // If we have processed enough tweets, stop. if ($tweet_count >= $tweets_to_display){ break; } } // Close the twitter wrapping element. $twitter_html .= $twitter_wrap_close; echo $twitter_html; // Generate a new cache file. $file = fopen($cache_file, 'w'); // Save the contents of output buffer to the file, and flush the buffer. fwrite($file, ob_get_contents()); fclose($file); ob_end_flush(); } } } } // In case the RSS feed did not parse or load correctly, show a link to the Twitter account. if (!$tweet_found){ echo $twitter_wrap_open.$tweet_wrap_open.'Oops, our twitter feed is unavailable right now. '.$meta_wrap_open.'<a href="http://twitter.com/'.$twitter_user_id.'">Follow us on Twitter</a>'.$meta_wrap_close.$tweet_wrap_close.$twitter_wrap_close; } } display_latest_tweets('YOUR_TWITTER_ID'); ?>
Usage
You should edit the Twitter ID in the function call above before using the function (it appears at the very bottom of the code snippet).
You probably also want to edit the location where the twitter feed is cached – by default it is written to the root level of your domain. To change the location, modify the $cache_file variable, or pass the new location as a function parameter.
Notes
Twitter feeds may contain UTF-8 characters. I have found that running PHP’s utf_decode method on tweets didn’t have the expected result, so my recommendation is to instead set the charset of your HTML page to UTF-8. Really we should all be doing this anyway.
Credits
The hashtag/username parsing in my example is from Get Twitter Tweets by gripnrip.
My RSS parsing is based on replies in the forum discussion “embedding twitter tweets” on the Boagworld website.
The file caching is based on the AddedBytes article “Caching output in PHP”.
Changelog
v1.1.4, 14 October 2012
- Changed the URL that tweet feeds are fetched from, after Twitter killed the old rss feeds
v1.1.2, 17 April 2012
- HTML entities in tweets are now decoded e.g. <
v1.1.1, 28 January 2011
- Fixed bug in the logic that pluralises the number of hours since a tweet
v1.1, 14 January 2011
- Fixed URL parsing regular expression to make it much more liberal. It will no longer choke on hyphens.
- Added an optional parameter $twitter_style_dates which will format dates the same way as the Twitter website, e.g. “12 minutes ago”. It is set to false by default.
- Dates are now formatted the same as on the Twitter website, e.g. “3:48 PM Jan 14th”. This can be overridden using the $date_format parameter.
- The username parser will no longer include a trailing colon as part of the username, so the autolink for a string like “@username: hello!” won’t get messed up.

Thanks soooo much for this!! I’ve been trying to tweak it a bit to style links within a tweet but I’m not having any luck. So far I have the following to display very close to what I need but the links within the tweet are the default web color. Can you please help me?
$twitter_html .= $tweet_wrap_open.”.$twitter_user_id.’ ’.”.$tweet_desc.$meta_wrap_open.’‘.”.$display_time.’‘.$meta_wrap_close.$tweet_wrap_close;
@Lili You can just style links using CSS, as you would usually do for any other links on your page.
Can any one suggest how to display the number of followers of a Twitter feed?
Thanks,
Duncan
That’s what I figured & have tried but cannot get it to work. I have the following css declaration on the page that will be displaying my twitter feed.
.class1 a:link {color: #e1953f;}
.class1 a:visited {color: #e1953f;}
.class1 a:active {color: #e1953f;}
.class1 a:hover {color: #e1953f;}
Then in the php code I am assigning the .class1 style to the tag in the function display_latest_tweets…
$twitter_wrap_open = ”,
and in the code that renders the tweet…
// Render the tweet.
$twitter_html .= $tweet_wrap_open.”.”.$twitter_user_id.’ ’.”.”.$tweet_desc.$meta_wrap_open.”.’‘.”.$display_time.’‘.”.”.$meta_wrap_close.”.$tweet_wrap_close;
I admit, I’m not a PRO with CSS but do know some basics. Just wondering if I was applying in the wrong spot. The link for a given tweet displays correctly but a link within a tweet doesn’t. Any suggestions? Thx
Hey Jonathan,
I really love this plugin and have had it working in a plugin of mine for some time, but I just realized that it’s not actually using the caching ability.
In debugging I think I’ve narrowed it down to the filemtime function.
The file time keeps showing as 0 so the if statement to use the cache is skipped. When I run filemtime() on the file directly, I get a stat failed warning.
I have verified that the cache file path is correct and have manually created the file, permissions are 777 for both the file and the data directory it resides within.
If it helps, the cache file has never been created automatically which is why I created the file manually that I am testing.
I’ve tried a lot of potential solutions to this point with no luck so I’m hoping you might have some insight?
The Cache works, there are just no instructions to properly set it up.
create your cache file manually in your root
touch twitter.txt
make it writeable
chmod 777 twitter.txt
when you refresh your page, it will not show tweets becuase it will wait for an hour to go by to refresh it so,
change the cache time to like 10 secs
$cachetime = 10;
now reload and it will populate the cache,
then change the cachetime back to an hour.
$cachetime = 60*60;
Hey Gary,
Thanks for your response. I just took another stab at getting this to work and still no luck. I’m wondering if the fact that it’s embedded into a WP plugin could be causing the issue?
Here’s the section of code that pertains to cache. I wonder if anything jumps out at you?
$cache_file = plugins_url(‘twitter.txt’, __FILE__); //This targets plugin folder for WP
// Seconds to cache feed (10 sec.)
$cachetime = 10;
// Time that the cache was last filled.
$cache_file_created = ((@file_exists($cache_file))) ? @filemtime($cache_file) : 0;
// A flag so we know if the feed was successfully parsed.
$tweet_found = false;
// Show file from cache if still valid.
if (time() – $cachetime < $cache_file_created) {
$tweet_found = true;
// Display tweets from the cache.
@readfile($cache_file);
} else {
…
twitter.txt is sitting in the root folder of the plugin, does it need to be in the root of the domain?
Thanks for any input you or anyone else may have.
$cache_file = ‘./twitter.txt’,
that needs to lead directly to the file, wherever it may be, so in a wp plugin dir it would probably be
$cache_file=’./wp-content/plugins/twitter.txt’,
make sure its writeable too permissions wise.
Right on, got it!
I tried hard coding the path like you suggested, and that worked, but isn’t good for WP standards to do that. But then I realized the function I was using was giving me the URL, not the PATH, so I changed it to:
$cache_file = plugin_dir_path(__FILE__) . ‘/twitter.txt’;
and that worked! I’ve also since tested and, if twitter.txt doesn’t yet exist, it will get created.
Thanks so much for hashing this out with me Gary!
This has bugged me for months and I’ve just been lucky, I guess, that no one is using this part of my plugin on a super busy site because no one has caught this bug yet.
glad I could help.
yes, I knew I’d need the cache so wanted to get it working.
great work by the author, all works fine, just needed a bit of clarity n setup..
A great tutorial! Thanks a lot. I will be using this technique a lot in the future. Just one question. How do I display dates Twitter style e.g. “12 minutes ago”?
I don’t know why my Twitter feed doesn’t work anymore, although it was working in the beginning? I just have ‘ops, our twitter feed is unavailable right now.’ at the moment instead of tweets and refreshing doesn’t help. Have checked twitter.txt and there is a content in there – a list of tweets but they are not being displayed on the site. Would you be able to tell me what the problem is? thanks a lot.
@Petra You can display dates in Twitter format by changing the configuration variable $twitter_style_dates = false to $twitter_style_dates = true
I agree with @Petra. My feed no longer works. Can you see why? It just recently stopped working?
@Petra @Zach – Still working for me, so I’m not sure why you guys are having issues. Perhaps someone else will be able to chime in with advice. As you’ll notice from the comments, most issues people have relate to caching – PHP being unable to write to or read from the cache.
@Jonathan. Thanks. I tried $twitter_style_dates = true and it doesn’t make any difference to me. I changed the default false to true in function display_latest_tweets() in my functions file. Is this the right way of doing it?
My twitter feed sometimes works, sometimes doesn’t. I don’t know why. It could be that php really can’t read from cache, but I don’t know how to resolve this issue. I checked twitter.txt and there is always a list of tweets there. Any ideas?
absolutely perfect!
How come I show small thumbnail?
Thanks!
Anyone any solutions. My tweets sometime shows, sometimes they don’t. I love this php application; i was hoping the author would respond.
@Persus – most likely a caching issue, i.e. the caching isn’t working, and every request is going directly to Twitter. When you hit Twitter’s hourly request limit the the feed stops displaying. I would suggest checking that the cached file (default is a root level file twitter.txt) is being created, and that it is then being successfully loaded for subsequent requests (manually edit twitter.txt, check that changes are reflected on your front end).
Just had a small issue with this script. since it won’t render the html characters…
example: it shows > instead of >
to fix this, simple add the html_entity_decode() function to the echo…
// Render the tweet.
$twitter_html .= $tweet_wrap_open.html_entity_decode($tweet_desc).$meta_wrap_open.’‘.$display_time.’‘.$meta_wrap_close.$tweet_wrap_close;
hope it helps!
regards,
ZerovicIM
@SerocIM Thanks for spotting that! I have updated my function so that HTML entities are decoded.
@Petra – the $twitter_style_dates feature doesn’t work when there are only tweets published more than a day ago. The reason is that there’s a switch case missing in the function.
So you’ll need to place the following lines between the third and the fourth (the default) case:
case ($time_diff >= 86400):
$day = floor($time_diff/86400);
$display_time = ‘about ‘.$day.’ day’;
if ($day > 1){ $display_time .= ‘s’; }
$display_time .= ‘ ago’;
break;
I’ve just tested it, it works fine for me.
@Lunule – Thanks, that addition will be useful for people who want to display dates in the format: ‘x days ago’.
The Twitter website doesn’t display dates in that format – they do e.g. ’10m’, ’2h’, but for tweets older than 24 hours they display a date, which is the format I emulated. For anyone wondering why $twitter_style_dates doesn’t appear to be working, that’s probably why: your tweets are too old.
@Lunule – thanks for the additional script!
@Jonathan – sometimes my tweets are not working and I do have twitter.txt, but I wonder if it’s in the right place. I have it under themes folder in WordPress website. the file gets written, but sometimes it doesn’t show the tweets.
Thanks for this it great.
My clients host networksolutions.com wouldn’t allow @file_get_contents to grab the rss feed. Reading else where others seem to feel its pretty unsecure also but I’m no expert.
Anyway replaced the rss look up with curl code (from http://goffgrafix.com) as below and all is good:
// Cache file not found, or old. Fetch the RSS feed from Twitter.//$rss = @file_get_contents('http://twitter.com/statuses/user_timeline /'.$twitter_user_id.'.rss'); //<- the old line
$ch = curl_init();
curl_setopt ($ch, CURLOPT_URL, 'http://twitter.com/statuses/user_timeline/'.$twitter_user_id.'.rss');
curl_setopt ($ch, CURLOPT_RETURNTRANSFER, 1);
$contents = curl_exec($ch);
curl_close($ch);
// assign content to original rss variable
$rss = $contents;
@Petra – who knows but perhaps your host has changed their security policy.
I was originally getting the same “…our twitter feed is unavailable…” message but it was permanent not intermittent like you describe.
I have posted my alteration in (currently moderation) in comment above
@Petra – I *might* have a solution for you. Is your feed sometimes entirely full of @replies? If that’s the case, and you had $ignore_replies set to true, then it would explain the intermittent problem you describe.
Also, in my function there are several points where I was suppressing PHP errors/warnings (lines of code preceded by an @ character). I no longer consider this good practice, and have removed all error suppression.
Hi Jonathan
so what are these “lines of code preceded by an @ character”? what is the right way of doing it? You mean this: $rss = @file_get_contents(‘http://twitter.com/statuses/user_timeline/’.$twitter_user_id.’.rss’);
I still have these @ characters everywhere. Should I just remove them?
@Petra They stop PHP warnings from displaying. If you update your version of the script to the one on this page, any PHP errors will be displayed rather than suppressed.
thanks Jonathan. My code now has no @characters, but at the moment the twitter feed is still not working. I checked twitter.txt. There is some text in there but it looks like it’s not reading it. I changed $cachetime = 10; temporarily so I could see the tweets on refresh but no success.
It’s interesting to note that on local environment tweets shop up at this moment, but not on live website.
@Jonathan and the tweets which are not showing up are now 10 days old.
I am getting a maximum of 20 tweets. What can I do to get more as the limit set by twitter is 3200 tweets
The script works great but is there a way to display twitter.txt contents when the feed is not found?
@John This method doesn’t use the twitter API, it uses the rss feed Twitter generates for a user’s timeline, which only includes the 20 most recent items.
Is there a code available to retrieve 3200 tweets of any user.
Jonathan… thanks for the share this is great. Ive set this up on a site, the cache file is created and populated and the content displays also; perfect. But when the the first cache cycle is reached I get the ‘if tweet not found’ error message. It stays this way indefinately unless I manually delete the cache file, in which case it will re-create it and work again for the next cycle.
Seem to be having trouble when it comes to checking if the cache file is already there. Sorry to bother you with this but I can’t figure it out. Any ideas?
Excellent script, have only one questions: is there a way to get & display the image associated with each tweet ?
@Pedro I have a vague plan to build a proper class that interfaces with Twitter’s search API (rather than scraping RSS feeds), which would make it possible to show a profile picture next to each tweet. However in the meantime you might like to try something like http://tweet.seaofclouds.com/
I was having the very occasional “Oops, our twitter feed is unavailable right now.” message but it was working most of the time.
The text file had tweets in it so I assumed it was a problem contacting twitter. In this scenario I’d rather just display the cache than the the error.
if (!$tweet_found){
@readfile($cache_file);
}
To be honest I haven’t investigated thoroughly so please inform me if this is a bad idea.
Thanks for the script anyway dudey.
I have the same problem as Kippy. My twitter feed has worked flawlessly since I incorporated your code. All day today its been displaying “Oops, our twitter feed is unavailable right now.”
Not sure what the problem is. Maybe twitter is shutting us out.
Well I was able to fix my twitter feed by changing the URL.
Found the solution here:
http://brodiesnotes.blogspot.co.uk/2012/10/has-twitter-killed-rss-feeds-yet.html
@Jason Thanks for the find. I knew it was only a matter of time until Twitter disabled their old feeds, but it good to know that it’s still possible to access a user’s status updates in RSS format. I have updated my script with the new RSS URL format.
@ Jason, @ Jonathan
Yes, Lot’s of thanks and greetings from Germany! I have the same problem since a few days – and new everything is working fine.
Thanks you guys very much. The Line must be:
$rss = @file_get_contents(‘https://api.twitter.com/1/statuses/user_timeline.rss?screen_name=’.$twitter_user_id);
Then the script works.
@Jason thanks for the post, I’ve now fixed my Twitter feeds. These are the changes:
Feed: (this script)
From – http://twitter.com/statuses/user_timeline/‘.$twitter_user_id.’.rss
To – https://api.twitter.com/1/statuses/user_timeline.rss?screen_name=“. $twitter_user
Users:
From – http://twitter.com/users/show.xml?screen_name=“. $twitter_user
To – http://api.twitter.com/1/users/show.xml?screen_name=“. $twitter_user
Correction to my previous post:
Feed: (this script)
To – https://api.twitter.com/1/statuses/user_timeline.rss?screen_name=projectsmart
It would appear the the new RSS feeds are encoding htmlspecialchars already.
Hi – anyone know of a functioning RSS feed URL for a Twitter list? Someone’s asked on my blog so I thought I’d ask you
(I wrote the ‘brodiesnotes’ blog that Jason mentioned)
Jo
@Jo You can get an XML feed of a list like so:
https://api.twitter.com/1/lists/statuses.xml?owner_screen_name=YOUR_USER_NAME&slug=YOUR_LIST_NAME
Hi, I’ve got this script working on several sites, but as of today the scripts on one of the servers have failed with the following error: (asterisks added)
file_get_contents(https://api.twitter.com/1/statuses/user_timeline.rss?screen_name=the*****) [function.file-get-contents]: failed to open stream: Connection refused in /home/******/public_html/responsive/inc/twitterpost.php on line 55
Any ideas please? Nothing has been changed or been added on the server so I’m a bit confused.
Thanks!
Following on from the above, I believe this is due to the server IP address being blocked by the Twitter API. I’ve asked if this is the case.