Pixel Acres

Display recent Twitter tweets using PHP

IMPORTANT! This script has been superseded by TweetPHP.

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. &lt;

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.

125 Responses to “Display recent Twitter tweets using PHP”

  1. Lili says:

    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;

  2. Jonathan says:

    @Lili You can just style links using CSS, as you would usually do for any other links on your page.

  3. Duncan says:

    Can any one suggest how to display the number of followers of a Twitter feed?

    Thanks,
    Duncan

  4. Lili says:

    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

  5. 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?

  6. Gary says:

    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;

  7. 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.

  8. Gary says:

    $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.

  9. 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. :)

  10. Gary says:

    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..

  11. Petra says:

    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”?

  12. Petra says:

    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.

  13. Jonathan says:

    @Petra You can display dates in Twitter format by changing the configuration variable $twitter_style_dates = false to $twitter_style_dates = true

  14. Zach Reed says:

    I agree with @Petra. My feed no longer works. Can you see why? It just recently stopped working?

  15. Jonathan says:

    @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.

  16. Petra says:

    @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?

  17. Sam says:

    absolutely perfect!

    How come I show small thumbnail?

    Thanks!

  18. Perseus says:

    Anyone any solutions. My tweets sometime shows, sometimes they don’t. I love this php application; i was hoping the author would respond.

  19. Jonathan says:

    @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).

  20. Just had a small issue with this script. since it won’t render the html characters…
    example: it shows &gt; 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

  21. Jonathan says:

    @SerocIM Thanks for spotting that! I have updated my function so that HTML entities are decoded.

  22. Lunule says:

    @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.

  23. Jonathan says:

    @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.

  24. Petra says:

    @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.

  25. zeniph says:

    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;

  26. zeniph says:

    @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

  27. Jonathan says:

    @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.

  28. Petra says:

    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?

  29. Jonathan says:

    @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.

  30. Petra says:

    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.

  31. Petra says:

    @Jonathan and the tweets which are not showing up are now 10 days old.

  32. John says:

    I am getting a maximum of 20 tweets. What can I do to get more as the limit set by twitter is 3200 tweets

  33. Aaron says:

    The script works great but is there a way to display twitter.txt contents when the feed is not found?

  34. Jonathan says:

    @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.

  35. John says:

    Is there a code available to retrieve 3200 tweets of any user.

  36. Shandy says:

    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?

  37. Pedro says:

    Excellent script, have only one questions: is there a way to get & display the image associated with each tweet ?

  38. Jonathan says:

    @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/

  39. Kippy says:

    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.

  40. Jason says:

    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.

  41. Jason says:

    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

  42. Jonathan says:

    @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.

  43. Wolle says:

    @ 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.

  44. Duncan says:

    @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

  45. Kippy says:

    It would appear the the new RSS feeds are encoding htmlspecialchars already.

  46. Jo Brodie says:

    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

  47. Steve says:

    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!

  48. Steve says:

    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.