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://jonathannicol.com/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 thoughts on “Display recent Twitter tweets using PHP

  1. Jaitra says:

    Very useful post, thanks Jonathan.

    Correct me if I am wrong, but this approach would also have the advantage (over JS) of making your Twitter content accessible to Google.

  2. Jonathan says:

    @Jaitra – quite right! I have avoided JavaScript based approaches for that reason.

  3. Matt says:

    Hey this is a great piece of code – but how can I get it the date format to be “X hours ago” or “X days ago” etc rather than a static date?

  4. Jonhatan, thanks for such a great piece of code!
    I was wondering if is possible to pass the parameter of “$tweets_to_display” directly in the call: display_latest_tweets(‘YOUR_TWITTER_ID’);

    I’m not sure of how to do this via PHP, I’ll be grateful with your help.

    Best regards and thanks again.

  5. Jonhatan! I just noticed a rendering error, or it seem like. If a tweet have an URL embedded with more than 38 characters or 40 (not sure exactly) the URL is cut.

    Check this printscreen:
    http://nicolasfranz.com/dropbox/error-tweet.jpg

    You’ll notice that the URL doesn’t go til the ending character, but it parse the link before it.

    I can’t send you a link of the website now because I have it in my localhost, but let me know if you have an idea of what could be happening here, best regards.

  6. Jonathan says:

    @Nicolas The function takes 10 parameters, and the 3rd one is the number of tweets to display. So you need to pass the function those first three parameters:

    display_latest_tweets(‘YOUR_TWITTER_ID’,’./twitter.txt’,10);

    The second parameter is the location of the cache file.

    Regarding the URL parsing error, I used a 3rd party regular expression for URL parsing, but it looks like you’ve discovered a bug with it! I expect it’s choking when it hits the hyphen in your URL. I’ll look into it when I have some spare time.

  7. Thank you Jonathan! I just notices that I was passing only two parameters:
    display_latest_tweets(‘YOUR_TWITTER_ID’,10);

    The result was that, in one page, where I called the function with only 2 tweets, it loaded the 10 recent tweets from the /twitter.txt file at Cache. And vice versa, when I was in the page where I called 10 tweets, the cache had stored only 2 tweets.

    But now with the 3 parameters this issue is fixed. Thanks a lot. Best regards!

  8. Lance says:

    Jonathan –

    Great idea on caching the tweets. Quick question.. I’m wanting to bring in tweets that contain a specific hash tag… any idea how this might work?

    thanks again!

  9. Otto Rask says:

    Hello and many thanks for this script! :)

    I noticed the script parses some URLs wrongly, ones which look like http://www.blabla.com/~blabla. It thinks the URL ends at the ‘~’ and leaves it broken.

    As I can’t make anything out of regular expressions, I was hoping you would take a look at them.

    Thanks! :)

  10. Jonathan says:

    I have updated the script to fix the URL parsing, and to include optional “Twitter style” dates, e.g. “12 minutes ago”. I fixed a few other bugs too. All the changes are listed in the changelog above.

    Thanks to those of you who posted suggestions and bugs :)

  11. Otto Rask says:

    Many thanks Jonathan! Gonna get updating. :) ->

  12. negru says:

    Doesn’t work too well for me, most of the time I see “Oops, our twitter feed is unavailable right now.”
    I think is a cache issue, or is my server… Any idea?

  13. Jonathan says:

    @negru – Sound like a cache problem. All I can suggest is making sure that the RSS feed is being correctly cached on your server, and once cached is being successfully loaded.

    To test this, look for the file twitter.txt in your domain’s root level, or wherever you specified that it be written. Edit that text file with some telltale modification that will let you know your application is reading data from the cached text file, rather than a Twitter RSS feed.

    If you don’t see your modification reflected in your application, then you know that the data is still being loaded from Twitter’s server. Since Twitter limits the times that a single client can access a feed per hour, after 150 page views the feed will stop loading.

  14. t.avison says:

    Thanks Jonathan
    I have looked through tones of code snips and plugins but this works awesomely and the way I wanted to display my tweets.
    Excellent work and Thanks very much

  15. Jonathan says:

    No worries Toby, I’m pleased you found it useful :)

  16. colin says:

    i’m having trouble writing to the text file as well.

    the script doesn’t seem to be able to generate its own cache if i don’t specify a file, and then when I do specify, it can’t write to it.

  17. Duncan says:

    I’m having trouble with the option to add s to the hour when it is greater than one.

    This is the code I have, but with this I get no s at all.

    case ($time_diff > 3600 && $time_diff 1){ $display_time .= ‘s'; }
    $display_time .= ‘ ago';
    break;

    Can you help? I’ve been racking my brains, but can’t think why it’s not working.

    Thanks in advance,
    Duncan

  18. Duncan says:

    This is the code, not that in my previous comment…

    case ($time_diff > 3600 && $time_diff 1){ $display_time .= ‘s'; }
    $display_time .= ‘ ago';
    break;

  19. Duncan says:

    Hi, Duncan again. Just to let you know I’ve solved the issues.

    I’ve replaced

    _if ($display_time>1){ $display_time .= ‘s'; }

    with…

    _if ($hour>1){ $display_time .= ‘s'; }

    Thanks.

  20. Jonathan says:

    @Duncan – Gosh, thanks for spotting that bug! I have updated to fix the error.

  21. Adam Cap says:

    Hey I just wanted to let you know this is awesome! I’ve been searching all morning for different scripts to display your latest tweets along with the time, and this is by far the best solution I’ve found. Thanks man!

    I’ve got 1 bug and 1 request though…

    The bug is that the date/time link goes to my general twitter account, and not the individual tweet. So for example, the date at the end of the tweet links to:

    twitter.com/my_twitter

    and not

    twitter.com/my_twitter/status/123452315325235 (a bunch of numbers)

    I don’t know if you designed it that way, but I was expecting the individual status to be linked to (and not my account).

    Also my request for future improvement would be to give a way to filter retweets, like how you already included a way to filter out replies.

    Oh and lastly do you think 1 hour is a good time to cache the feed, or could it be cached for less time? An hour seems long to me, but I dunno.

    Thanks!!

    -Adam

  22. Jonathan says:

    @Adam Cap – thanks for the feedback and suggestions.

    The reason I chose to link to the Twitter profile rather than the individual tweet is because that was the implementation I needed when I wrote the script. But I think you’re right, the behaviour most people would expect is to link to the tweet, so I will make that change when I have a moment.

    I will look at filtering out retweets too…

    The reason the cache has a lifespan of one hour is because every hour Twitter enforce a limit of 150 requests for a user’s RSS feed from the same IP. Your web application can easily exceed that limit if it fetches the remote RSS feed instead of reading from a local cache. Setting a cache time of one hour ensures this never happens.

  23. Adam Cap says:

    Cool, thanks for the reply man!

    Also I changed to code a little bit to show days, weeks, months, and years ago for the time. Might be a little excessive… but I wanted to add “days ago” and thought what the hell… I’ll add the rest too:

    switch ($time_diff)
    {
    case ($time_diff = 60 && $time_diff = 3600 && $time_diff 1){ $display_time .= ‘s'; }
    $display_time .= ‘ ago';
    case ($time_diff >= 86400 && $time_diff 1){ $display_time .= ‘s'; }
    $display_time .= ‘ ago';
    break;
    case ($time_diff >= 604800 && $time_diff 1){ $display_time .= ‘s'; }
    $display_time .= ‘ ago';
    break;
    case ($time_diff >= 2592000 && $time_diff 1){ $display_time .= ‘s'; }
    $display_time .= ‘ ago';
    break;
    case ($time_diff >= 31536000):
    $year = floor($time_dif/31536000)
    $display_time = $year.’ year';
    if ($year> 1){ $display_time .= ‘s'; }
    $display_time .= ‘ ago';
    break;
    default:
    $display_time = date($date_format,$tweet_time);
    break;
    }

  24. Have spent most of my evening looking for something that produced just this. I’m no PHP pro. I handle front end HTML/CSS build and I have a very good grasp of WordPress, but tonight I wanted to feature recent tweets within a simple php page and it’s taken me hours to find this truly wonderful post.

    Thanks for sharing – I can now go to bed without worrying about disappointing my client in the morning.

  25. Ross says:

    Thanks for this. I tried a few other twitter scripts but none of them had the functionality and diversity of this one. I can now stop banging my head against the wall due to php issues :D

    Thanks again :D

  26. Daniel Foss says:

    Very useful code, would it be ok for you if I paste it into a custom Widget which is part of a ThemeForest-Theme I’m developing actually and sell this theme, or you don’t allow use for commercial projects?

  27. Jonathan says:

    @Daniel Yeah go for it. But if you could leave an author credit/link in your source code that would be appreciated.

  28. Daniel Foss says:

    Thanks for your fast answer Jonathan!
    Will leave your original credits into the widget source, appreciate your support!

  29. Laveena says:

    Hi,

    Thanks for sharing this script.

    I have integrated your coding and it is working fine.

    But sometimes it displays the “Oops, our twitter feed is unavailable right now”

    Could you please anyone help me to out this issue.

    Am i did any application error ?

    Thanks to looking in to that,

  30. Jonathan says:

    @Laveena See the comment above where I reply to negru

  31. Dan O'Neil says:

    This is wonderful! Ditch another wordpress plugin…

    I’ve changed the twitter style dates a bit – so that I can have yesterday or some days ago, last week or some weeks ago etc. You can add in months and years if you need to, seems like overkill to me! Thanks to Adam Cap for the inspiration with his tweak above.

    switch ($time_diff) {
    case ($time_diff = 60 && $time_diff = 3600 && $time_diff 1){ $display_time .= ‘s'; }
    $display_time .= ‘ ago';
    break;

    case ($time_diff >= 86400 && $time_diff < 604800):
    $day = floor($time_diff/86400);
    $display_time = 'about '.$day.' days ago';
    if ($day = 604800 && $time_diff < 2592000):
    $week = floor($time_diff/604800);
    $display_time = 'about '.$week.' weeks ago';
    if ($week < 1){ $display_time = 'last week'; }
    break;

    default:
    $display_time = date($date_format,$tweet_time);
    break;
    }

  32. Dan O'Neil says:

    Another quick mod to get the proper status links for each of your tweets:

    // Render the tweet.
    $twitter_html .= $tweet_wrap_open.$tweet_desc.$meta_wrap_open.'<a href=”‘.$tweet->link.'”>’.$display_time.’‘.$meta_wrap_close.$tweet_wrap_close;
    }

    A simple change from the previous:
    <a href=”http://twitter.com/’.$twitter_user_id.'” rel=”nofollow”>

    changed to:
    <a href=”‘.$tweet->link.'”>

    Job done! There’s probably a cleaner way, but it works!

  33. Ian P says:

    @Jonathan

    This is great, thank you. What I’d like to do is package it up and tie it into the ModX Revolution OpenSource CMS. imho, the community is lacking twitter integration of this quality and I’d like to build on your code and provide them with something they can quickly pick up (downloadable through the new package manager system) and start to use on their websites using minimal configuration and just simple snippet calls.

    Would this be okay? I’d give you credit for all your hard work, of course.

  34. Jonathan says:

    @Ian P Definitely, go for it! From what I’ve seen ModX is a great CMS, so if my code helps make it even better then I’ll be very happy.

  35. qwerty says:

    Hi Jonathan, awesome plugin! I have it all working but is there any way to aggregate two or more users into a single feed?

  36. Jonathan says:

    @qwerty Sorry, as it currently stands that wouldn’t be possible.

  37. Sudeer says:

    Hi Jonathan,

    I am trying to use the script to fetch the latest tweets, but i am getting a blank screen on executing the script after updating the user_id value.

    And when i started debuging the code, i see my RSS url returns blank (below url)

    http://twitter.com/statuses/user_timeline/sudeerde.rss
    http://twitter.com/statuses/user_timeline/delaware_gov.rss

    can you let me know what i am doing wrong here?

    Thanks for your help in advance,
    Sudeer.

  38. Jonathan says:

    @Sudeer – Not sure why it’s not working for you. Both those rss feeds return data, and I tested them both with my display_latest_tweets function and they worked…

  39. Linda says:

    Hello,

    I have set the twitter_style_date to true :$twitter_style_dates = true){

    However, it’s still showing the date and time (from a different time zone).

    Can anybody help?

    I tried copying and pasting some of the above codes in the comments, but I was getting syntax errors.

    Any help would be greatly appreciated.

  40. Linda says:

    Actually…none of the changes I’ve made under “function display_latest_tweets” have worked…I must be missing something really stupid.

  41. Jonathan says:

    @Linda If none of your changes are taking effect that is most likely because the cached version of the twitter feed is being displayed. Delete the cached twitter.txt file (by default it should be saved in your root level directory), then you should see your changes take effect on the next page refresh.

  42. Andy says:

    There’s an issue displaying tweets posted one minute ago (they show up as posted “1 Minutes Ago”).

    A simple fix by adding:

    case ($time_diff >= 60 && $time_diff < 120):
    $min = floor($time_diff/60);
    $display_time = $min.' minute ago';
    break;

    (Make sure to then change the existing case from 60 to 120.)

  43. sean says:

    thanks! very helpful after finding several script examples that didn’t work.

  44. Charlie says:

    Hi Jonathan,

    Nice work! Got one question: is it possible to add any filters in case you want to read tweets with certain hash tags?

    Regards, Charlie

  45. Jonathan says:

    @Charlie That’s not possible, but it’s an interesting idea. If I find time I’ll add that feature in.

  46. Jon says:

    I keep getting syntax errors when I try to use the modified code pasted in over here. I’m struggling with Dan O’Neil time_diff code.

    I can see that the ‘ symbols is wrong.. I guess this website converts them. But even if I change them I still get a syntax error. :(

    Help please.

  47. w3 says:

    Is it possible to hide the hashtags ?

    commenting this part only removes the hyperlink on the hashtags

    //$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);

    Thanks for such wonderful code!!!

  48. Jonathan says:

    @w3 – you should be able to change that line to:

    $tweet_desc = preg_replace('/(^|[\n\s])#([^\s"\t\n\r<:]*)/is', '$1', $tweet_desc);

  49. Adrian says:

    Just wanted to say thanks for this code. I’d been using some other code prior to this which worked ok, but had issues with caching. Your code works beautifully. Thanks!

  50. Steven Clark says:

    THe code has just stopped working for me. It no longer creates the twitter.txt file in the root of the site. It previously worked fine, but has just stopped for some reason. Any advice? Thanks

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

  52. Jonathan says:

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

  53. Duncan says:

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

    Thanks,
    Duncan

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

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

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

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

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

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

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

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

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

  63. Jonathan says:

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

  64. Zach Reed says:

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

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

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

  67. Sam says:

    absolutely perfect!

    How come I show small thumbnail?

    Thanks!

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

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

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

  71. Jonathan says:

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

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

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

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

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

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

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

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

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

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

  81. Petra says:

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

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

  83. Aaron says:

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

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

  85. John says:

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

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

  87. Pedro says:

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

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

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

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

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

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

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

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

  95. Kippy says:

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

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

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

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

  99. Jonathan says:

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

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

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

  102. Jonathan says:

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

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

  104. Rajagopal says:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  118. Sebastian says:

    Same problem here “oops….”

    Thanks, Jonathan, for looking into it.

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

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

  121. Great work Jonathan!

  122. Marc says:

    Thanks Jonathan. I’m a huge fan :)

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

Comments are closed.