Create a dynamic Twitter-search feed

For years the ancient (and some would say instinctive) art of tweeting was restricted to the avian species of the sky, but recently a service has come about that allows us humans to partake in the practise. You might have heard of this little company, they call themselves Twitter, and have provided an extensive API for us web-folk to play with their service. At this point you might be wondering what the heck I’m going on about, so how’s this? We’ll be creating a dynamic Twitter feed based on a search term, that automatically updates without the user having to refresh the page, while ensuring we don’t overload our own servers with long-polling (more on that in a mo). We’ll even take a look at allowing only a certain set of users to appear in our steam, to avoid spammers. Let’s get started!

A quick bit of background about the technical side of this before we get going though. For us to be able to update our stream constantly we need AJAX to poll (or make a request) to a server, to ask for any new tweets. Now when I first began playing around I had a page on my own server that was requested every 10 seconds, that would then grab, parse, and output any new tweets, and return it to my stream. But, and it’s a big but, this approach can devastate your servers if you’re not careful. If you’ve never delved into long-polling before you’re lucky! But take it from me, having 100 users requesting a page on your server every 10 seconds, each, is a really bad idea. So in this tutorial we’ll look at how we can use JavaScript to parse and output results directly from Twitter’s servers.

To begin we’ll look at how we can GP+O (Get, Parse, and Output) our tweets with PHP. Here’s our code:

function searchTwitter($search) {
    $url = 'http://search.twitter.com/search.atom?rpp=300&q='.urlencode($search) ;
    $ch = curl_init($url);
    curl_setopt ($ch, CURLOPT_RETURNTRANSFER, TRUE);
    $xml = curl_exec ($ch);
    curl_close ($ch);

    $result = new SimpleXMLElement($xml);
    
    foreach ($result->entry as $entry) {
    	$author = trim($entry->author->name);
		$name = explode(' (', $entry->author->name);
        $content = trim($entry->title);
        $time = @strtotime($entry->published);
        $id = $entry->id;

        echo "<li data-id=\"".str_replace('tag:search.twitter.com,2005:', '', $id)."\">
        		<img src=\"http://api.twitter.com/1/users/profile_image/$name[0]?size=normal\" />
        		<div class=\"content\">
        			<span class=\"name\">".substr($name[1], 0, -1).":</span><br> 
        			<span class=\"tweet\">".$content."</span><br>  
        			<span class=\"time\">Posted ".gmdate('j/n/y g:i a',$time)."</span>
        		</div>
        	</li>";
    }
}
	
searchTwitter('myquery');

Because we’re good developers we’re encapsulating our code into a handy, reusable function – aren’t we good?! Let’s look at what we’re doing here. First off we use PHP’s cURL library to make a request to Twitter. There are a bunch of URL variables avalible, all of which can be found over at the Twitter API docs, the only ones we’re concerned with are “rpp” – results-per-page, and “q” – query. I’ve set the results-per-page pretty high, but the function of this variable is fairly self-explanatory, as for the query variable, notice the use of the handy urlencode() function, which will take care of encoding things like hash-tags and spaces in our queries. Phew! That the first line done! The next few lines simply request the page, and shove the resulting data into the $xml variable.

We then use the excellent SimpleXML parser to translate our raw XML data into a useful variable. From then onwards we use a foreach() loop to go through every tweet in our list. If you’d like to know what variables are contained in each entry, just whack a print_r() in the loop. For our purposes we only need to access a few parts of each entry. Here’s a list of our variables, and what they do:

  • $author: The user’s name in the format “[Full name] ([User name])”
  • $name: An array of the user’s name in the form [0]=>”Full name”, [1]=>”Username )” – yes that’s a bracket on the end
  • $content: The tweet itself, all tidied up using trim()
  • $time: A useful representation of when the tweet was posted
  • $id: The unique tweet ID – we’ll use this later when we request updates

We can use these variables to then output a list that looks very much like Twitter itself, with the user’s profile image to the left and data to the right. Our code outputs list items with nicely formatted dates, and here’s where you might want to update the code to reflect your markup. And that’s it for the PHP! However, if you want to have a stream of tweets only from approved users, you would want the following, updated code:

function searchTwitter($search) {
    $url = 'http://search.twitter.com/search.atom?rpp=300&q='.urlencode($search) ;
    $ch = curl_init($url);
    curl_setopt ($ch, CURLOPT_RETURNTRANSFER, TRUE);
    $xml = curl_exec ($ch);
    curl_close ($ch);

    $result = new SimpleXMLElement($xml);
    
    $hidden = array();
    
    foreach ($result->entry as $entry) {
    	$author = trim($entry->author->name);
		$name = explode(' (', $entry->author->name);
		
    	if(in_array(strtolower($name[0]), array('user1', 'user2'))){
	        $content = trim($entry->title);
	        $time = @strtotime($entry->published);
	        $id = $entry->id;
	        echo "<li data-id=\"".str_replace('tag:search.twitter.com,2005:', '', $id)."\">
	        		<img src=\"http://api.twitter.com/1/users/profile_image/$name[0]?size=normal\" />
	        		<div class=\"content\">
	        			<span class=\"name\">".substr($name[1], 0, -1).":</span><br> 
	        			<span class=\"tweet\">".$content."</span><br>  
	        			<span class=\"time\">Posted ".gmdate('j/n/y g:i a',$time)."</span>
	        		</div>
	        	</li>";
	       
        }else { array_push($hidden, $name[0]); }
    }
    
	echo "<!-- Tweets from: ";
	for($x = 0; $x < count($hidden); $x++){
		echo $hidden[$x] . ', ';
	}
	echo " have been hidden -->";
}
	
searchTwitter('myquery');

Notice the addition of the $hidden array – which will allow us to keep track of any blocked tweets. Stepping inside our loop you’ll notice the addition of an if statement that checks to see if the entry was posted by a user in an array of approved tweeters. If it is, then it continues to output the tweet, otherwise it adds it to the array of blocked users. And just for fun, we output a HTML comment at the end to let us know if any users were blocked.

Now that we’ve got our PHP sorted, let’s take a look at how we can use JavaScript to make this bad-boy dynamic! Before we write any of our own code, we need to borrow for some excellent chaps for some utility functions – if you want to format the date any differently to how Twitter returns it by default, you’ll want to go and grab the JavaScript Date.format code, and if you want to only allow tweets from pre-approved users, you’ll want a translation of PHP’s in_array() function. Once you’ve got that code, we can go straight ahead and use:

var lastUpdate;

function update(){
	$.ajax({
		url: "http://search.twitter.com/search.json?q=myquery&since_id="+lastUpdate,
		dataType: "jsonp",
		success : function(data){
			var tweets = data.results;
			tweets.reverse();
			for(var x = 0; x < tweets.length; x++){
				var date = new Date(tweets[x].created_at);
				date = date.format('d/m/y g:ia');
				$('.stream').prepend('<li data-id="'+tweets[x].id_str+'" class="new hidden"><img src="'+tweets[x].profile_image_url+'" /><div class="content"><span class="name">'+tweets[x].from_user_name+'</span><br><span class="tweet">'+tweets[x].text+'</span><br><span class="time">Posted '+date+'</span></div></li>');
				$('.new').slideDown().removeClass('new');			
			}
			
			if(tweets.length > 0){
				lastUpdate = tweets[tweets.length - 1].id_str;
			}
			
			setTimeout(function(){ update(); }, 10000);
		}
	});			
}

lastUpdate = $('.stream li:first').data('id');
setTimeout(function(){update();}, 10000);

So in the code above we’ve created a function that makes an AJAX request to Twitter to ask for new tweets. We use the most recent tweet’s ID in our request, using the parameter “since_id” – stored in the lastUpdate variable (this is first assigned a value at the bottom of the code, and extracts the ID from the first list item in the “.stream” list). Notice the URL features a “.json” file extension – perfect JavaScript goodness, that will allow us to play around with the data. If the request is a success we then go about adding any new tweets to our stream.

We first create the tweets variable, and reverse it – we do this because they are returned in reverse-chronological order (most recent first), and we want to output them in chronological order. We then loop through all the new tweets, formatting, prepending, and sliding-down one-by-one. Once the loop is finished we check to see if we actually had any new tweets, and if we did, we update our lastUpdate variable to reflect the most recent tweet in our stream. Finally we use the setTimeout() function to call our function again in 10 seconds – essentially mimicking real-time updates. And that’s our code!

If you want to allow updates from only approved users, here’s what the JavaScript looks like:

var lastUpdate;
var allowedNames = ['user1','user2'];

function update(){
	$.ajax({
		url: "http://search.twitter.com/search.json?q=myquery&since_id="+lastUpdate,
		dataType: "jsonp",
		success : function(data){
			var tweets = data.results;
			tweets.reverse();
			for(var x = 0; x < tweets.length; x++){
				if(in_array(tweets[x].from_user, allowedNames)){
					var date = new Date(tweets[x].created_at);
					date = date.format('d/m/y g:ia');
					$('.stream').prepend('<li data-id="'+tweets[x].id_str+'" class="new hidden"><img src="'+tweets[x].profile_image_url+'" /><div class="content"><span class="name">'+tweets[x].from_user_name+'</span><br><span class="tweet">'+tweets[x].text+'</span><br><span class="time">Posted '+date+'</span></div></li>');
					$('.new').slideDown().removeClass('new');
				}else {
					$('.stream').prepend('<!-- Blocked Tweet from: '+tweets[x].from_user+' -->');
				}
			}
			
			if(tweets.length > 0){
				lastUpdate = tweets[tweets.length - 1].id_str;
			}
			
			setTimeout(function(){ update(); }, 10000);
		}
	});		
}

lastUpdate = $('.stream li:first').data('id');
setTimeout(function(){update();}, 10000);

And that is how to create a dynamic Twitter stream using PHP and JavaScript!


Posted

in

by