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!

Generate a calendar with PHP

Note: This article has been marked for a quality review and will soon be updated.

Calendars are useful for a wide variety of things, least of all planning and recording events that are going to happen in the future, and while it may not be obvious at first, there a are a number of challenges in creating a dynamic calendar with PHP. In this tutorial I will show you the basics of what you need to get started. See an example of what we will be creating here.

Let's first think about what we are creating. We want a standard calendar that will display dates month by month, and in this tutorial we will make it dynamic as to display the current month, and highlight the current day. But unfortunately this isn't as simple as printing out 31 days and labelling them up. Because calendars show dates preceding and succeeding the month, we need to display our data based on the day of the week that the month starts on, this is something we need to think about when designing the code. Not all months have 31 days in them either, so we need to build a system which adapts to each month.

Let's take a look at the PHP. First we will gather all the information we need about the month - we will be using the current month, but you could change the values of the following variables based on your needs.

$today = date("d"); // Current day
$month = date("m"); // Current month
$year = date("Y"); // Current year
$days = cal_days_in_month(CAL_GREGORIAN,$month,$year); // Days in current month

$lastmonth = date("t", mktime(0,0,0,$month-1,1,$year)); // Days in previous month
		
$start = date("N", mktime(0,0,0,$month,1,$year)); // Starting day of current month
$finish = date("N", mktime(0,0,0,$month,$days,$year)); // Finishing day of  current month
$laststart = $start - 1; // Days of previous month in calendar

Okay, so the code above utilises some inbuilt functions and stores al the data we need. Firstly we get today's date using PHP's date() function, having this will allow us to highlight the current day. We then collect the current month, year, and the number of days in the current month. All pretty standard so far. Then we go on to get the number of days in the month preceding the current month, as well as both the start and end date of the current month. We can use those variables to figure out an offset so that we can display the day on which the current month started correctly.

To that we will add the logic that will actually display the calendar:

$today = date("d"); // Current day
$month = date("m"); // Current month
$year = date("Y"); // Current year
$days = cal_days_in_month(CAL_GREGORIAN,$month,$year); // Days in current month

$lastmonth = date("t", mktime(0,0,0,$month-1,1,$year)); // Days in previous month

$start = date("N", mktime(0,0,0,$month,1,$year)); // Starting day of current month
$finish = date("N", mktime(0,0,0,$month,$days,$year)); // Finishing day of  current month
$laststart = $start - 1; // Days of previous month in calander

$counter = 1;
$nextMonthCounter = 1;

if($start > 5){	$rows = 6; }else {$rows = 5; }
for($i = 1; $i <= $rows; $i++){
	echo '<tr class="week">';
	for($x = 1; $x <= 7; $x++){				
		
		if(($counter - $start) < 0){
			$date = (($lastmonth - $laststart) + $counter);
			$class = 'class="blur"';
		}else if(($counter - $start) >= $days){
			$date = ($nextMonthCounter);
			$nextMonthCounter++;
			
			$class = 'class="blur"';
		
		}else {
			$date = ($counter - $start + 1);
			if($today == $counter - $start + 1){
				$class = 'class="today"';
			}
		}
				
			
		echo '<td '.$class.'><a class="date">'. $date . '</a></td>';
		
		$counter++;
		$class = '';
	}
	echo '</tr>';
}

Let's go over what that does. To begin with we figure out how many rows we are going to need by checking to see if the start day is greater than a Friday; if it is, we have 6 rows, otherwise we have 5. We then have a nested loop - one for the weeks, and one for the days in the weeks. Looking at the code we can see that the first loop runs as many times as there are rows, while the inner loop always loops 7 times. Then we have a fancy if statement to give us our date. In plain English, this is what it does: if the number of days already processed, take the starting day of the current month is less than 0, output a day from the last month - this makes sure we fill in the gaps, so if the current month started on a Tuesday, this code would get executed on the first iteration to display last month's Monday. If not, is the day in question part of next month? If so output the day with it's own date - nice an simple because all months start from 1.

And then we come to the last part of the if statement, which will be executed if the day in question is part of the current month. If it is today's date we give it a class of "today". Then we output the variables, increment our counter variable to keep track of the current day, and reset our $class variable.

So to put it all together within a table we would have:

<section id="content" class="planner">	
<h2><?php echo date("F Y") ?></h2>

<table class="month">
	<tr class="days">
		<td>Mon</td>
		<td>Tues</td>
		<td>Wed</td>
		<td>Thurs</td>
		<td>Fri</td>
		<td>Sat</td>
		<td>Sun</td>
	</tr>
<?php 
		
	$today = date("d"); // Current day
	$month = date("m"); // Current month
	$year = date("Y"); // Current year
	$days = cal_days_in_month(CAL_GREGORIAN,$month,$year); // Days in current month
	
	$lastmonth = date("t", mktime(0,0,0,$month-1,1,$year)); // Days in previous month
	
	$start = date("N", mktime(0,0,0,$month,1,$year)); // Starting day of current month
	$finish = date("N", mktime(0,0,0,$month,$days,$year)); // Finishing day of  current month
	$laststart = $start - 1; // Days of previous month in calander
	
	$counter = 1;
	$nextMonthCounter = 1;
	
	if($start > 5){	$rows = 6; }else {$rows = 5; }

	for($i = 1; $i <= $rows; $i++){
		echo '<tr class="week">';
		for($x = 1; $x <= 7; $x++){				
			
			if(($counter - $start) < 0){
				$date = (($lastmonth - $laststart) + $counter);
				$class = 'class="blur"';
			}else if(($counter - $start) >= $days){
				$date = ($nextMonthCounter);
				$nextMonthCounter++;
				
				$class = 'class="blur"';
					
			}else {
				$date = ($counter - $start + 1);
				if($today == $counter - $start + 1){
					$class = 'class="today"';
				}
			}
				
			
			echo '<td '.$class.'><a class="date">'. $date . '</a></td>';
		
			$counter++;
			$class = '';
		}
		echo '</tr>';
	}
	
?>
</table>
</section>	

If you wanted to add things like 'st' to each date, or even the day to each date you could use a case statement to switch on the $x variable - 1 being Monday, 2 being Tuesday, and so on. And that's that! Don't forget to style the classes that I added such as "blur" to those days that don't fall in the current month, as well as "today" to, well I'm sure you can figure that one out 😉 And as always check out the demo here.