How to Create Your Own Twitter Widget in PHP, Part 1

Twitter status widgets are ten a penny, so why create your own? Because you can! Your own widget will always be more customizable than any off-the-shelf solution, and you’ll be the envy of your peers. We’ve also been asked by several readers for articles about the topic, and it’s a great introduction to PHP, REST APIs, JSON, regular expressions and Object Orientated Programming.

Twitter widget in PHP

The Challenge

This is a 3-part tutorial based on source code you can download here. Our objectives are to:

    • Interrogate the Twitter API and fetch any number of status updates for an individual user.
    • Apply the data to a configurable HTML template and convert URLs, @id and #hashtags to proper links.
    • Format dates into a friendlier format, e.g. posted ten minutes ago, yesterday, two weeks ago, etc.
    • Cache the widget HTML so the fetching process is not required during every page load.
    • Make the resulting widget work in all browsers — yes, that includes IE6!

      The Twitter API

      Twitter provides a REST API. The following URL fetches the last N tweets posted by a specific user:

      http://twitter.com/statuses/user_timeline/user_id.format?count=N

      Where:

      • user_id is the Twitter user name
      • format is either json, xml, rss or atom
      • N is the number of status updates to return

      Therefore, the following URL returns Sitepoint’s last 10 updates in XML format:

      http://twitter.com/statuses/user_timeline/sitepointdotcom.xml?count=10

      JSON vs XML?

      The future of XML is a hot discussion topic, especially following Twitter’s decision to drop the format from their Streaming API in favor of JSON. I won’t get into that debate here, but my original intention had been to use the XML feed and transform it to HTML using XSLT. That became a little too convoluted–primarily because links within a tweet must be resolved using regular expressions. That would have been possible using XSLT 2.0 but, unfortunately, PHP’s libxslt only supports XSLT 1.0.

      I therefore choose JSON; it’s a less-verbose format and can be decoded by PHP’s json_decode() function.

      The TwitterStatus Class

      The widget functionality is wrapped in a single class named TwitterStatus (refer to the twitter/twitterstatus.php file in the download). Seven public and one private property is defined:

      
      class TwitterStatus
      {
      
      	public $ID;				// twitter user name
      	public $Count;			// tweets to fetch
      	public $WidgetTemplate;	// widget template
      	public $TweetTemplate;	// template for each tweet
      	public $ParseLinks;		// parse links in Twitter status
      	public $DateFormat;		// PHP or "friendly" dates
      	public $CacheFor;		// number of seconds to cache feed
      
      	private $cache;			// location of cache files
      
      note: Public and Private?

      If you’re new to Object-Orientated Programming, a public property (variable) or method (function) can be accessed from outside the class, i.e. we can set and examine the $ID value from any code no matter where it resides.

      A private property or method has local scope and only be used within the class itself. The $cache property can be set and examined within the TwitterStatus class, but it’s not visible elsewhere.

      PHP also provides protected properties and methods. We’re not using those here, but protected members are visible within the class and to inherited and parent classes.

      We now require a constructor function which runs when a new TwitterStatus object is created. Our constructor simply sets each property to a default value:

      
      // constructor
      public function __construct($id = null, $count = 0) {
      
      	// constants
      	$this->cache = __DIR__ . '/cache/';	// cache location
      	$this->CacheFor = 900;				// cache feed for 15 minutes
      
      	$this->ID = $id;
      	$this->Count = $count;
      	$this->ParseLinks = true;
      	$this->DateFormat = 'friendly';
      
      	// default widget template
      	$this->WidgetTemplate =
      		'<div class="twitterstatus">' .
      		'<h2><a href="http://twitter.com/{screen_name}"><img src="{profile_image_url}" width="24" height="24" alt="{name}" />{name}</a></h2>' .
      		'<ul>{TWEETS}</ul>' .
      		'</div>';
      
      	// default tweet template
      	$this->TweetTemplate =
      		'<li>{text} <em>{created_at}</em></li>';
      
      }
      

      The only private property is $cache–the directory where we’ll store cached versions of the widget so we’re not interrogating the Twitter API during every page load. It’s set to the directory named ‘cache’ within the directory where twitterstatus.php resides–it’ll need to have appropriate read/write permissions set.

      The $ID and $Count properties can be set when a TwitterStatus object is instantiated, e.g.,

      
      $t = new TwitterStatus('sitepointdotcom', 10);
      

      or we can change the properties separately, e.g.,

      
      $t = new TwitterStatus();
      $t->ID = 'sitepointdotcom';
      $t->Count = 10;
      

      If $ParseLinks is set to true, our code will translate links within the tweet to HTML anchor tags.

      $DateFormat can be set to a PHP date() format, e.g.

      
      $t->DateFormat = 'g:ia j F Y'; // 1:15pm 27 January 2011
      

      Alternatively, $DateFormat can be set to “friendly” (the default string) to indicate we want friendly dates (ten minutes ago, yesterday, last week, etc.)

      Finally, we set two HTML templates:

      • $WidgetTemplate is the widget’s outer HTML. The {TWEETS} code indicates where one or more $TweetTemplate blocks will appear.
      • $TweetTemplate is the HTML for an individual tweet.

      Either property can include {named-values} from the Twitter feed, e.g. {text}, {source}, {name}, {location}, {profile_image_url}, {statuses_count} etc. It’s easiest to view the XML version of the feed to locate named values.

      The developer can therefore specify their own widget HTML, e.g.,

      
      $t->WidgetTemplate =
      	'<div>' .
      	'<img src="{profile_image_url}" />' .
      	'<strong>{name}</strong>'
      	'<ul>{TWEETS}</ul>' .
      	'</div>';
      
      $t->TweetTemplate =
      	'<li>Tweet {statuses_count}: {text} {created_at}</li>';
      

      Fetching the Twitter Feed

      We can now implement a private method to fetch the Twitter status data using PHP’s Client URL (cURL) library:

      
      private function FetchFeed() {
      
      	$r = '';
      	if ($this->ID != '' && $this->Count > 0) {
      		// fetch feed
      		$c = curl_init();
      		curl_setopt_array($c, array(
      			CURLOPT_URL => 'http://twitter.com/statuses/user_timeline/' . $this->ID . '.json?count=' . $this->Count,
      			CURLOPT_HEADER => false,
      			CURLOPT_TIMEOUT => 10,
      			CURLOPT_RETURNTRANSFER => true
      		));
      		$r = curl_exec($c);
      		curl_close($c);
      	}
      
      	// return JSON as array
      	return (!!$r ? json_decode($r, true) : false);
      }
      

      A number of cURL options are set, but the most important is the timeout. We’re allowing ten seconds for a Twitter response and, if that’s exceeded, PHP will give up and the function will return false.

      If everything works as expected, the result is parsed using json_decode() which returns an array of associative arrays matching the JSON data.

      We’ve covered a lot of ground today so it’s time for a break. In the next post, we’ll examine the code which transforms Twitter data into HTML code.

      Free book: Jump Start HTML5 Basics

      Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

      • http://www.budnmarys.com syxx

        Cool example and use of PHP! We use a facebook app that is similar to this and shows all our facebook posts with pictures/texts and the ability to friend us. It’s slowly increasing our daily friend count which is awesome! I have a button to add us to twitter as well and may start using this, however we don’t use twitter as much to be honest, I have a utility that automatically tweets some of our updates and such which is about all we use it for!

      • fattyjules

        Sorry to pick nits, but please consider changing REST API to HTTP API.

        When Roy Fielding invented the term, he specified that all REST responses contain a list to possible client actions (as links). The Twitter API doesn’t do that, nor do many other web APIs that are wrongfully called REST.

      • FATAL ERROR??!?!?

        Fatal error: Call to undefined function curl_init() in /home/MYWEBSITE/www/twitter/twitterstatus.php on line 112??!?!!

        • ahallicks

          Looks like you don’t have cURL installed on your web server.

        • http://www.optimalworks.net/ Craig Buckler

          cURL is a PHP extension which needs to be loaded otherwise the functions won’t be available. See your PHP documentation for information about enabling it.

      • Justen

        JSON was a much better choice. XML is nice if you have a specific schema that must be adhered to; otherwise it’s a waste of bandwidth and obtuse to boot.

        • http://www.optimalworks.net/ Craig Buckler

          I still think XML would have been a great choice had I been able to create a simple XSLT document. In this particular situation, the bandwidth differences were fairly negligible because of the fairly small message size and infrequent polling.

          Watch out for a few articles on SitePoint soon which compare the pros and cons of web service data formats.

      • Zoe

        While tweets are displaying properly, I’m getting this error right above them.

        Warning: file_put_contents(__DIR__/cache/CachedFileName.html) [function.file-put-contents]: failed to open stream: No such file or directory in /home/content/07/5841707/html/twitterwidget/twitter/twitterstatus.php on line 88

        Where “CachedFileName” was an item I cleared from the cache before uploading the files to my site. I tried copying the cached file it’s looking for into the cache folder on my site. Everything works perfectly, locally…

        • http://www.optimalworks.net/ Craig Buckler

          Hi Zoe,

          I think it depends on the OS you’re using, but try changing line 31 from:

          $this->cache = __DIR__ . ‘/cache/’; // cache location

          to:

          $this->cache = ‘cache/’; // cache location

          Otherwise, I suspect it’s file permissions. Ensure you chmod the cache folder to 755 (or possibly 777 if it still fails).

          • Zoe

            I did as you recommended, got an alert that I was dividing by 0, took out the slash right after cache, and it seems to be working just fine now.

            Thanks, Craig!

      • alex

        Hi,

        I am trying this tutorial and i get the message

        Fatal error: Cannot use object of type stdClass as array i

        My php version is 5.1.6
        ( so i had to use dirname(__FILE__) vs __DIR___ , and i had to use jsonwrapper)

        How do transfer object to use as array???

        regards

      • bwaindwain

        Maybe everybody already knows this but I just figured it out. If you’re testing behind a proxy server, curl functions won’t work. In twitterstatus.php, line 118 I added:

        CURLOPT_PROXY => “http://proxy.domain.com”,
        CURLOPT_PROXYPORT => 8080

        don’t forget to add a comma at the end of line 117

        cheers

      • http://www.brandoncarson.com Brandon Carson

        This is an excellent tool, Craig! Thank you very much for sharing. Has anyone had luck with adding a re-tweet option for each tweet?