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. Jonathan says:

    @Steve – Yeah not sure. That URL format still works in my tests.

  2. Victor says:

    I can`t use your function because I have an error in line:

    $rss = file_get_contents(‘https://api.twitter.com/1/statuses/user_timeline.rss?screen_name=’.$twitter_user_id);

    I always have an rss as null, becasue de url is ok.

    My rss url is https://api.twitter.com/1/statuses/user_timeline.rss?screen_name=Pymefinance

    Thanks

  3. Linda says:

    Hi,

    I believe the Twitter Parser is no longer working on my site because Twitter are no longer supporting RSS. I tried changing this section:

    // Cache file not found, or old. Fetch the RSS feed from Twitter.
    $rss = @file_get_contents(‘feed://api.twitter.com/1/statuses/user_timeline.rss?screen_name=lindamusic’);
    [note I have switched http://twitter.com/statuses/user_timeline/'.$twitter_user_id.'.rss ]

    But it still doesn’t appear to be working. Can you help at all?

    Thanks,

    Linda

  4. Jonathan says:

    @Linda – I notice you’re trying to access Twitter’s API using the feed:// prefix. Try using the https:// prefix instead.

  5. Zach_RWW says:

    Hi, I am using your Latest tweets script… My question is: Do you know how to filter OUT the most recent tweet and then show the NEXT 4 most recent tweets?

  6. Rajagopal says:

    i am using your code. its working perfectly. thanks a lot

  7. Ben says:

    Hello,
    i can’t get it to work!
    it always says the 150 request thing!
    i have no idea! i read and tried everything!
    if you want i can send you the tweets.php and config.php!
    best regards

    Ben

  8. Marc says:

    I finally got this up and running! At least in a php file on its own.

    But I noticed for the twitter dates, after 24 hours it reverts back to the old style. Couldn’t you make the date say, “x days ago”?

    Not to say I’m not appreciative; I just noticed that and it doesn’t seem right.

  9. Marc says:

    I guess the following code would add the “2 days ago”. I see, now, that “2 days ago” isn’t entirely twitter-like: it just reverts to the actual date at some point. But for my site, and for embedding, I think I’d prefer the relative date like this. Could always expand the code and say, after 5 days, just show the calendar date. This is a very cool script.

    case ($time_diff >= 86400):
    $day = floor($time_diff/86400)+1;
    $display_time = ”.$day.’ day’;
    if ($day > 1){ $display_time .= ‘s’; }
    $display_time .= ‘ ago’;
    break;

  10. Ross says:

    Hi there,

    I love this, and I use it for many of my clients. Will this still work once Twitter disables the current API? And if not, will you be developing a new version which will work with the new API?

    Thanks!
    Ross

  11. Marc says:

    I have this going on http://fhu.com. Thanks for the great script. There are a bunch out there, but I really like how this one is set up.

  12. Aaron says:

    Hi, I’ve got this going pretty well but am intermittently getting this error(s):

    [02-Apr-2013 09:46:37 UTC] PHP Warning: fopen(twitter.txt) [function.fopen]: failed to open stream: HTTP wrapper does not support writeable connections in twitter.php on line 140
    [02-Apr-2013 09:46:37 UTC] PHP Warning: fwrite() expects parameter 1 to be resource, boolean given in twitter.php on line 143
    [02-Apr-2013 09:46:37 UTC] PHP Warning: fclose() expects parameter 1 to be resource, boolean given in twitter.php on line 144

    Any ideas?

  13. Robert says:

    Hi first of all thanks for your great code… helped me much :)

    But recently i have noticed that link lists separeted by comma or other symbols has been a challenge for your regular expression…
    e.g. @Brittney, @SkyDigga, @King

    In this case you are wrapping the anchor tags around the hole string including that comma
    e.g. @Brittney

    best regards
    robert

  14. Mike says:

    Will you be updating this script inline with the API 1.1 updates which no longer support RSS therefore rendering the current script broken?

  15. Richard says:

    I just added this to my website, and it was working great, but in the same week I added the code Twitter have gone and turned off the service, how can you make this code work with the new API?

    The Twitter REST API v1 is no longer active. Please migrate to API v1.1. https://dev.twitter.com/docs/api/1.1/overview.

  16. Marc says:

    All of a sudden I’m getting the “Oops, our twitter feed is unavailable right now” error being displayed after this working perfectly for some time. When I resave my twitter.txt file, the server is able to read it and displays my recent Tweet. As soon as the cache expires, the error message comes up again. I wonder if anyone has a thought as to why this has started occurring?

  17. Jonathan says:

    Obviously there is interest in having this script work with Twitter’s new API, so yep I will make that happen, though I won’t have time to work on it for a few days.

  18. This script no longer works because API v1 is not retired.

    I was using this on my website, so when it stopped working, I quickly found a fix.

    I have now modified the script to include user authentication.

    You can access it here : https://github.com/andrewbiggart/latest-tweets-php-o-auth

  19. Terry Upton says:

    Thanks Jonathan. It would be excellent and much appreciated if you did manage to rework this script for the latest API.

    If possible would you be able to put a tweet out if/when you manage to sort this.

    Many thanks and great work sir!

  20. Sebastian says:

    Same problem here “oops….”

    Thanks, Jonathan, for looking into it.

  21. Jonathan says:

    Hi all – I have completely rewritten this script to make use of Twitter API 1.1. The new class is called TweetPHP, and is on Github:

    https://github.com/jnicol/tweet-php

    When I have a moment I will create a new blog post for TweetPHP, but the readme on Github is reasonably detailed. The HTML markup rendered by TweetPHP should be identical to what you’re used to, but the constructor is different (it uses a proper options array now, and has a few new options).

    I’m now using a 3rd party library for parsing links in tweets (e.g. usernames, hashtags), so it should be more robust than the old script.

    Please note that to interact with the Twitter API you need to create an API Key. Instructions are in the repository readme.

    Let me know if you encounter any problems. Thanks for your patience!

  22. Jonathan says:

    @Andrew Biggart Great job man! I was tempted just to skip the rewrite and tell people to use your version instead… Now that my new class is on Github it will be much easier for motivated folk such as yourself to contribute :-)

  23. Great work Jonathan!

  24. Marc says:

    Thanks Jonathan. I’m a huge fan :)

  25. Jonathan says:

    Thanks guys. I’ve created a permanent home for the new class.

    I’m closing comments on this post. Anyone who is using the old version of the Twitter RSS feed parser should migrate to TweetPHP at the link provided above.