Advertisement
  1. Code
  2. JavaScript
  3. Web APIs

Creating a Twitter OAuth Application

Scroll to top
38 min read

OAuth can be a tricky concept to wrap your head around at first, but with the Twitter API now requiring its use, it is something you need to understand before creating a Twitter application. This tutorial will introduce you to OAuth, and walk you through the process of creating a basic application.


Introduction

In this tutorial, we will be building a simple app that allows users to apply different effects to their Twitter avatar. In order to work with the Twitter API, we must use OAuth to authorize our app to make requests on the user's behalf.

Our application flow will be something like this:

  1. The user is asked to connect with Twitter.
  2. The user is presented a list of preview avatars to select from.
  3. Upon selection, the user is presented a confirmation screen showing the original and new avatar for comparison. The user is also offered an option to send a tweet.
  4. After the user confirms, the app creates and uploads the modified avatar to Twitter and a success page is displayed.

Setup

To start, we should set up our source directory. We need a lib directory for our PHP library (class) files, a tmp directory to keep temporary files (this needs to writable by the server), a css directory for our style sheet, and an img directory for any images.

Here's what your directory tree should look like:

  • tutorial
    • css
    • img
    • lib
    • tmp (writable)

Register Your Application

In order to use OAuth, you'll need what is called a consumer key and secret to identify your app. To obtain these, you must register an application with Twitter by following these steps.

Go to the registration page, logging in if necessary. You'll be greeted by the form pictured below:

Fill out the form with details pertaining to your app. In our case, the Application Type is Browser and we need to set a default Callback URL. This URL can be anything as long as it is a valid format. We will be overriding the callback within our code, so it isn't critical that it is a real URL. The Default Access type should be Read & Write for convenience.

Once you register and accept the terms, you'll be presented with information on your new application. The important details we need are the Consumer key and Consumer secret, which should look something like this:


Download the tmhOAuth Library

We will be making use of a library to handle all the details behind making OAuth requests. In this tutorial, we will be using @themattharris' tmhOAuth library, which supports file uploading.

  1. Download tmhOAuth from GitHub
  2. Extract tmhOAuth.php to the lib directory we created earlier

Authentication

Authentication with OAuth is basically a three step process. For a more in depth explanation, see this page on authentication at Twitter, but here is a summary:

  1. App obtains a request token: The first step is for our app to identify itself to Twitter (using its consumer key) and obtain a request token. We'll need to save this request token for later.
  2. User authorizes app on Twitter: The user now needs to be sent to Twitter in order to grant our app access to their account. After that, the user is sent back to the callback URL specified by the app.
  3. App exchanges request token for access token: Now that our app has been approved, it can exchange the request token from step 1 for an access token. Once we have the access token, our app is free to interact with the Twitter API on the user's behalf.

So let's get started with some code. We'll handle all authentication tasks in a class called TwitterApp. Start with the following code in a new file called lib/TwitterApp.php:

1
<?php
2
class TwitterApp {
3
    /**

4
     * This variable holds the tmhOAuth object used throughout the class

5
     *

6
     * @var tmhOAuth An object of the tmhOAuth class

7
     */
8
    public $tmhOAuth;
9
10
    /**

11
     * User's Twitter account data

12
     *

13
     * @var array Information on the current authenticated user

14
     */
15
    public $userdata;
16
17
    /**

18
     * Authentication state

19
     *

20
     * Values:

21
     *  - 0: not authed

22
     *  - 1: Request token obtained

23
     *  - 2: Access token obtained (authed)

24
     *

25
     * @var int The current state of authentication

26
     */
27
    protected $state;
28
29
    /**

30
     * Initialize a new TwitterApp object

31
     *

32
     * @param tmhOAuth $tmhOAuth A tmhOAuth object with consumer key and secret

33
     */
34
    public function  __construct(tmhOAuth $tmhOAuth) {
35
        
36
        // save the tmhOAuth object

37
        $this->tmhOAuth = $tmhOAuth;
38
    }
39
}

Here we've created three properties and a simple constructor. The $tmhOAuth property will be a tmhOAuth object, which will be used throughout the class. The $userdata property will hold an object containing info on the user such as their Twitter user name and status. The $state property keeps track of the current state of authentication.

The constructor simply accepts a tmhOAuth object and assigns it to the $tmhOAuth property.


Step 1: Get a Request Token

Here is the method to obtain a request token:

1
/**

2
 * Obtain a request token from Twitter

3
 *

4
 * @return bool False if request failed

5
 */
6
private function getRequestToken() {
7
    
8
    // send request for a request token

9
    $this->tmhOAuth->request("POST", $this->tmhOAuth->url("oauth/request_token", ""), array(
10
        // pass a variable to set the callback

11
        'oauth_callback'    => $this->tmhOAuth->php_self()
12
    ));
13
14
    if($this->tmhOAuth->response["code"] == 200) {
15
        
16
        // get and store the request token

17
        $response = $this->tmhOAuth->extract_params($this->tmhOAuth->response["response"]);
18
        $_SESSION["authtoken"] = $response["oauth_token"];
19
        $_SESSION["authsecret"] = $response["oauth_token_secret"];
20
21
        // state is now 1

22
        $_SESSION["authstate"] = 1;
23
24
        // redirect the user to Twitter to authorize

25
        $url = $this->tmhOAuth->url("oauth/authorize", "") . '?oauth_token=' . $response["oauth_token"];
26
        header("Location: ' . $url);

27
        exit;

28
    }

29
    return false;

30
}

To understand the first part, you need to know about the tmhOAuth::request() method. This method allows us to make an OAuth enabled HTTP request, and it has the following usage:

tmhOAuth::request($method, $url[, $params[, $useauth[, $multipart]]])

  • string $method - The request method to use (GET, POST, etc.)
  • string $url - The URL to access
  • array $params (optional) - Associative array of parameters to include in the request
  • bool $useauth (optional, default true) - Is authentication required?
  • bool $multipart (optional, default false) - Set to true for file uploads

For the $url parameter, we make use of the tmhOAuth::url() method to craft a URL based on the API method we are calling:

tmhOAuth::url($request[, $format])

  • string $request - The api method (without extension)
  • string $format (optional, default 'json") - The desired response format (JSON, XML, etc.)

Now that you are familiar with those methods, we must make a POST request to the oauth/request_token API method. This will return OAuth data in a special format, so we need to set the format to blank when we use the tmhOAuth::url() method. We also need to pass a variable called oauth_callback, which is where the user will return after authorizing at Twitter. We will use the tmhOAuth::php_self() method to refer to the current page. Here is that code again:

1
// send request for a request token

2
$this->tmhOAuth->request("POST", $this->tmhOAuth->url("oauth/request_token", ""), array(
3
    // pass a variable to set the callback

4
    'oauth_callback'    => $this->tmhOAuth->php_self()
5
));

Once we make the request, the response is stored as an array in the tmhOAuth::response property, with the following key pieces of data:

  • code - The HTTP response code
  • response - The actual data returned
  • headers - The response headers

So the next part of our code checks the response code (200 means success), and then places the oauth_token and oauth_token_secret that we received into session variables since we'll need them later. These are extracted from the response data using the tmhOAuth::extract_params() method, which returns an array of data contained in the response. We also set an authstate session variable to signal that we are on the next stage of authentication. Here is the code:

1
if($this->tmhOAuth->response["code"] == 200) {
2
3
    // get and store the request token

4
    $response = $this->tmhOAuth->extract_params($this->tmhOAuth->response["response"]);
5
    $_SESSION["authtoken"] = $response["oauth_token"];
6
    $_SESSION["authsecret"] = $response["oauth_token_secret"];
7
8
    // state is now 1

9
    $_SESSION["authstate"] = 1;
10
}

After that is done, we must now redirect the user to the oauth/authorize URL, including the oauth_token in a GET parameter. Here is that code again:

1
// redirect the user to Twitter to authorize

2
$url = $this->tmhOAuth->url("oauth/authorize", "") . '?oauth_token=' . $response["oauth_token"];
3
header("Location: ' . $url);

4
exit;

Step 2: Get an Access Token

Here is the method to exchange our request token for an access token:

1
/**

2
 * Obtain an access token from Twitter

3
 *

4
 * @return bool False if request failed

5
 */
6
private function getAccessToken() {
7
8
    // set the request token and secret we have stored

9
    $this->tmhOAuth->config["user_token"] = $_SESSION["authtoken"];
10
    $this->tmhOAuth->config["user_secret"] = $_SESSION["authsecret"];
11
12
    // send request for an access token

13
    $this->tmhOAuth->request("POST", $this->tmhOAuth->url("oauth/access_token", ""), array(
14
        // pass the oauth_verifier received from Twitter

15
        'oauth_verifier'    => $_GET["oauth_verifier"]
16
    ));
17
18
    if($this->tmhOAuth->response["code"] == 200) {
19
20
        // get the access token and store it in a cookie

21
        $response = $this->tmhOAuth->extract_params($this->tmhOAuth->response["response"]);
22
        setcookie("access_token", $response["oauth_token"], time()+3600*24*30);
23
        setcookie("access_token_secret", $response["oauth_token_secret"], time()+3600*24*30);
24
25
        // state is now 2

26
        $_SESSION["authstate"] = 2;
27
28
        // redirect user to clear leftover GET variables

29
        header("Location: ' . $this->tmhOAuth->php_self());

30
        exit;

31
    }

32
    return false;

33
}

The first thing we do is set the user_token and user_secret in the tmhOAuth::config array to the request token we obtained earlier.

1
// set the request token and secret we have stored

2
$this->tmhOAuth->config["user_token"] = $_SESSION["authtoken"];
3
$this->tmhOAuth->config["user_secret"] = $_SESSION["authsecret"];

The next part is where we make a POST request to oauth/access_token. We pass the oauth_verifier we received in a GET variable as a parameter in this request.

1
// send request for an access token

2
$this->tmhOAuth->request("POST", $this->tmhOAuth->url("oauth/access_token", ""), array(
3
    // pass the oauth_verifier received from Twitter

4
    'oauth_verifier'    => $_GET["oauth_verifier"]
5
));

Twitter will respond with an access token and secret, which we'll need to save for any future requests. So the next chunk of code takes these and saves each in a cookie, then sets the state to 2.

1
if($this->tmhOAuth->response["code"] == 200) {
2
3
    // get the access token and store it in a cookie

4
    $response = $this->tmhOAuth->extract_params($this->tmhOAuth->response["response"]);
5
    setcookie("access_token", $response["oauth_token"], time()+3600*24*30);
6
    setcookie("access_token_secret", $response["oauth_token_secret"], time()+3600*24*30);
7
8
    // state is now 2

9
    $_SESSION["authstate"] = 2;
10
11
    // redirect user to clear leftover GET variables

12
    header("Location: ' . $this->tmhOAuth->php_self());

13
    exit;

14
}

The redirect at the end is there to clear the URL parameters left by Twitter, and allows the cookies to take effect.


Step 3: Verify the Access Token

With our access token obtained, we should check to make sure it is valid. Here is the method to do that:

1
/**

2
 * Verify the validity of our access token

3
 *

4
 * @return bool Access token verified

5
 */
6
private function verifyAccessToken() {
7
    $this->tmhOAuth->config["user_token"] = $_COOKIE["access_token"];
8
    $this->tmhOAuth->config["user_secret"] = $_COOKIE["access_token_secret"];
9
10
    // send verification request to test access key

11
    $this->tmhOAuth->request("GET", $this->tmhOAuth->url("1/account/verify_credentials"));
12
13
    // store the user data returned from the API

14
    $this->userdata = json_decode($this->tmhOAuth->response["response"]);
15
16
    // HTTP 200 means we were successful

17
    return ($this->tmhOAuth->response["code"] == 200);
18
}

This code should look pretty familiar by now. All we do here is set the user_token and user_secret and make a GET request to 1/account/verify_credentials. If Twitter responds with a 200 code, then the access token is valid.

Another detail to note is that this is where we populate the $userdata property with the data returned by this Twitter request. The data is in the JSON format, so we use json_decode() to convert it to a PHP object. Here's that line again:

1
// store the user data returned from the API

2
$this->userdata = json_decode($this->tmhOAuth->response["response"]);

Step 4: Tie Everything Together

With our OAuth components in place, it is time to tie everything together. We need a public facing method to allow our client code to start the authentication process, and here it is:

1
/**

2
 * Authenticate user with Twitter

3
 *

4
 * @return bool Authentication successful

5
 */
6
public function auth() {
7
8
    // state 1 requires a GET variable to exist

9
    if($this->state == 1 && !isset($_GET["oauth_verifier"])) {
10
        $this->state = 0;
11
    }
12
13
    // Step 1: Get a request token

14
    if($this->state == 0) {
15
        return $this->getRequestToken();
16
    }
17
    // Step 2: Get an access token

18
    elseif($this->state == 1) {
19
        return $this->getAccessToken();
20
    }
21
22
    // Step 3: Verify the access token

23
    return $this->verifyAccessToken();
24
}

Most of the auth() method should be self-explanatory. Based on the state, it executes the appropriate method for that stage of authentication. If the state is 1, an oauth_verifier GET variable should exists, so the method also checks that.

We should now create a public method to find out if we are authenticated. This isAuthed() method returns true if the state is 2:

1
/**

2
 * Check the current state of authentication

3
 *

4
 * @return bool True if state is 2 (authenticated)

5
 */
6
public function isAuthed() {
7
    return $this->state == 2;
8
}

We can also use a method to remove the user's authentication. This endSession() method sets the state to 0 and removes the cookies containing the access token:

1
/**

2
 * Remove user's access token cookies

3
 */
4
public function endSession() {
5
    $this->state = 0;
6
    $_SESSION["authstate"] = 0;
7
    setcookie("access_token", "", 0);
8
    setcookie("access_token_secret", "", 0);
9
}

Initialization

Now we need to add some things to our __construct() method to figure out which authentication state the application is in upon initialization. Also, since our code uses session variables, we should make sure the session is started with this code:

1
// start a session if one does not exist

2
if(!session_id()) {
3
    session_start();
4
}

This next part is where we determine the state. State starts at 0; if there are cookies set containing an access token, state is assumed to be 2; failing that, the state is set to the authstate session variable if it exists. Here is the code:

1
// determine the authentication status

2
// default to 0

3
$this->state = 0;
4
// 2 (authenticated) if the cookies are set

5
if(isset($_COOKIE["access_token"], $_COOKIE["access_token_secret"])) {
6
    $this->state = 2;
7
}
8
// otherwise use value stored in session

9
elseif(isset($_SESSION["authstate"])) {
10
    $this->state = (int)$_SESSION["authstate"];
11
}

If the state is 1, that means we are in the process of authentication. So we can go ahead and continue the process at this point:

1
// if we are in the process of authentication we continue

2
if($this->state == 1) {
3
    $this->auth();
4
}

If the state is 2, we should verify the access token. If authentication fails, this code clears the cookies and resets the state:

1
// verify authentication, clearing cookies if it fails

2
elseif($this->state == 2 && !$this->auth()) {
3
    $this->endSession();
4
}

Here is the new constructor with these changes made:

1
/**

2
 * Initialize a new TwitterApp object

3
 *

4
 * @param tmhOAuth $tmhOAuth A tmhOAuth object with consumer key and secret

5
 */
6
public function  __construct(tmhOAuth $tmhOAuth) {
7
8
    // save the tmhOAuth object

9
    $this->tmhOAuth = $tmhOAuth;
10
11
    // start a session if one does not exist

12
    if(!session_id()) {
13
        session_start();
14
    }
15
16
    // determine the authentication status

17
    // default to 0

18
    $this->state = 0;
19
    // 2 (authenticated) if the cookies are set

20
    if(isset($_COOKIE["access_token"], $_COOKIE["access_token_secret"])) {
21
        $this->state = 2;
22
    }
23
    // otherwise use value stored in session

24
    elseif(isset($_SESSION["authstate"])) {
25
    $this->state = (int)$_SESSION["authstate"];
26
    }
27
28
    // if we are in the process of authentication we continue

29
    if($this->state == 1) {
30
        $this->auth();
31
    }
32
    // verify authentication, clearing cookies if it fails

33
    elseif($this->state == 2 && !$this->auth()) {
34
        $this->endSession();
35
    }
36
}

Sending a Tweet

Now that all the authorization code is complete, we can add some common functionality to our class. Here is a method to send a tweet through the Twitter API:

1
/**

2
 * Send a tweet on the user's behalf

3
 *

4
 * @param string $text Text to tweet

5
 * @return bool Tweet successfully sent

6
 */
7
public function sendTweet($text) {
8
9
    // limit the string to 140 characters

10
    $text = substr($text, 0, 140);
11
12
    // POST the text to the statuses/update method

13
    $this->tmhOAuth->request("POST", $this->tmhOAuth->url("1/statuses/update"), array(
14
        'status' => $text
15
    ));
16
17
    return ($this->tmhOAuth->response["code"] == 200);
18
}

The sendTweet() method accepts a string, limits it to 140 characters, and then sends it in a POST request to 1/statuses/update. This pattern should be pretty familiar by now.


The Complete TwitterApp Class

1
<?php
2
class TwitterApp {
3
    
4
    /**
5
     * This variable holds the tmhOAuth object used throughout the class
6
     *
7
     * @var tmhOAuth An object of the tmhOAuth class
8
     */
9
    public $tmhOAuth;
10
11
    /**
12
     * User's Twitter account data
13
     *
14
     * @var array Information on the current authenticated user
15
     */
16
    public $userdata;
17
18
    /**
19
     * Authentication state
20
     *
21
     * Values:
22
     *  - 0: not authed
23
     *  - 1: Request token obtained
24
     *  - 2: Access token obtained (authed)
25
     *
26
     * @var int The current state of authentication
27
     */
28
    protected $state;
29
30
    /**
31
     * Initialize a new TwitterApp object
32
     *
33
     * @param tmhOAuth $tmhOAuth A tmhOAuth object with consumer key and secret
34
     */
35
    public function  __construct(tmhOAuth $tmhOAuth) {
36
        
37
        // save the tmhOAuth object
38
        $this->tmhOAuth = $tmhOAuth;
39
40
        // start a session if one does not exist
41
        if(!session_id()) {
42
            session_start();
43
        }
44
        
45
        // determine the authentication status
46
        // default to 0
47
        $this->state = 0;
48
        // 2 (authenticated) if the cookies are set
49
        if(isset($_COOKIE["access_token"], $_COOKIE["access_token_secret"])) {
50
            $this->state = 2;
51
        }
52
        // otherwise use value stored in session
53
        elseif(isset($_SESSION["authstate"])) {
54
            $this->state = (int)$_SESSION["authstate"];
55
        }
56
        
57
        // if we are in the process of authentication we continue
58
        if($this->state == 1) {
59
            $this->auth();
60
        }
61
        // verify authentication, clearing cookies if it fails
62
        elseif($this->state == 2 && !$this->auth()) {
63
            $this->endSession();
64
        }
65
    }
66
67
    /**
68
     * Authenticate user with Twitter
69
     *
70
     * @return bool Authentication successful
71
     */
72
    public function auth() {
73
        
74
        // state 1 requires a GET variable to exist
75
        if($this->state == 1 && !isset($_GET["oauth_verifier"])) {
76
            $this->state = 0;
77
        }
78
79
        // Step 1: Get a request token
80
        if($this->state == 0) {
81
            return $this->getRequestToken();
82
        }
83
        // Step 2: Get an access token
84
85
        elseif($this->state == 1) {
86
            return $this->getAccessToken();
87
        }
88
89
        // Step 3: Verify the access token
90
        return $this->verifyAccessToken();
91
    }
92
93
    /**
94
     * Obtain a request token from Twitter
95
     *
96
     * @return bool False if request failed
97
     */
98
    private function getRequestToken() {
99
        
100
        // send request for a request token
101
        $this->tmhOAuth->request("POST", $this->tmhOAuth->url("oauth/request_token", ""), array(
102
            // pass a variable to set the callback
103
            'oauth_callback'    => $this->tmhOAuth->php_self()
104
        ));
105
106
        if($this->tmhOAuth->response["code"] == 200) {
107
            
108
            // get and store the request token
109
            $response = $this->tmhOAuth->extract_params($this->tmhOAuth->response["response"]);
110
            $_SESSION["authtoken"] = $response["oauth_token"];
111
            $_SESSION["authsecret"] = $response["oauth_token_secret"];
112
113
            // state is now 1
114
            $_SESSION["authstate"] = 1;
115
116
            // redirect the user to Twitter to authorize
117
            $url = $this->tmhOAuth->url("oauth/authorize", "") . '?oauth_token=' . $response["oauth_token"];
118
            header("Location: ' . $url);
119
            exit;
120
        }
121
        return false;
122
    }
123
124
    /**
125
     * Obtain an access token from Twitter
126
     *
127
     * @return bool False if request failed
128
     */
129
    private function getAccessToken() {
130
        
131
        // set the request token and secret we have stored
132
        $this->tmhOAuth->config["user_token"] = $_SESSION["authtoken"];
133
        $this->tmhOAuth->config["user_secret"] = $_SESSION["authsecret"];
134
135
        // send request for an access token
136
        $this->tmhOAuth->request("POST", $this->tmhOAuth->url("oauth/access_token", ""), array(
137
            // pass the oauth_verifier received from Twitter
138
            'oauth_verifier'    => $_GET["oauth_verifier"]
139
        ));
140
141
        if($this->tmhOAuth->response["code"] == 200) {
142
143
            // get the access token and store it in a cookie
144
            $response = $this->tmhOAuth->extract_params($this->tmhOAuth->response["response"]);
145
            setcookie("access_token", $response["oauth_token"], time()+3600*24*30);
146
            setcookie("access_token_secret", $response["oauth_token_secret"], time()+3600*24*30);
147
148
            // state is now 2
149
            $_SESSION["authstate"] = 2;
150
151
            // redirect user to clear leftover GET variables
152
            header("Location: ' . $this->tmhOAuth->php_self());
153
            exit;
154
        }
155
        return false;
156
    }
157
158
    /**
159
     * Verify the validity of our access token
160
     *
161
     * @return bool Access token verified
162
     */
163
    private function verifyAccessToken() {
164
        $this->tmhOAuth->config["user_token"] = $_COOKIE["access_token"];
165
        $this->tmhOAuth->config["user_secret"] = $_COOKIE["access_token_secret"];
166
167
        // send verification request to test access key
168
        $this->tmhOAuth->request("GET", $this->tmhOAuth->url("1/account/verify_credentials"));
169
170
        // store the user data returned from the API
171
        $this->userdata = json_decode($this->tmhOAuth->response["response"]);
172
173
        // HTTP 200 means we were successful
174
        return ($this->tmhOAuth->response["code"] == 200);
175
    }
176
177
    /**
178
     * Check the current state of authentication
179
     *
180
     * @return bool True if state is 2 (authenticated)
181
     */
182
    public function isAuthed() {
183
        return $this->state == 2;
184
    }
185
186
    /**
187
     * Remove user's access token cookies
188
     */
189
    public function endSession() {
190
        $this->state = 0;
191
        $_SESSION["authstate"] = 0;
192
        setcookie("access_token", "", 0);
193
        setcookie("access_token_secret", "", 0);
194
    }
195
    
196
    /**
197
     * Send a tweet on the user's behalf
198
     *
199
     * @param string $text Text to tweet
200
     * @return bool Tweet successfully sent
201
     */
202
    public function sendTweet($text) {
203
204
        // limit the string to 140 characters
205
        $text = substr($text, 0, 140);
206
207
        // POST the text to the statuses/update method
208
        $this->tmhOAuth->request("POST", $this->tmhOAuth->url("1/statuses/update"), array(
209
            'status' => $text
210
        ));
211
        
212
        return ($this->tmhOAuth->response["code"] == 200);
213
    }
214
}

Our Application

Now that we have a class that handles all the OAuth tasks, we can now extend it with functionality specific to our application. This includes the ability to get, alter, and set the user's avatar.

We will be extending the TwitterApp class with a TwitterAvatars class. Start with the following code in a new file called lib/TwitterAvatars.php:

1
<?php
2
class TwitterAvatars extends TwitterApp {
3
    
4
    /**

5
     * The path to our temporary files directory

6
     *

7
     * @var string Path to store image files

8
     */
9
    public $path;
10
    
11
    /**

12
     * These are all the GD image filters available in this class

13
     *

14
     * @var array Associative array of image filters

15
     */
16
    protected $filters = array(
17
        'grayscale'     => IMG_FILTER_GRAYSCALE,
18
        'negative'      => IMG_FILTER_NEGATE,
19
        'edgedetect'    => IMG_FILTER_EDGEDETECT,
20
        'embossed'      => IMG_FILTER_EMBOSS,
21
        'blurry'        => IMG_FILTER_GAUSSIAN_BLUR,
22
        'sketchy'       => IMG_FILTER_MEAN_REMOVAL
23
    );
24
    
25
    /**

26
     * Initialize a new TwitterAvatars object

27
     *

28
     * @param tmhOAuth $tmhOAuth A tmhOAuth object with consumer key and secret

29
     * @param string $path Path to store image files (default 'tmp")

30
     */
31
    public function  __construct(tmhOAuth $tmhOAuth, $path = 'tmp") {

32
        

33
        // call the parent class' constructor
34
        parent::__construct($tmhOAuth);
35
36
        // save the path variable

37
        $this->path = $path;
38
    }
39
}

As you can see, the extended class includes a $path property to point to where temporary image files will go, a $filters property holding an array of image filters, and an extended constructor with a parameter to set the path. Since we are overriding the original constructor, we have to explicitly call the parent's constructor with parent::__construct().

Now we can start adding our methods.


Downloading

Obviously, we'll need the ability to download images in order to manipulate them. Here is a generic download() method that accepts a URL and returns the data at that location. The method makes a basic cURL request.

1
/**

2
 * Download data from specified URL

3
 *

4
 * @param string $url URL to download

5
 * @return string Downloaded data

6
 */
7
protected function download($url) {
8
    $ch = curl_init();
9
    curl_setopt($ch, CURLOPT_URL, $url);
10
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
11
12
    $ret = curl_exec($ch);
13
    curl_close($ch);
14
15
    return $ret;
16
}

Finding URLs

Now that we can download files, we need to find the location of the files we need. There are two different images we are interested in, the standard sized thumbnail and the original full-sized image. So, we'll create a method to get each URL.

To get the standard sized thumbnail, we'll call the users/profile_image/:screen_name API method which responds with a 302 redirect to the specified user's avatar image. This means the URL will be found in the Location header. Here is that method:

1
/**

2
 * Get the URL to the standard sized avatar

3
 *

4
 * @return string The URL to the image file

5
 */
6
protected function getImageURL() {
7
8
    // request user's 'bigger' profile image

9
    $this->tmhOAuth->request("GET", $this->tmhOAuth->url("1/users/profile_image/" . $this->userdata->screen_name), array(
10
        'screen_name'   => $this->userdata->screen_name,
11
        'size'          => 'bigger'
12
    ));
13
14
    if($this->tmhOAuth->response["code"] == 302) {
15
16
        // the direct URL is in the Location header

17
        return $this->tmhOAuth->response["headers"]["location"];
18
    }
19
    throw new Exception("Error locating image");
20
}

Note that we are making a GET request with tmhOAuth, passing screen_name and size parameters, then returning the contents of the Location header.

There is no API method to get the full sized image, so for our next method we'll cheat a little and edit the URL. The user data contains a profile_image_url field that points to something like avatar_normal.jpg, and the original image can be found at avatar.jpg without the suffix. So this method gets the URL, removes the size suffix and returns the modified URL:

1
/**

2
 * Get the URL to the full sized avatar

3
 *

4
 * @return string The URL to the image file

5
 */
6
protected function getOriginalImageURL() {
7
8
    // get the regular sized avatar

9
    $url = $this->userdata->profile_image_url;
10
11
    // save the extension for later

12
    $ext = strrchr($url, '.");

13


14
    // strip the "_normal' suffix and add back the extension
15
    return substr($url, 0, strrpos($url, "_")) . $ext;
16
}

Reading Images

Now that we can locate and download images, we need a way to read them. We'll be using the GD library to manipulate images, so this method will convert the raw image data into a GD image resource.

1
/**

2
 * Convert raw image data to a GD resource

3
 *

4
 * @param string $data Binary image data to parse

5
 * @return resource A GD image resource identifier

6
 */
7
protected function readImage($data) {
8
9
    // read in the original image

10
    $src = imagecreatefromstring($data);
11
12
    if(!$src) {
13
        throw new Exception("Error reading image");
14
    }
15
16
    // get the dimensions

17
    $width = imagesx($src);
18
    $height = imagesy($src);
19
20
    // create a blank true color image of the same size

21
    $img = imagecreatetruecolor($width, $height);
22
23
    // copy the original image to this new canvas

24
    imagecopy($img, $src, 0, 0, 0, 0, $width, $height);
25
26
    // discard the source image

27
    imagedestroy($src);
28
29
    return $img;
30
}

To describe what's happening above:

  1. The image data is converted to a GD resource using the imagecreatefromstring() function.
  2. The image dimensions are recorded using imagesx() and imagesy().
  3. A new blank true color image with the same dimensions is created using imagecreatetruecolor().
  4. The original image is copied into the new image using the imagecopy() function. This results in a true color version of the original image regardless of the original color mode.
  5. The original image resource is destroyed using imagedestroy() and the handle to the new image is returned.

Saving Images

Now that we can download images and create a GD resource, we need a method to save the images to the file system. Here is the method that saves the supplied image as a PNG file with the specified name using imagepng():

1
/**

2
 * Save a GD image resource to a PNG file

3
 *

4
 * @param resource $img GD image resource identifier

5
 * @param string $name Name of the image

6
 * @return string Path to the saved image

7
 */
8
protected function saveImage($img, $name) {
9
    $path = $this->path . "/' . $name . '.png';

10
    imagepng($img, $path);

11
    imagedestroy($img);

12
    return $path;

13
}

Generating Previews

Now that we have all the pieces that power our app, we can start putting them together. In our application flow, we will give the user a selection of previews to choose from. Here is the method to generate these previews:

1
/**

2
 * Generate previews for each image filter

3
 *

4
 * @return array Associative array of image previews

5
 */
6
public function generatePreviews() {
7
8
    // we need valid user info to know whose avatar to handle

9
    if(!$this->isAuthed()) {
10
        throw new Exception("Requires oauth authorization");
11
    }
12
    $username = $this->userdata->screen_name;
13
14
    // cache the raw data to use

15
    $data = $this->download($this->getImageURL());
16
17
    // copy the original image

18
    $img = $this->readImage($data);
19
    $this->saveImage($img, $username . "_orig");
20
21
    // array to hold the list of previews

22
    $images = array();
23
24
    // loop through each filter to generate previews

25
    foreach($this->filters as $filter_name => $filter) {
26
        $img = $this->readImage($data);
27
        imagefilter($img, $filter);
28
        $images[$filter_name] = $this->saveImage($img, $username . "_' . $filter_name);

29
    }

30


31
    return $images;

32
}

The first thing we do is check that the user is authenticated and then grab the user name to use later in file names.

1
// we need valid user info to know whose avatar to handle

2
if(!$this->isAuthed()) {
3
    throw new Exception("Requires oauth authorization");
4
}
5
$username = $this->userdata->screen_name;

Then we download the user's image using the getImageURL() and download() methods we have created. This data will be used repeatedly for each preview so we save it in the $data variable.

1
// cache the raw data to use

2
$data = $this->download($this->getImageURL());

Next, we save an unmodified copy with the _orig suffix. This is for visual comparison later.

1
// copy the original image

2
$img = $this->readImage($data);
3
$this->saveImage($img, $username . "_orig");

The last part of the method is where we loop through the image filters listed in the $filters property, generating an image for each filter. In each iteration, we're creating an image and applying the imagefilter() function, which accepts one of the constants we have listed in the $filters array. Then for each image we save, we add its path to an associative array (using the filter name as the key) which this method returns at the end.

1
// array to hold the list of previews

2
$images = array();
3
4
// loop through each filter to generate previews

5
foreach($this->filters as $filter_name => $filter) {
6
    $img = $this->readImage($data);
7
    imagefilter($img, $filter);
8
    $images[$filter_name] = $this->saveImage($img, $username . "_' . $filter_name);

9
}

10


11
return $images;

The next part of our application flow asks the user to confirm their choice, so we need a way to find a specific preview. Here is the simple method to get the path to an image based on the option passed as a parameter, defaulting to the original image:

1
/**

2
 * Get the path to a previously generated preview

3
 *

4
 * @param string $filter The image filter to get the preview for

5
 * @return string The path to the preview file or null if not found

6
 */
7
public function getPreview($filter = 'orig") {

8
    if(!$this->isAuthed()) {

9
        throw new Exception("Requires oauth authorization");

10
    }

11
    $path = $this->path . "/' . $this->userdata->screen_name . "_' . $filter . '.png';

12
    if(file_exists($path)) {

13
        return $path;

14
    }

15
    return null;

16
}

Changing The Avatar

The final stage of our application flow is to actually change the user's avatar. First we need a method to get the full sized image and apply a specific filter to it. Here it is:

1
/**

2
 * Process the user's full avatar using one of the filters

3
 *

4
 * @param string $filter The filter to apply to the image

5
 * @return string Path to the output file

6
 */
7
protected function processImage($filter = "grayscale") {
8
9
    // make sure the filter exists

10
    $filter = strtolower($filter);
11
    if(!array_key_exists($filter, $this->filters)) {
12
        throw new Exception("Unsupported image filter");
13
    }
14
15
    $username = $this->userdata->screen_name;
16
17
    // get the full sized avatar

18
    $data = $this->download($this->getOriginalImageURL());
19
    $img = $this->readImage($data);
20
21
    // apply the filter to the image

22
    imagefilter($img, $this->filters[$filter]);
23
24
    // save the image and return the path

25
    return $this->saveImage($img, $username . "_' . $filter . "_full");

26
}

That should be easy to follow since it is very similar to the generatePreviews() method. It accepts a parameter to specify an image filter and checks that it exists. Then it downloads the original image and applies the filter to it, passing back the path to the generated image as the return value.

Now we need the method that actually sends the generated image to Twitter, updating the user's avatar. This method calls the processImage() method to create the image and uploads it to Twitter via the 1/account/update_profile_image API method:

1
/**

2
 * Update user's avatar with a filtered version

3
 *

4
 * @param string $filter The filter to use

5
 * @return bool Operation successful

6
 */
7
public function commitAvatar($filter) {
8
    if(!$this->isAuthed()) {
9
        throw new Exception("Requires oauth authorization");
10
    }
11
12
    // generate the image and get the path

13
    $path = $this->processImage($filter);
14
    if(file_exists($path)) {
15
16
        // send a multipart POST request with the image file data

17
        $this->tmhOAuth->request("POST", $this->tmhOAuth->url("1/account/update_profile_image"), array(
18
            // format: @local/path.png;type=mime/type;filename=file_name.png

19
            'image' => '@' . $path . ';type=image/png;filename=' . basename($path)
20
        ), true, true);
21
22
        return ($this->tmhOAuth->response["code"] == 200);
23
    }
24
25
    return false;
26
}

The tricky part here is the actual tmhOAuth POST request, which is a multipart request containing the raw image data. In order to do this, we must set the last parameter of the tmhOAuth::request() method to true, and pass the image variable in a special format:

@[path_to_image];type=[mime_type];filename=[file_name]

For example, if we want to upload tmp/username_grayscale_full.png, the value would be @tmp/username_grayscale_full.png;type=image/png;filename=username_grayscale_full.png.

Here is that part of the code again:

1
// send a multipart POST request with the image file data

2
$this->tmhOAuth->request("POST", $this->tmhOAuth->url("1/account/update_profile_image"), array(
3
    // format: @local/path.png;type=mime/type;filename=file_name.png

4
    'image' => '@' . $path . ';type=image/png;filename=' . basename($path)
5
), true, true);

Cleaning Up

A side effect of all this file manipulation is a lot of temporary files being left behind. Here is a method to clean up the temporary directory:

1
/**

2
 * Delete leftover image files

3
 */
4
public function cleanupFiles() {
5
6
    // file to track when we last checked

7
    $flag = $this->path . "/.last_check';

8


9
    $time = time();

10


11
    // have we checked within the last hour?

12
    if(!file_exists($flag) || $time - filemtime($flag) > 3600) {

13


14
        // get an array of PNG files in the directory

15
        $files = glob($this->path . "/*.png");

16


17
        // loop through files, deleting old files (12+ hours)

18
        foreach($files as $file) {

19
            if($time - filemtime($file) > 60*60*12) {

20
                unlink($file);

21
            }

22
        }

23


24
        // update the timestamp of our flag file

25
        touch($flag);

26
    }

27
}

This simply loops through the PNG files, deleting those more than 12 hours old. It also checks how long it has been since we checked using the timestamp on a .last_check file, allowing us to limit the check to one per hour. This way we can call this method on every request without wasting resources.

Note: We are making use of the glob() function in PHP, which is an easy way to get an array of files matching a pattern.


The Complete TwitterAvatars Class

1
&?php
2
class TwitterAvatars extends TwitterApp {
3
    
4
    /**
5
     * The path to our temporary files directory
6
     *
7
     * @var string Path to store image files
8
     */
9
    public $path;
10
    
11
    /**
12
     * These are all the GD image filters available in this class
13
     *
14
     * @var array Associative array of image filters
15
     */
16
    protected $filters = array(
17
        'grayscale'     => IMG_FILTER_GRAYSCALE,
18
        'negative'      => IMG_FILTER_NEGATE,
19
        'edgedetect'    => IMG_FILTER_EDGEDETECT,
20
        'embossed'      => IMG_FILTER_EMBOSS,
21
        'blurry'        => IMG_FILTER_GAUSSIAN_BLUR,
22
        'sketchy'       => IMG_FILTER_MEAN_REMOVAL
23
    );
24
    
25
    /**
26
     * Initialize a new TwitterAvatars object
27
     *
28
     * @param tmhOAuth $tmhOAuth A tmhOAuth object with consumer key and secret
29
     * @param string $path Path to store image files (default 'tmp")
30
     */
31
    public function  __construct(tmhOAuth $tmhOAuth, $path = 'tmp") {
32
        
33
        // call the parent class' constructor
34
        parent::__construct($tmhOAuth);
35
36
        // save the path variable
37
        $this->path = $path;
38
    }
39
40
    /**
41
     * Download data from specified URL
42
     *
43
     * @param string $url URL to download
44
     * @return string Downloaded data
45
     */
46
    protected function download($url) {
47
        $ch = curl_init();
48
        curl_setopt($ch, CURLOPT_URL, $url);
49
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
50
51
        $ret = curl_exec($ch);
52
        curl_close($ch);
53
54
        return $ret;
55
    }
56
57
    /**
58
     * Get the URL to the standard sized avatar
59
     *
60
     * @return string The URL to the image file
61
     */
62
    protected function getImageURL() {
63
64
        // request user's 'bigger' profile image
65
        $this->tmhOAuth->request("GET", $this->tmhOAuth->url("1/users/profile_image/' . $this->userdata->screen_name), array(
66
            'screen_name'   => $this->userdata->screen_name,
67
            'size'          => 'bigger'
68
        ));
69
70
        if($this->tmhOAuth->response["code"] == 302) {
71
            
72
            // the direct URL is in the Location header
73
            return $this->tmhOAuth->response["headers"]["location"];
74
        }
75
        throw new Exception("Error locating image");
76
    }
77
78
    /**
79
     * Get the URL to the full sized avatar
80
     *
81
     * @return string The URL to the image file
82
83
     */
84
    protected function getOriginalImageURL() {
85
86
        // get the regular sized avatar
87
        $url = $this->userdata->profile_image_url;
88
89
        // save the extension for later
90
        $ext = strrchr($url, '.");
91
92
        // strip the "_normal' suffix and add back the extension
93
        return substr($url, 0, strrpos($url, "_")) . $ext;
94
    }
95
96
    /**
97
     * Convert raw image data to a GD resource
98
     *
99
     * @param string $data Binary image data to parse
100
     * @return resource A GD image resource identifier
101
     */
102
    protected function readImage($data) {
103
104
        // read in the original image
105
        $src = imagecreatefromstring($data);
106
107
        if(!$src) {
108
            throw new Exception("Error reading image");
109
        }
110
111
        // get the dimensions
112
        $width = imagesx($src);
113
        $height = imagesy($src);
114
115
        // create a blank true color image of the same size
116
        $img = imagecreatetruecolor($width, $height);
117
118
        // copy the original image to this new canvas
119
        imagecopy($img, $src, 0, 0, 0, 0, $width, $height);
120
121
        // discard the source image
122
        imagedestroy($src);
123
124
        return $img;
125
    }
126
127
    /**
128
     * Save a GD image resource to a PNG file
129
     *
130
     * @param resource $img GD image resource identifier
131
     * @param string $name Name of the image
132
     * @return string Path to the saved image
133
     */
134
    protected function saveImage($img, $name) {
135
        $path = $this->path . "/' . $name . '.png';
136
        imagepng($img, $path);
137
        imagedestroy($img);
138
        return $path;
139
    }
140
141
    /**
142
     * Generate previews for each image filter
143
     *
144
     * @return array Associative array of image previews
145
     */
146
    public function generatePreviews() {
147
        
148
        // we need valid user info to know whose avatar to handle
149
        if(!$this->isAuthed()) {
150
            throw new Exception("Requires oauth authorization");
151
        }
152
        $username = $this->userdata->screen_name;
153
154
        // cache the raw data to use
155
        $data = $this->download($this->getImageURL());
156
157
        // copy the original image
158
        $img = $this->readImage($data);
159
        $this->saveImage($img, $username . "_orig");
160
        
161
        // array to hold the list of previews
162
        $images = array();
163
164
        // loop through each filter to generate previews
165
        foreach($this->filters as $filter_name => $filter) {
166
            $img = $this->readImage($data);
167
            imagefilter($img, $filter);
168
            $images[$filter_name] = $this->saveImage($img, $username . "_' . $filter_name);
169
        }
170
171
        return $images;
172
    }
173
174
    /**
175
     * Get the path to a previously generated preview
176
     *
177
     * @param string $filter The image filter to get the preview for
178
     * @return string The path to the preview file or null if not found
179
     */
180
    public function getPreview($filter = 'orig") {
181
        if(!$this->isAuthed()) {
182
            throw new Exception("Requires oauth authorization");
183
        }
184
        $path = $this->path . "/' . $this->userdata->screen_name . "_' . $filter . '.png';
185
        if(file_exists($path)) {
186
            return $path;
187
        }
188
        return null;
189
    }
190
191
    /**
192
     * Process the user's full avatar using one of the filters
193
     *
194
     * @param string $filter The filter to apply to the image
195
     * @return string Path to the output file
196
     */
197
    protected function processImage($filter = 'grayscale") {
198
        
199
        // make sure the filter exists
200
        $filter = strtolower($filter);
201
        if(!array_key_exists($filter, $this->filters)) {
202
            throw new Exception("Unsupported image filter");
203
        }
204
205
        $username = $this->userdata->screen_name;
206
207
        // get the full sized avatar
208
        $data = $this->download($this->getOriginalImageURL());
209
        $img = $this->readImage($data);
210
211
        // apply the filter to the image
212
        imagefilter($img, $this->filters[$filter]);
213
        
214
        // save the image and return the path
215
        return $this->saveImage($img, $username . "_' . $filter . "_full");
216
    }
217
218
    /**
219
     * Update user's avatar with a filtered version
220
     *
221
     * @param string $filter The filter to use
222
     * @return bool Operation successful
223
     */
224
    public function commitAvatar($filter) {
225
        if(!$this->isAuthed()) {
226
            throw new Exception("Requires oauth authorization");
227
        }
228
229
        // generate the image and get the path
230
        $path = $this->processImage($filter);
231
        if(file_exists($path)) {
232
233
            // send a multipart POST request with the image file data
234
            $this->tmhOAuth->request("POST", $this->tmhOAuth->url("1/account/update_profile_image"), array(
235
                // format: @local/path.png;type=mime/type;filename=file_name.png
236
                'image' => '@' . $path . ';type=image/png;filename=' . basename($path)
237
            ), true, true);
238
239
            return ($this->tmhOAuth->response["code"] == 200);
240
        }
241
242
        return false;
243
    }
244
245
    /**
246
     * Delete leftover image files
247
     */
248
    public function cleanupFiles() {
249
        
250
        // file to track when we last checked
251
        $flag = $this->path . "/.last_check';
252
253
        $time = time();
254
255
        // have we checked within the last hour?
256
        if(!file_exists($flag) || $time - filemtime($flag) > 3600) {
257
            
258
            // get an array of PNG files in the directory
259
            $files = glob($this->path . "/*.png");
260
261
            // loop through files, deleting old files (12+ hours)
262
            foreach($files as $file) {
263
                if($time - filemtime($file) > 60*60*12) {
264
                    unlink($file);
265
                }
266
            }
267
268
            // update the timestamp of our flag file
269
            touch($flag);
270
        }
271
    }
272
}

The Front End

We have all the application's components together, so now all we need is the user interface. All the code here will go into the index.php file in the root directory. We'll start by including the libraries and setting the configuration:

1
<?php
2
3
// include our libraries

4
include 'lib/tmhOAuth.php';
5
include 'lib/TwitterApp.php';
6
include 'lib/TwitterAvatars.php';
7
8
// set the consumer key and secret

9
define("CONSUMER_KEY",      'qSkJum23MqlG6greF8Z76A");

10
define("CONSUMER_SECRET",   'Bs738r5UY2R7e5mwp1ilU0voe8OtXAtifgtZe9EhXw");

11
?>

Note: Be sure to replace the CONSUMER_KEY and CONSUMER_SECRET with your own.

We're going to place our code in a try-catch block so we can gracefully handle any errors, assigning their message to an $error variable.

1
try {
2
    
3
} catch(Exception $e) {
4
5
    // catch any errors that may occur

6
    $error = $e;
7
}

Within the try block we can begin writing our code, starting by initializing a TwitterAvatars object called $ta with a configured tmhOAuth object:

1
    // our tmhOAuth settings

2
    $config = array(
3
        'consumer_key'      => CONSUMER_KEY,
4
        'consumer_secret'   => CONSUMER_SECRET
5
    );
6
7
    // create a new TwitterAvatars object

8
    $ta = new TwitterAvatars(new tmhOAuth($config));

We can clear out any old temporary files at this point:

1
    // check for stale files

2
    $ta->cleanupFiles();

Next we check if the user is authenticated or, failing that, if the user has requested authentication, in which case we start the process by calling the auth() method:

1
    // check our authentication status

2
    if($ta->isAuthed()) {
3
        
4
    }
5
    // did the user request authorization?

6
    elseif(isset($_POST["auth"])) {
7
8
        // start authentication process

9
        $ta->auth();
10
    }

If the user is authenticated, we need to check if an option has been selected, otherwise we will generate previews:

1
    // check our authentication status

2
    if($ta->isAuthed()) {
3
4
        // has the user selected an option?

5
        if(isset($_POST["filter"])) {
6
            
7
        }
8
        // generate previews if the user has not chosen

9
        else {
10
11
            // $previews will be a list of images

12
            $previews = $ta->generatePreviews();
13
        }
14
    }

If an option was selected, we need to get the paths to the old and new images for display:

1
        // has the user selected an option?

2
        if(isset($_POST["filter"])) {
3
4
            // get the image paths for display

5
            $original = $ta->getPreview();
6
            $newimage = $ta->getPreview($_POST["filter"]);
7
        }

Finally, we check if the user has confirmed their choice and apply the change. We also send a tweet if requested and set a $success variable to true:

1
        // has the user selected an option?

2
        if(isset($_POST["filter"])) {
3
4
            // is the user sure?

5
            if(isset($_POST["confirm"])) {
6
7
                // change the user's avatar

8
                $ta->commitAvatar($_POST["filter"]);
9
10
                // tweet if the user chose to

11
                if(isset($_POST["tweet"])) {
12
                    $ta->sendTweet("I just updated my avatar using Avatar Effects...");
13
                }
14
15
                $success = true;
16
            }
17
18
            // get the image paths for display

19
            $original = $ta->getPreview();
20
            $newimage = $ta->getPreview($_POST["filter"]);
21
        }

Here is what we have so far:

1
<?php
2
3
// include our libraries

4
include 'lib/tmhOAuth.php';
5
include 'lib/TwitterApp.php';
6
include 'lib/TwitterAvatars.php';
7
8
// set the consumer key and secret

9
define("CONSUMER_KEY",      'qSkJum23MqlG6greF8Z76A");

10
define("CONSUMER_SECRET",   'Bs738r5UY2R7e5mwp1ilU0voe8OtXAtifgtZe9EhXw");

11


12
try {

13


14
    // our tmhOAuth settings

15
    $config = array(

16
        'consumer_key'      => CONSUMER_KEY,

17
        'consumer_secret'   => CONSUMER_SECRET

18
    );

19


20
    // create a new TwitterAvatars object

21
    $ta = new TwitterAvatars(new tmhOAuth($config));

22


23
    // check for stale files

24
    $ta->cleanupFiles();

25


26
    // check our authentication status

27
    if($ta->isAuthed()) {

28


29
        // has the user selected an option?

30
        if(isset($_POST["filter"])) {

31


32
            // is the user sure?

33
            if(isset($_POST["confirm"])) {

34


35
                // change the user's avatar

36
                $ta->commitAvatar($_POST["filter"]);

37


38
                // tweet if the user chose to

39
                if(isset($_POST["tweet"])) {

40
                    $ta->sendTweet("I just updated my avatar using Avatar Effects...");

41
                }

42


43
                $success = true;

44
            }

45


46
            // get the image paths for display

47
            $original = $ta->getPreview();

48
            $newimage = $ta->getPreview($_POST["filter"]);

49
        }

50
        // generate previews if the user has not chosen

51
        else {

52


53
            // $previews will be a list of images

54
            $previews = $ta->generatePreviews();

55
        }

56
    }

57
    // did the user request authorization?

58
    elseif(isset($_POST["auth"])) {

59


60
        // start authentication process

61
        $ta->auth();

62
    }

63
} catch(Exception $e) {

64


65
    // catch any errors that may occur

66
    $error = $e;

67
}

68
?>

The HTML

After the PHP code we will output the appropriate HTML, starting with this template, which sets the title and main heading:

1
<!DOCTYPE html>
2
<html>
3
  <head>
4
    <meta charset="UTF-8">
5
    <title>Twitter Avatar Effects</title>
6
    <link rel="stylesheet" href="css/style.css">
7
  </head>
8
  <body>
9
      <h1>Twitter Avatar Effects</h1>
10
  </body>
11
</html>

Here's where we display a form with image inputs for each preview:

1
  <?php if(isset($previews)): ?>
2
      <h2>Choose your weapon...</h2>
3
      <form action="index.php" method="post">
4
      <?php foreach($previews as $filter => $path): ?>
5
          <input type="image" src="<?php echo $path; ?>"
6
                 alt="<?php echo ucfirst($filter); ?>"
7
                  width="73" height="73"
8
                 name="filter" value="<?php echo $filter; ?>">
9
      <?php endforeach; ?>
10
      </form>
11
      <p>Select one of the images above to change your Twitter avatar.</p>

Here is the success page:

1
  <?php elseif(isset($success)): ?>
2
      <h2>Success! Your Twitter avatar is now:</h2>
3
      <img src="<?php echo $newimage; ?>" alt="Your Avatar" width="73" height="73">
4
      <p><a href="http://twitter.com/<?php echo $ta->userdata->screen_name; ?>">Go see it</a></p>

Here is the confirmation page, where we show the comparison and offer the chance to cancel:

1
  <?php elseif(isset($newimage)): ?>
2
      <h2>Are you sure?</h2>
3
      <img src="<?php echo $original; ?>" alt="Original" width="73" height="73">
4
      <span class="arrow">&rArr;</span>
5
      <img src="<?php echo $newimage; ?>" alt="<?php echo ucfirst($_POST["filter"]); ?>">
6
      <form action="index.php" method="post">
7
          <input type="hidden" name="filter" value="<?php echo $_POST["filter"]; ?>">
8
          <input type="submit" name="confirm" value="Confirm">
9
          <a href="index.php">Cancel</a>
10
          <p><label>Tweet about your new avatar?
11
                  <input type="checkbox" name="tweet" value="true"></label></p>
12
      </form>

Note that the confirmation form includes the selected filter in a hidden field.

If there is an error, we show this:

1
  <?php elseif(isset($error)): ?>
2
      <p>Error. <a href="index.php">Try again?</a></p>

The default display is the "Connect to Twitter" button as an image input (download one of the images from the bottom of this page to the img directory):

1
  <?php else: ?>
2
      <form action="index.php" method="post">
3
          <input type="image" src="img/sign-in-with-twitter-l.png"
4
                 alt="Connect to Twitter" name="auth" value="1">
5
      </form>
6
      <p>Connect to Twitter to use this app.</p>
7
  <?php endif; ?>

Here's the complete HTML section:

1
<!DOCTYPE html>
2
<html>
3
  <head>
4
    <meta charset="UTF-8">
5
    <title>Twitter Avatar Effects</title>
6
    <link rel="stylesheet" href="css/style.css">
7
  </head>
8
  <body>
9
      <h1>Twitter Avatar Effects</h1>
10
  <?php if(isset($previews)): ?>
11
      <h2>Choose your weapon...</h2>
12
      <form action="index.php" method="post">
13
      <?php foreach($previews as $filter => $path): ?>
14
          <input type="image" src="<?php echo $path; ?>"
15
                 alt="<?php echo ucfirst($filter); ?>"
16
                  width="73" height="73"
17
                 name="filter" value="<?php echo $filter; ?>">
18
      <?php endforeach; ?>
19
      </form>
20
      <p>Select one of the images above to change your Twitter avatar.</p>
21
  <?php elseif(isset($success)): ?>
22
      <h2>Success! Your Twitter avatar is now:</h2>
23
      <img src="<?php echo $newimage; ?>" alt="Your Avatar" width="73" height="73">
24
      <p><a href="http://twitter.com/<?php echo $ta->userdata->screen_name; ?>">Go see it</a></p>
25
  <?php elseif(isset($newimage)): ?>
26
      <h2>Are you sure?</h2>
27
      <img src="<?php echo $original; ?>" alt="Original" width="73" height="73">
28
      <span class="arrow">&rArr;</span>
29
      <img src="<?php echo $newimage; ?>" alt="<?php echo ucfirst($_POST["filter"]); ?>">
30
      <form action="index.php" method="post">
31
          <input type="hidden" name="filter" value="<?php echo $_POST["filter"]; ?>">
32
          <input type="submit" name="confirm" value="Confirm">
33
          <a href="index.php">Cancel</a>
34
          <p><label>Tweet about your new avatar?
35
                  <input type="checkbox" name="tweet" value="true"></label></p>
36
      </form>
37
  <?php elseif(isset($error)): ?>
38
      <p>Error. <a href="index.php">Try again?</a></p>
39
  <?php else: ?>
40
      <form action="index.php" method="post">
41
          <input type="image" src="img/sign-in-with-twitter-l.png"
42
                 alt="Connect to Twitter" name="auth" value="1">
43
      </form>
44
      <p>Connect to Twitter to use this app.</p>
45
  <?php endif; ?>
46
  </body>
47
</html>

The CSS

Here is some basic CSS to make the interface look nice, saved in css/style.css:

1
html {
2
    background-color: #eee;
3
    text-align: center;
4
    font-family: "Lucida Grande",Verdana, sans-serif;
5
    font-size: 16px;
6
    color: #224;
7
}
8
body {
9
    width: 700px;
10
    margin: 30px auto;
11
    background-color: #acf;
12
    padding: 10px;
13
    border-radius: 10px;
14
    -moz-border-radius: 10px;
15
    -webkit-border-radius: 10px;
16
}
17
p {
18
    font-size: 1em;
19
}
20
h1 {
21
    font-size: 2em;
22
}
23
h2 {
24
    font-size: 1.6em;
25
}
26
.arrow {
27
    font-size: 4em;
28
    font-weight: bold;
29
}

Results

Here is a video which details how our completed application should look:


Conclusion

If you've followed this tutorial all the way through, you should have a pretty good understanding of OAuth and what it takes to create a simple Twitter web application. Using the Twitter API is easy once you understand the basic concepts - especially if you use a library like tmhOAuth to handle the minor details.

The simple example we created in this tutorial can easily be modified or extended to do anything. So if you have a great idea for a cool new Twitter app, feel free to use this as the foundation.

Thanks for reading. If you have any questions or comments about this tutorial, please post!

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.