Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
tree: 3db7f4adb7
Fetching contributors…

Octocat-spinner-32-eaf2f5

Cannot retrieve contributors at this time

file 221 lines (200 sloc) 6.803 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
<?php
/**
* Phing Task object for updating a Twitter status
*
* @author David Winterbottom <david.winterbottom@gmail.com>
* @license: Creative Commons Attribution-ShareAlike 2.5 <http://creativecommons.org/licenses/by-sa/2.5/>
*/

require_once "phing/Task.php";

/**
* Sends an update to a Twitter account
*
* @author David Winterbottom <david@codeinthehole.com>
* @version $Id$
* @package phing.tasks.ext
*/
class TwitterUpdateTask extends Task
{
    const URL_TEMPLATE_UPDATE = 'http://twitter.com/statuses/update.xml?status=%s';
    const MAXIMUM_MESSAGE_LENGTH = 140;
    
    // Twitter response codes
    const HTTP_RESPONSE_SUCCESS = 200;
    const HTTP_RESPONSE_NOT_MODIFIED = 304;
    const HTTP_RESPONSE_BAD_REQUEST = 400;
    const HTTP_RESPONSE_BAD_CREDENTIALS = 401;
    const HTTP_RESPONSE_FORBIDDEN = 403;
    const HTTP_RESPONSE_BAD_URL = 404;
    const HTTP_RESPONSE_SERVER_ERROR = 500;
    const HTTP_RESPONSE_BAD_GATEWAY = 502;
    const HTTP_RESPONSE_SERVICE_UNAVAILABLE = 503;
    
    /**
* Friendly translations of the unsuccessful Twitter response codes
*
* See http://apiwiki.twitter.com/REST+API+Documentation#HTTPStatusCodes for more details
*
* @var array
*/
    private static $responseMessages = array(
        self::HTTP_RESPONSE_NOT_MODIFIED => 'Status hasn\'t changed since last update',
        self::HTTP_RESPONSE_BAD_REQUEST => 'Bad request - you may have exceeded the rate limit',
        self::HTTP_RESPONSE_BAD_CREDENTIALS => 'Your username and password did not authenticate',
        self::HTTP_RESPONSE_FORBIDDEN => 'Forbidden request - Twitter are refusing to honour the request',
        self::HTTP_RESPONSE_BAD_URL => 'The Twitter URL is invalid',
        self::HTTP_RESPONSE_SERVER_ERROR => 'There is a problem with the Twitter server',
        self::HTTP_RESPONSE_BAD_GATEWAY => 'Twitter is either down or being upgraded',
        self::HTTP_RESPONSE_SERVICE_UNAVAILABLE => 'Twitter servers are overloaded and refusing request',
    );
    
    /**
* @var string
*/
    private $username;
    
    /**
* @var string
*/
    private $password;
    
    /**
* Tweet message
*
* @var string
*/
    private $message;
    
    /**
* Whether to throw an exception if the update fails
*
* @var boolean
*/
    private $checkReturn = false;
    
    /**
* Username setter
*
* @param string $username
*/
    public function setUsername($username)
    {
        $this->username = $username;
    }
    
    /**
* Password setter
*
* @param string $password
*/
    public function setPassword($password)
    {
        $this->password = $password;
    }
    
    /**
* Tweet message setter
*
* @param string $message
*/
    public function setMessage($message)
    {
        $this->message = trim($message);
    }
    
    /**
* Whether to continue build process if update fails
*
* @param boolean $checkReturn
*/
    public function setCheckReturn($checkReturn)
    {
        $this->checkReturn = (boolean)$checkReturn;
    }
    
    /**
* Checks that the cURL extension is loaded
*/
    public function init()
    {
        if (!extension_loaded('curl')) {
            throw new BuildException("Cannot update Twitter", "The cURL extension is not installed");
        }
    }

    /**
* Make API call to the Twitter API
*
* This creates a cURL connection and makes a POST request to the Twitter API
* to update a status message.
*/
    public function main()
    {
        $this->validateProperties();
        
        $curlHandle = curl_init();
        curl_setopt($curlHandle, CURLOPT_POST, true);
        curl_setopt($curlHandle, CURLOPT_POSTFIELDS, array());
        curl_setopt($curlHandle, CURLOPT_URL, $this->getUpdateUrl());
        curl_setopt($curlHandle, CURLOPT_USERPWD, "$this->username:$this->password");
        curl_setopt($curlHandle, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curlHandle, CURLOPT_HTTPHEADER, array('Expect:'));
        $twitterData = curl_exec($curlHandle);
        $responseCode = curl_getinfo($curlHandle, CURLINFO_HTTP_CODE);
        $errorCode = curl_errno($curlHandle);
        $errorMessage = curl_error($curlHandle);
        curl_close($curlHandle);
        
        if (0 != $errorCode) {
            throw new BuildException("cURL error ($errorCode): $errorMessage");
        }
        $this->handleTwitterResponseCode((int)$responseCode);
    }
    
    /**
* Validates the set properties
*/
    private function validateProperties()
    {
        if (!$this->username || !$this->password) {
            throw new BuildException("You must specify a Twitter username and password");
        }
        if (!$this->message) {
            throw new BuildException("You must specify a message");
        } elseif (strlen($this->message) > self::MAXIMUM_MESSAGE_LENGTH) {
            $this->message = substr($this->message, 0, self::MAXIMUM_MESSAGE_LENGTH);
            $this->log("Message is greater than the maximum message length - truncating...", Project::MSG_WARN);
        }
    }
    
    /**
* Returns the URL for a status update (including the URL-encoded message)
*
* @return string
*/
    private function getUpdateUrl()
    {
        return sprintf(self::URL_TEMPLATE_UPDATE, $this->getEncodedMessage());
    }
    
    /**
* Returns a URL-encoded message
*
* @return string
*/
    private function getEncodedMessage()
    {
        return urlencode(stripslashes(urldecode($this->message)));
    }
    
    /**
* Invokes the appopriate behaviour for each Twitter return code
*
* @param int $code
*/
    private function handleTwitterResponseCode($code)
    {
        if ($code == self::HTTP_RESPONSE_SUCCESS) {
            $this->log("Twitter status updated to: '$this->message'", Project::MSG_INFO);
            return;
        }
        if (array_key_exists($code, self::$responseMessages)) {
            $this->handleFailedUpdate(self::$responseMessages[$code]);
        } else {
            $this->handleFailedUpdate("Unrecognised HTTP response code '$code' from Twitter");
        }
    }
    
    /**
* Invokes appropriate behaviour for a failed update
*
* @param $failureMessage
*/
    private function handleFailedUpdate($failureMessage)
    {
        if (true === $this->checkReturn) {
            throw new BuildException($failureMessage);
        }
        $this->log("Update unsuccessful: $failureMessage", Project::MSG_WARN);
    }
}
Something went wrong with that request. Please try again.