Get Twitter RSS Feeds with CakePHP

This article is also available in the following languages:
By Firecreek
Using CakePHP model get your RSS twitter feeds with caching and limits.

Installation


  1. Save this script below into your models directory
  2. Change your Twitter ID and Twitter Name
  3. Add to your $uses controller variable, public $uses = array('Twitter')
  4. Add to your controller method, $this->set('twits',$this->Twitter->find());
  5. Then add to your view


Model Class:

<?php 
    
/**
     * Get Twitter Updates
     *
     * $twits = $this->Twitter->find(array('cache'=>true,'limit'=>8));
     *
     * @author        Darren Moore, zeeneo@gmail.com
     * @link          http://www.zeen.co.uk
     */
    
class Twitter extends AppModel
    
{
        
/**
         * Your Twitter ID
         *
         * @var integer
         * @access public
         */
        
public $twitterId 14210082;
        
        
/**
         * Remove your name from posts
         * Set to false to not remove your name, otherwise set to your name
         *
         * @var mixed
         * @access public
         */
        
public $twitterName 'zeeneo';
        
        
/**
         * Show replies to people
         *
         * @var boolean
         * @access public
         */
        
public $showReplies false;
        
        
/**
         * Twitter RSS URL
         *
         * @var string
         * @access public
         */
        
public $rssUrl 'http://twitter.com/statuses/user_timeline/:twitterId.rss';
        
        
/**
         * Turn off table usage
         *
         * @var string
         * @access public
         */
        
public $useTable false;
        
        
/**
         * Duration of cache
         *
         * @var string
         * @access public
         */
        
public $cacheDuration '+30 mins';
    
    
        
/**
         * Find Twitters
         *
         * @param array $options Options when getting twits, as followed:
         *                          - cache: Force caching on or off
         *                          - limit: Limit number of records returned
         * @access public
         * @return array
         */
        
public function find($options = array())
        {
            
//Get twits
            
if((isset($options['cache']) && $options['cache'] == false) || ($twits Cache::read('Twitter.lines')) == false)
            {
                
$twits $this->_getTwits();
                
Cache::set(array('duration' => $this->cacheDuration));
                
Cache::write('Twitter.lines',$twits);
            }
            
            
//Set to limit
            
if(isset($options['limit']) && count($twits) > $options['limit'])
            {
                
$twits array_slice($twits0$options['limit']);
            }
            
            return 
$twits;
        }
        
        
/**
         * Get Twitter Lines
         * 
         * @access private
         * @return array
         */
        
private function _getTwits()
        {        
            
//Get feed
            
$ch curl_init();
            
curl_setopt($chCURLOPT_URL,String::insert($this->rssUrl,array('twitterId'=>$this->twitterId)));
            
curl_setopt($chCURLOPT_CONNECTTIMEOUT2);
            
curl_setopt($chCURLOPT_RETURNTRANSFERtrue);
            
$feed curl_exec($ch);
            
curl_close($ch);
            
            if(!
$feed) { return false; }
            
            
$xml = new SimpleXmlElement($feed);
            
            foreach(
$xml->channel->item as $item)
            {
                
//
                
$title = (string)$item->title;
            
                
//Skip if it's a reply
                
if(!$this->showReplies && preg_match('/^'.$this->twitterName.': @/',$title))
                    continue;
            
                
//Remove name
                
if($this->twitterName)
                    
$title trim(preg_replace('/^'.$this->twitterName.':/','',$title));
            
                
$out[] = array(
                    
'title' => $title,
                    
'description' => (string)$item->description,
                    
'pubDate' => strtotime($item->pubDate),
                    
'link' => (string)$item->link
                
);
            }
            
            return 
$out;
        }
    
    }

?>

Comments

  • Posted 05/31/10 06:52:38 PM
    this was exactly what I was looking for, thanks!
  • Posted 02/22/10 04:33:59 PM
    From http://workbench.cadenhead.org/twitter-rss-to-html/. Turn urls and users into hyperlinks.


    // turn URLs into hyperlinks
    $tweet = preg_replace("/(http:\/\/)(.*?)\/([\w\.\/\&\=\?\-\,\:\;\#\_\~\%\+]*)/", "<a href=\"\\0\">\\0</a>", $tweet);
    // link to users in replies
    $tweet = preg_replace("(@([a-zA-Z0-9\_]+))", "<a href=\"http://www.twitter.com/\\1\">\\0</a>", $tweet);
  • Posted 11/04/09 04:08:46 PM
    Thanks for the classes
  • Posted 08/28/09 01:32:10 PM
    Just like Rahil Sondhi I seem to be getting an empty array. The only problem is that this only happens when accessing online, not when my app runs locally. I first figured this had to do something with the cache, but honestly I'm stuck right now.

    The frustrating thing to me is that from time to time it will work, yet when I visit half an hour later, it won't.

    Does anybody have an explanation for me? I a using the optimized model by Mark.

    Controller:

    <?php 
    class TweetsController extends AppController {

        public 
    $uses = array('Twitter');
        var 
    $name 'Tweets';

        function 
    index(){
           
    $export $this->Twitter->find('stefanvanaken',array('limit'=>6));
           return 
    $export;
        }

    ?> 

    Element:

    <div id="twitter_box">
        <?=$html->link($html->image('layout/twitter.png',array('border'=>0,'alt'=>'@stefanvanaken','class'=>'event_logo')),'http://twitter.com/stefanvanaken',array('target'=>'_blank'),false,false);?>
        
        <? $tweets = $this->requestAction('tweets/index'); ?>
        
        <?     foreach ($tweets as $tweet) { ?>
            <p><span style="font-size:10px;margin-bottom:10px;"><?=date('d-m-Y \o\m G:i',$tweet['pubDate']);?></span><br>
                <?=$tweet['title'];?>
            </p>
        <?     } ?>
    </div>

    Thanks a million!
  • Posted 08/27/09 11:54:02 AM
    This doesn't work. I get an empty array.

    Controller Class:

    <?php 
    $lastTweet 
    $this->Twitter->find('myTwitterUsername', array('cache'=>true,'limit'=>10));
    debug($lastTweet);
    ?>
  • Posted 07/13/09 02:59:55 PM
    For everybody who needs an even more powerful twitter model:
    - supports channelInfos, twitterName (cached as well)
    - now multiple find($accountId) possible (auto-reset feature)
    - you can retrieve tweets by either twitterId or twitterName
    - error logging for severe errors

    <?php 
        
    /**
         * Get Twitter Updates
         * $twits = $this->Twitter->find($id, array('cache'=>true,'limit'=>8));
         * NOTE: don't forget to sanitize or escape output: h() should work for most cases
         */
        
    class Twitter extends AppModel {
            
    /**
             * Twitter ID
             * @var integer/string
             */
            
    private $twitterId null;
            
            
    /**
             * Twitter Name
             * @var string
             */
            
    private $twitterName '';

            
    /**
             * Remove twitterName from posts
             * Set to false to not remove your name, otherwise set to your name 
             * @var boolean
             */        
            
    private $removeTwitterName false;
            
            
    /**
             * Channel info
             * @var array
             */
            
    private $twitterChannel = array();
                    
            
    /**
             * Show replies to people
             * @var boolean
             */
            
    private $showReplies false;
            
            
    /**
             * Twitter RSS URL
             * @var string
             */
            
    private $rssUrl 'http://twitter.com/statuses/user_timeline/:twitterId.rss';
            
            
    /**
             * Turn off table usage
             * @var string
             * @access public
             */
            
    public $useTable false;
            
            
    /**
             * Duration of cache
             * @var string
             */
            
    private $defaultDuration '+30 mins';
            private 
    $cacheDuration '';

            
    /**
             * Duration of cache
             * @var string
             */
            
    private $logErrors true;
                
            
    /**
             * Error
             * @var string
             */
            
    private $error '';
          
        
        
            public function 
    __construct() {
                
    parent::__construct();
                
    $this->reset(true);
                
                
    # try to get default id from configs
                
    $id Configure::read('Twitter.id');
                if (!empty(
    $id)) {
                    
    $this->setAccount($id);
                   }
                   
                   
    # set the cacheDuration to default one (can be overridden by setDuration() later on)
                   
    $duration Configure::read('Twitter.cache_duration');
                   if (!empty(
    $duration)) {
                       
    $this->cacheDuration $duration;
                } else {
                    
    $this->cacheDuration $this->defaultDuration;
                }
            }
            
            
    /**
             * reset just error message or all values
             * @param boolean $resetWholeAccount (default: false)
             */
            
    public function reset($resetAccount false) {
                
    $this->error '';
                if (
    $resetAccount) {
                    
    $this->twitterId null;
                    
    $this->twitterName '';
                    
    $this->twitterChannel = array(
                        
    'title' => '',
                        
    'link' => '',
                        
    'description' => '',
                        
    'language' => '',
                        
    'ttl' => '',    
                    );
                }
            }

            
            
    /**
             * @param int/string $account: twitter id or twitter name
             * @param string $name: twitter name (optional)
             */
            
    public function setAccount($account$name null) {
                if (empty(
    $account)) {
                    return 
    false;
                }
                
                
    $this->reset(true);
                
    $this->twitterId $account;
                if (!empty(
    $name)) {
                    
    $this->twitterName $name;
                } elseif (!
    is_numeric($account) || strlen($account) != 8) {
                    
    $this->twitterName $account;
                }
                return 
    true;
            }
            
            
    /**
             * retrieve current twitter accountName
             */
            
    public function name() {
                return 
    $this->twitterName;
            }
     
             
    /**
             * retrieve twitter channelInfos for current account
             */       
            
    public function channel() {
                return 
    $this->twitterChannel;
            }
            
            
    /**
             * enable/disable logging of (severe) errors
             */
            
    public function logErrors($value true) {
                
    $this->logErrors = (bool)$value;
            }

            
    /**
             * enable/disable removal of twitterName from 'twitterName: xyz' (defaults to FALSE)
             */
            
    public function removeName($value true) {
                
    $this->removeTwitterName = (bool)$value;
            }        
            
            
    /**
             * setDuration with string '+x Minutes' etc or with int 'x' seconds
             * tip: null as param resets duration
             */
              
    public function setDuration($cacheDuration null) {
                  if (
    $cacheDuration === null) {
                      
    $cacheDuration $this->defaultDuration;
                  }
                   
    $this->cacheDuration $cacheDuration;
                   return 
    true;
            }      
                
            
    /**
             * Find Twitters
             * @param int/string $account: twitter id or twitter name (optional)
             *  - leave empty if defined before (or if set via Configure::write('Twitter.id'))
             * @param array $options: Options when getting twits, as followed:
             *  - cache: Force caching on or off
             *  - limit: Limit number of records returned
             *  - duration: Cache Duration (if caching is on)
             * @access public
             * @return array
             */
            
    public function find($account null$options = array()) {
                
    $this->setAccount($account);
                
                if (empty(
    $this->twitterId)) {
                    
    $this->_setError('no twitterAccount found'true);
                    return array();
                }
                
                
    # setDuration
                
    if(!empty($options['duration'])) {
                    
    $this->setDuration($options['duration']);
                }
                
                
    # get twits
                
    if((isset($options['cache']) && $options['cache'] == false) || (($twits $this->_getCachedTwits()) === false)) {
                    
    $twits $this->_getTwits();
                    
    $this->_setCachedTwits($twits);
                }
                
                
    # set to limit
                
    if(isset($options['limit']) && count($twits) > $options['limit'])
                {
                    
    $twits array_slice($twits0$options['limit']);
                }

                return 
    $twits;
            }
            
            
    /**
             * get cached twitterItems, twitterCannel, twitterName
             */
            
    private function _getCachedTwits() {
                
    Cache::set(array('duration' => $this->cacheDuration));
                
    $twits Cache::read('Twitter.'.$this->twitterId.'.lines');
                
                if (
    $twits != false && isset($twits['items'])) {
                    
    $this->twitterChannel $twits['channel'];
                    
    $this->twitterName $twits['name'];
                    
    $this->error $twits['error'];
                    return 
    $twits['items'];
                }         
                return 
    false;
            }
            
            
    /**
             * cache twitterItems, twitterCannel, twitterName
             * @todo can some specialchared twitterNames cause problems in filename???
             * @todo save in subfolder of "cache" (not in root level!)
             */
            
    private function _setCachedTwits($twits) {
                
    $twits = array(
                    
    'items' => $twits,
                    
    'channel' => $this->twitterChannel,
                    
    'name' => $this->twitterName,
                    
    'error' => $this->error,
                );
                
                   
    Cache::write('Twitter.'.$this->twitterId.'.lines',$twits);
                   return 
    true;
               }

            
    /**
             * Get Twitter Lines
             * @access private
             * @return array
             */
            
    private function _getTwits() {
                
    $out = array();
                
                
    # Get feed
                
    $ch curl_init();
                
    curl_setopt($chCURLOPT_URL,String::insert($this->rssUrl,array('twitterId'=>$this->twitterId)));
                
    curl_setopt($chCURLOPT_CONNECTTIMEOUT2);
                
    curl_setopt($chCURLOPT_RETURNTRANSFERtrue);
                
    $feed curl_exec($ch);
                
    curl_close($ch);
                
                if(!
    $feed) { 
                    
    $this->_setError('Invalid request'true);
                    return array(); 
                }
                
                
    $xml = new SimpleXmlElement($feed);

                if (!empty(
    $xml->error)) {
                    
    $this->_setError((string)$xml->error); # usually no log entry neccessary
                    
    return array();
                }
                
                
    $this->twitterChannel = array(
                    
    'title' => (string)$xml->channel->title,
                    
    'link' => (string)$xml->channel->link,
                    
    'description' => (string)$xml->channel->description,
                    
    'language' => (string)$xml->channel->language,
                    
    'ttl' => (int)$xml->channel->ttl,
                );
                
                
    $twitterName trim(str_replace('http://twitter.com/','',$xml->channel->link));
                if (!empty(
    $twitterName)) {
                    
    $this->twitterName $twitterName;
                }
                foreach(
    $xml->channel->item as $item) {
                    
    $title = (string)$item->title;
                
                    
    //Skip if it's a reply
                    
    if(!$this->showReplies && preg_match('/^'.$this->twitterName.': @/',$title))
                        continue;
                
                    
    //Remove name
                    
    if($this->removeTwitterName && !empty($this->twitterName)) {
                        
    $title trim(preg_replace('/^'.$this->twitterName.':/','',$title));
                    }
                    
    $out[] = array(
                        
    'title' => $title,
                        
    'description' => (string)$item->description,
                        
    'pubDate' => strtotime($item->pubDate),
                        
    'link' => (string)$item->link
                    
    );
                }

                return 
    $out;
            }

            
    /**
             * return latest error (if any)
             * @return string
             */
            
    public function error() {
                return 
    $this->error;
            }
            
            private function 
    _setError($error$log false) {
                
    $this->error $error;
                if (
    $log && $this->logErrors) {
                    
    $this->log('ID: '.(string)$this->twitterId.' - '.$error,'twitter');
                }
            }

        }
    ?>


    Usage:
    <?php
    $this
    ->Twitter->setAccount($account);
    $this->set('twits',$this->Twitter->find());

    or

    Configure::write('Twitter.id'$account); // in configs e.g.
    $this->set('twits',$this->Twitter->find());

    or 
    just

    $this
    ->set('twits',$this->Twitter->find($account$options));
    ?>
  • Posted 07/12/09 04:03:18 PM
    by the way:
    Test-Cases are always a great way to ensure the model is working correctly:

    <?php 
    App
    ::import('Model''Twitter');

    class 
    TwitterTestCase extends CakeTestCase {
        var 
    $Twitter null;
        
        function 
    startTest() {
            
    $this->Twitter =& ClassRegistry::init('Twitter');
        }

        function 
    testTwitterInstance() {
            
    $this->assertTrue(is_a($this->Twitter'Twitter'));
        }

        function 
    testTwitterFind() {
            
    # valid ID
            
    $id 14210082
            
    $this->Twitter->setAccount($id);
            
            
    $results $this->Twitter->find();
            
    $this->assertTrue(!empty($results));
            
            
    pr ($results);
            
            
    # invalid ID
            
    $id 14210081
            
    $this->Twitter->setAccount($id);
            
    $results $this->Twitter->find();
            
    $this->assertTrue(empty($results));
            
            
    $error $this->Twitter->error();
            
    $this->assertTrue(!empty($error));
            
    pr ($error);
        }
    }
    ?>

  • Posted 07/12/09 03:44:02 PM
    i enhanced the code (+ fixed some stuff that would have thrown warnings etc..):
    - reset + indirect access functionality
    - several twitterAccounts possible
    - error handling (+ retrieval)


    <?php
        
    class Twitter extends AppModel {
            
    /**
             * Twitter ID
             *
             * @var integer
             */
            
    private $twitterId null;
            
            
    /**
             * Remove your name from posts
             * Set to false to not remove your name, otherwise set to your name
             *
             * @var mixed
             */
            
    private $twitterName '';
            
            
    /**
             * Show replies to people
             *
             * @var boolean
             */
            
    private $showReplies false;
            
            
    /**
             * Twitter RSS URL
             *
             * @var string
             */
            
    private $rssUrl 'http://twitter.com/statuses/user_timeline/:twitterId.rss';
            
            
    /**
             * Turn off table usage
             *
             * @var string
             * @access public
             */
            
    public $useTable false;
            
            
    /**
             * Duration of cache
             *
             * @var string
             */
            
    private $cacheDuration '+30 mins';

            
    /**
             * Duration of cache
             *
             * @var string
             */
            
    private $logErrors true;
                
            
    /**
             * Error
             *
             * @var string
             */
            
    private $error '';
          
        
        
            public function 
    __construct() {
                
    parent::__construct();
                
    # try to get default id from configs
                
    $id Configure::read('Twitter.id');
                if (!empty(
    $id)) {
                    
    $this->setId();
                   }
            }
            
            public function 
    reset($resetAccount false) {
                
    $this->error '';
                if (
    $resetAccount) {
                    
    $this->twitterId null;
                }
            }
            
            public function 
    setAccount($id$name null) {
                
    $this->reset();
                
    $this->twitterId $id;
                if (!empty(
    $name)) {
                    
    $this->twitterName $name;
                }
            }
            
            public function 
    logErrors($log true) {
                
    $this->logErrors $log;
            }
            
              public function 
    setDuration($cacheDuration) {
                   
    $this->cacheDuration $cacheDuration;
            }      
                
        
            
    /**
             * Find Twitters
             *
             * @param array $options Options when getting twits, as followed:
             *                          - cache: Force caching on or off
             *                          - limit: Limit number of records returned
             *                             - duration: Cache Duration (if caching is on)
             * @access public
             * @return array
             */
            
    public function find($options = array()) {
                if (empty(
    $this->twitterId)) {
                    
    $this->_setError('no twitterAccount found'true);
                    return array();
                }
                
                
    # setDuration
                
    if(!empty($options['duration'])) {
                    
    $this->setDuration($options['duration']);
                }
                
                
    # Get twits
                
    if((isset($options['cache']) && $options['cache'] == false) || ($twits Cache::read('Twitter.'.$this->twitterId.'.lines')) == false)
                {
                    
    $twits $this->_getTwits();
                    
    Cache::set(array('duration' => $this->cacheDuration));
                    
    Cache::write('Twitter.'.$this->twitterId.'.lines',$twits);
                }
                
                
    # Set to limit
                
    if(isset($options['limit']) && count($twits) > $options['limit'])
                {
                    
    $twits array_slice($twits0$options['limit']);
                }
                
                return 
    $twits;
            }
            
            
    /**
             * Get Twitter Lines
             * 
             * @access private
             * @return array
             */
            
    private function _getTwits() {
                
    $out = array();
                
                
    # Get feed
                
    $ch curl_init();
                
    curl_setopt($chCURLOPT_URL,String::insert($this->rssUrl,array('twitterId'=>$this->twitterId)));
                
    curl_setopt($chCURLOPT_CONNECTTIMEOUT2);
                
    curl_setopt($chCURLOPT_RETURNTRANSFERtrue);
                
    $feed curl_exec($ch);
                
    curl_close($ch);
                
                if(!
    $feed) { 
                    
    $this->_setError('invalid request'true);
                    return array(); 
                }
                
                
    $xml = new SimpleXmlElement($feed);
                
                if (!empty(
    $xml->error)) {
                    
    $this->_setError((string)$xml->error); # usually no log entry neccessary
                    
    return array();
                }
                
                foreach(
    $xml->channel->item as $item) {
                    
    //
                    
    $title = (string)$item->title;
                
                    
    //Skip if it's a reply
                    
    if(!$this->showReplies && preg_match('/^'.$this->twitterName.': @/',$title))
                        continue;
                
                    
    //Remove name
                    
    if($this->twitterName) {
                        
    $title trim(preg_replace('/^'.$this->twitterName.':/','',$title));
                    }
                    
    $out[] = array(
                        
    'title' => $title,
                        
    'description' => (string)$item->description,
                        
    'pubDate' => strtotime($item->pubDate),
                        
    'link' => (string)$item->link
                    
    );
                }
                
                return 
    $out;
            }
        
            
    /**
             * return error (if any)
             * @return string
             */
            
    public function error() {
                return 
    $this->error;
            }
            
            private function 
    _setError($error$log false) {
                
    $this->error $error;
                if (
    $log && $this->logErrors) {
                    
    $this->log('ID: '.(string)$this->twitterId.' - '.$error,'twitter');
                }
            }
        
        }
    ?>
  • Posted 07/08/09 12:05:58 PM
    Hi people.

    When I get a spare 10 minutes I'll update this code to use HttpSocket, optional table caching and a few other clean ups, like using the word 'tweet' instead of 'twit' :)
  • Posted 03/11/09 07:05:42 AM
    Disclaimer - I am 1 day old to CakePhp and trying to pick it up for my next app.

    That said.
    1. I created the model as you've instructed called twitter.php under models folder
    2. I changed the twitter id and the twitter username
    3. I created a controller called TwittersController under controller folder
    4. Added the code you mentioned, whch is here
    class TwittersController extends Controller{
    var $uses= array("Twitter"); // this is the model to use
    //var $autoRender=false; // this specifies the view to use

    function tweet(){
    $this->set('twits',$this->Twitter->find());
    }
    }
    ?> 5. I am not using any view for now.

    When I run this as
    http://localhost/cake/twitters/tweet

    it gives me an error stating:
    Fatal error: Call to undefined function curl_init() in C:\xampp\htdocs\cake\app\models\twitter.php on line 99

    Thanks for help in advance
    • Posted 07/08/09 12:03:24 PM
      SNIP

      it gives me an error stating:
      Fatal error: Call to undefined function curl_init() in C:\xampp\htdocs\cake\app\models\twitter.php on line 99

      Thanks for help in advance

      You need to have curl installed on your server. I will update this script soon to use HttpSocket
  • Posted 03/06/09 05:30:28 AM
    I do not understand well enough, if you are cleaning the data anywhere? No matter if twitter or any other source - if you do not check and sanitize the data you pull from other sites, you will open your site to potential attacks. Is the data cleaned automagically by cake in this case? If yes, please explain. If not, please change the code.
    I think it would be quite important to keep an eye on the quality of tutorials that are posted here. People will adapt bad techniques published here and problems will fall back on the cakephp project as "not beeing safe" or something. Thanks!
    • Posted 07/08/09 12:02:24 PM
      I do not understand well enough, if you are cleaning the data anywhere? No matter if twitter or any other source - if you do not check and sanitize the data you pull from other sites, you will open your site to potential attacks. Is the data cleaned automagically by cake in this case? If yes, please explain. If not, please change the code.
      I think it would be quite important to keep an eye on the quality of tutorials that are posted here. People will adapt bad techniques published here and problems will fall back on the cakephp project as "not beeing safe" or something. Thanks!

      I have not included a view example. It is up to you how you escape the output and you'll do this in your view file.
  • Posted 01/21/09 06:59:49 AM
    I've modified this code to allow for multiple accounts to be integrated into one feed (currently using on a band's website). I'll post the code if anyone wants it..
  • Posted 01/17/09 09:40:25 AM
    This was exactly what I was looking for, thanks!

Comments are closed for articles over a year old