Build a Twitter OAuth application

王林
Release: 2023-08-27 20:34:02
Original
532 people have browsed it

OAuth can be a difficult concept to understand at first, but with the Twitter API now requiring its use, you need to understand it before creating a Twitter application. This tutorial introduces you to OAuth and walks you through the process of creating a basic application.


Introduction

In this tutorial, we will build a simple application that allows users to apply different effects to their Twitter avatar. In order to use the Twitter API, we must use OAuth to authorize our application to make requests on behalf of the user.

Our application flow will be like this:

  1. The system will ask the user to connect to Twitter.
  2. The system will display a list of preview avatars to the user for selection.
  3. After selection, the user will see a confirmation screen showing the original avatar and the new avatar for comparison. Users can also choose to send tweets.
  4. After the user confirms, the app creates the modified avatar and uploads it to Twitter, displaying a success page.

set up

First, we should set the source directory. We need a lib directory to store our PHP library (class) files, a tmp directory to save temporary files (this needs to be writable by the server), and a css Directory to hold our stylesheets, and a img directory for any images.

Your directory tree should look like this:

  • Tutorial
    • CSS
    • picture
    • lib
    • tmp(writable)

Register your application

In order to use OAuth, you need what is called a consumer key and secret to identify your application. To get this information, you must follow the steps below to register your application with Twitter.

Go to the registration page and log in if necessary. You will see a table like this:

构建 Twitter OAuth 应用程序Build a Twitter OAuth application构建 Twitter OAuth 应用程序

Fill out the form with details relevant to your app. In our case, the Application type is Browser and we need to set the default callback URL. The URL can be anything as long as it is in a valid format. We'll rewrite the callback in code so it doesn't matter if it's a real URL. For convenience, the default access type should be read and write.

After registering and accepting the terms, you will see information about your new application. The important details we need are the Consumer Key and Consumer Secret, which should look like this:

构建 Twitter OAuth 应用程序Build a Twitter OAuth application构建 Twitter OAuth 应用程序

Download tmhOAuth library

We will utilize a library to handle all the details behind OAuth requests. In this tutorial, we'll use @themattharris' tmhOAuth library, which supports file uploads.

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

Authentication

Authentication using OAuth is basically a three-step process. For a more in-depth explanation, see Twitter's page on authentication, but here's a summary:

  1. The app gets the request token: The first step is for our app to identify itself to Twitter (using its consumer key) and get the request token. We need to save this request token for later use.
  2. User authorizes application on Twitter: The user now needs to be sent to Twitter to grant our application permission to access their account. The user will then be sent back to the callback URL specified by the application.
  3. Application exchanges request token for access token: Now that our application is approved, it can exchange the request token from step 1 for an access token. After obtaining the access token, our application can freely interact with the Twitter API on behalf of the user.

So let's start writing some code. We will handle all authentication tasks in a class called TwitterApp. Start the following code in a new file named lib/TwitterApp.php:

tmhOAuth = $tmhOAuth;
    }
}
Copy after login

Here we create three properties and a simple constructor. $tmhOAuth The attribute will be a tmhOAuth object that will be used throughout the class. The $userdata property will hold an object containing user information, such as their Twitter username and status. $state Property tracks the current authentication state.

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


Step 1: Get the request token

Here's how to get the request token:

/**
 * Obtain a request token from Twitter
 *
 * @return bool False if request failed
 */
private function getRequestToken() {
    
    // send request for a request token
    $this->tmhOAuth->request("POST", $this->tmhOAuth->url("oauth/request_token", ""), array(
        // pass a variable to set the callback
        'oauth_callback'    => $this->tmhOAuth->php_self()
    ));

    if($this->tmhOAuth->response["code"] == 200) {
        
        // get and store the request token
        $response = $this->tmhOAuth->extract_params($this->tmhOAuth->response["response"]);
        $_SESSION["authtoken"] = $response["oauth_token"];
        $_SESSION["authsecret"] = $response["oauth_token_secret"];

        // state is now 1
        $_SESSION["authstate"] = 1;

        // redirect the user to Twitter to authorize
        $url = $this->tmhOAuth->url("oauth/authorize", "") . '?oauth_token=' . $response["oauth_token"];
        header("Location: ' . $url);
        exit;
    }
    return false;
}
Copy after login

To understand the first part, you need to understand the tmhOAuth::request() method. This method allows us to make an OAuth enabled HTTP request and can be used as follows:

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

  • string $method - The request method to use (GET, POST, etc.)
  • string $url - 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) - file upload set to true

For the $url parameter, we use the tmhOAuth::url() method to create the URL based on the API method we called:

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

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

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

// send request for a request token
$this->tmhOAuth->request("POST", $this->tmhOAuth->url("oauth/request_token", ""), array(
    // pass a variable to set the callback
    'oauth_callback'    => $this->tmhOAuth->php_self()
));
Copy after login

Once we make the request, the response is stored as an array in the tmhOAuth::response attribute, which contains the following key data:

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

So the next part of our code checks the response code (200 for success) and then puts the oauth_token and oauth_token_secret we received into session variables because later we They will be needed. 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 the authstate session variable to indicate that we are in the next stage of authentication. This is the code:

if($this->tmhOAuth->response["code"] == 200) {

    // get and store the request token
    $response = $this->tmhOAuth->extract_params($this->tmhOAuth->response["response"]);
    $_SESSION["authtoken"] = $response["oauth_token"];
    $_SESSION["authsecret"] = $response["oauth_token_secret"];

    // state is now 1
    $_SESSION["authstate"] = 1;
}
Copy after login

Once completed, we must now redirect the user to the oauth/authorize URL, including the oauth_token in the GET parameters. This is the code:

// redirect the user to Twitter to authorize
$url = $this->tmhOAuth->url("oauth/authorize", "") . '?oauth_token=' . $response["oauth_token"];
header("Location: ' . $url);
exit;
Copy after login

Step 2: Obtain access token

Here's how to exchange the request token for an access token:

/**
 * Obtain an access token from Twitter
 *
 * @return bool False if request failed
 */
private function getAccessToken() {

    // set the request token and secret we have stored
    $this->tmhOAuth->config["user_token"] = $_SESSION["authtoken"];
    $this->tmhOAuth->config["user_secret"] = $_SESSION["authsecret"];

    // send request for an access token
    $this->tmhOAuth->request("POST", $this->tmhOAuth->url("oauth/access_token", ""), array(
        // pass the oauth_verifier received from Twitter
        'oauth_verifier'    => $_GET["oauth_verifier"]
    ));

    if($this->tmhOAuth->response["code"] == 200) {

        // get the access token and store it in a cookie
        $response = $this->tmhOAuth->extract_params($this->tmhOAuth->response["response"]);
        setcookie("access_token", $response["oauth_token"], time()+3600*24*30);
        setcookie("access_token_secret", $response["oauth_token_secret"], time()+3600*24*30);

        // state is now 2
        $_SESSION["authstate"] = 2;

        // redirect user to clear leftover GET variables
        header("Location: ' . $this->tmhOAuth->php_self());
        exit;
    }
    return false;
}
Copy after login

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

// set the request token and secret we have stored
$this->tmhOAuth->config["user_token"] = $_SESSION["authtoken"];
$this->tmhOAuth->config["user_secret"] = $_SESSION["authsecret"];
Copy after login

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

// send request for an access token
$this->tmhOAuth->request("POST", $this->tmhOAuth->url("oauth/access_token", ""), array(
    // pass the oauth_verifier received from Twitter
    'oauth_verifier'    => $_GET["oauth_verifier"]
));
Copy after login

Twitter will respond with the access token and secret, we need to save these tokens for future requests. So the next piece of code takes these and saves each content in a cookie and then sets the state to 2.

if($this->tmhOAuth->response["code"] == 200) {

    // get the access token and store it in a cookie
    $response = $this->tmhOAuth->extract_params($this->tmhOAuth->response["response"]);
    setcookie("access_token", $response["oauth_token"], time()+3600*24*30);
    setcookie("access_token_secret", $response["oauth_token_secret"], time()+3600*24*30);

    // state is now 2
    $_SESSION["authstate"] = 2;

    // redirect user to clear leftover GET variables
    header("Location: ' . $this->tmhOAuth->php_self());
    exit;
}
Copy after login

The final redirection is to clear the URL parameters left by Twitter and allow the cookie to take effect.


Step 3: Verify Access Token

After obtaining the access token, we should check to make sure it is valid. Here's how to do it:

/**
 * Verify the validity of our access token
 *
 * @return bool Access token verified
 */
private function verifyAccessToken() {
    $this->tmhOAuth->config["user_token"] = $_COOKIE["access_token"];
    $this->tmhOAuth->config["user_secret"] = $_COOKIE["access_token_secret"];

    // send verification request to test access key
    $this->tmhOAuth->request("GET", $this->tmhOAuth->url("1/account/verify_credentials"));

    // store the user data returned from the API
    $this->userdata = json_decode($this->tmhOAuth->response["response"]);

    // HTTP 200 means we were successful
    return ($this->tmhOAuth->response["code"] == 200);
}
Copy after login

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

Another detail to note is that we are populating the $userdata property here with the data returned from this Twitter request. The data is in JSON format, so we use json_decode() to convert it to a PHP object. This is that line again:

// store the user data returned from the API
$this->userdata = json_decode($this->tmhOAuth->response["response"]);
Copy after login

Step 4: Tying everything together

With our OAuth components in place, it's time to bring everything together. We need a public-facing method to allow our client code to initiate the authentication process, like this:

/**
 * Authenticate user with Twitter
 *
 * @return bool Authentication successful
 */
public function auth() {

    // state 1 requires a GET variable to exist
    if($this->state == 1 && !isset($_GET["oauth_verifier"])) {
        $this->state = 0;
    }

    // Step 1: Get a request token
    if($this->state == 0) {
        return $this->getRequestToken();
    }
    // Step 2: Get an access token
    elseif($this->state == 1) {
        return $this->getAccessToken();
    }

    // Step 3: Verify the access token
    return $this->verifyAccessToken();
}
Copy after login

大多数 auth() 方法应该是不言自明的。根据状态,它执行该阶段的身份验证的适当方法。如果状态为 1,则 oauth_verifier GET 变量应该存在,因此该方法也会检查该变量。

我们现在应该创建一个公共方法来确定我们是否通过了身份验证。如果状态为 2,则 isAuthed() 方法返回 true:

/**
 * Check the current state of authentication
 *
 * @return bool True if state is 2 (authenticated)
 */
public function isAuthed() {
    return $this->state == 2;
}
Copy after login

我们还可以使用一种方法来删除用户的身份验证。此 endSession() 方法将状态设置为 0 并删除包含访问令牌的 cookie:

/**
 * Remove user's access token cookies
 */
public function endSession() {
    $this->state = 0;
    $_SESSION["authstate"] = 0;
    setcookie("access_token", "", 0);
    setcookie("access_token_secret", "", 0);
}
Copy after login

初始化

现在我们需要向 __construct() 方法添加一些内容,以确定应用程序在初始化时处于哪种身份验证状态。另外,由于我们的代码使用会话变量,因此我们应该确保会话是使用以下代码启动的:

// start a session if one does not exist
if(!session_id()) {
    session_start();
}
Copy after login

下一部分是我们确定状态的地方。状态从0开始;如果设置了包含访问令牌的 cookie,则状态假定为 2;如果失败,状态将设置为 authstate 会话变量(如果存在)。这是代码:

// determine the authentication status
// default to 0
$this->state = 0;
// 2 (authenticated) if the cookies are set
if(isset($_COOKIE["access_token"], $_COOKIE["access_token_secret"])) {
    $this->state = 2;
}
// otherwise use value stored in session
elseif(isset($_SESSION["authstate"])) {
    $this->state = (int)$_SESSION["authstate"];
}
Copy after login

如果状态为 1,则表示我们正在进行身份验证。所以我们现在可以继续这个过程:

// if we are in the process of authentication we continue
if($this->state == 1) {
    $this->auth();
}
Copy after login

如果状态为2,我们应该验证访问令牌。如果身份验证失败,此代码将清除 cookie 并重置状态:

// verify authentication, clearing cookies if it fails
elseif($this->state == 2 && !$this->auth()) {
    $this->endSession();
}
Copy after login

这是进行了这些更改的新构造函数:

/**
 * Initialize a new TwitterApp object
 *
 * @param tmhOAuth $tmhOAuth A tmhOAuth object with consumer key and secret
 */
public function  __construct(tmhOAuth $tmhOAuth) {

    // save the tmhOAuth object
    $this->tmhOAuth = $tmhOAuth;

    // start a session if one does not exist
    if(!session_id()) {
        session_start();
    }

    // determine the authentication status
    // default to 0
    $this->state = 0;
    // 2 (authenticated) if the cookies are set
    if(isset($_COOKIE["access_token"], $_COOKIE["access_token_secret"])) {
        $this->state = 2;
    }
    // otherwise use value stored in session
    elseif(isset($_SESSION["authstate"])) {
    $this->state = (int)$_SESSION["authstate"];
    }

    // if we are in the process of authentication we continue
    if($this->state == 1) {
        $this->auth();
    }
    // verify authentication, clearing cookies if it fails
    elseif($this->state == 2 && !$this->auth()) {
        $this->endSession();
    }
}
Copy after login

发送推文

现在所有授权代码都已完成,我们可以向我们的类添加一些常用功能。以下是通过 Twitter API 发送推文的方法:

/**
 * Send a tweet on the user's behalf
 *
 * @param string $text Text to tweet
 * @return bool Tweet successfully sent
 */
public function sendTweet($text) {

    // limit the string to 140 characters
    $text = substr($text, 0, 140);

    // POST the text to the statuses/update method
    $this->tmhOAuth->request("POST", $this->tmhOAuth->url("1/statuses/update"), array(
        'status' => $text
    ));

    return ($this->tmhOAuth->response["code"] == 200);
}
Copy after login

sendTweet() 方法接受一个字符串,将其限制为 140 个字符,然后在 POST 请求中将其发送到 1/statuses/update。这种模式现在应该非常熟悉了。


完整的 TwitterApp 类

tmhOAuth = $tmhOAuth;

        // start a session if one does not exist
        if(!session_id()) {
            session_start();
        }
        
        // determine the authentication status
        // default to 0
        $this->state = 0;
        // 2 (authenticated) if the cookies are set
        if(isset($_COOKIE["access_token"], $_COOKIE["access_token_secret"])) {
            $this->state = 2;
        }
        // otherwise use value stored in session
        elseif(isset($_SESSION["authstate"])) {
            $this->state = (int)$_SESSION["authstate"];
        }
        
        // if we are in the process of authentication we continue
        if($this->state == 1) {
            $this->auth();
        }
        // verify authentication, clearing cookies if it fails
        elseif($this->state == 2 && !$this->auth()) {
            $this->endSession();
        }
    }

    /**
     * Authenticate user with Twitter
     *
     * @return bool Authentication successful
     */
    public function auth() {
        
        // state 1 requires a GET variable to exist
        if($this->state == 1 && !isset($_GET["oauth_verifier"])) {
            $this->state = 0;
        }

        // Step 1: Get a request token
        if($this->state == 0) {
            return $this->getRequestToken();
        }
        // Step 2: Get an access token

        elseif($this->state == 1) {
            return $this->getAccessToken();
        }

        // Step 3: Verify the access token
        return $this->verifyAccessToken();
    }

    /**
     * Obtain a request token from Twitter
     *
     * @return bool False if request failed
     */
    private function getRequestToken() {
        
        // send request for a request token
        $this->tmhOAuth->request("POST", $this->tmhOAuth->url("oauth/request_token", ""), array(
            // pass a variable to set the callback
            'oauth_callback'    => $this->tmhOAuth->php_self()
        ));

        if($this->tmhOAuth->response["code"] == 200) {
            
            // get and store the request token
            $response = $this->tmhOAuth->extract_params($this->tmhOAuth->response["response"]);
            $_SESSION["authtoken"] = $response["oauth_token"];
            $_SESSION["authsecret"] = $response["oauth_token_secret"];

            // state is now 1
            $_SESSION["authstate"] = 1;

            // redirect the user to Twitter to authorize
            $url = $this->tmhOAuth->url("oauth/authorize", "") . '?oauth_token=' . $response["oauth_token"];
            header("Location: ' . $url);
            exit;
        }
        return false;
    }

    /**
     * Obtain an access token from Twitter
     *
     * @return bool False if request failed
     */
    private function getAccessToken() {
        
        // set the request token and secret we have stored
        $this->tmhOAuth->config["user_token"] = $_SESSION["authtoken"];
        $this->tmhOAuth->config["user_secret"] = $_SESSION["authsecret"];

        // send request for an access token
        $this->tmhOAuth->request("POST", $this->tmhOAuth->url("oauth/access_token", ""), array(
            // pass the oauth_verifier received from Twitter
            'oauth_verifier'    => $_GET["oauth_verifier"]
        ));

        if($this->tmhOAuth->response["code"] == 200) {

            // get the access token and store it in a cookie
            $response = $this->tmhOAuth->extract_params($this->tmhOAuth->response["response"]);
            setcookie("access_token", $response["oauth_token"], time()+3600*24*30);
            setcookie("access_token_secret", $response["oauth_token_secret"], time()+3600*24*30);

            // state is now 2
            $_SESSION["authstate"] = 2;

            // redirect user to clear leftover GET variables
            header("Location: ' . $this->tmhOAuth->php_self());
            exit;
        }
        return false;
    }

    /**
     * Verify the validity of our access token
     *
     * @return bool Access token verified
     */
    private function verifyAccessToken() {
        $this->tmhOAuth->config["user_token"] = $_COOKIE["access_token"];
        $this->tmhOAuth->config["user_secret"] = $_COOKIE["access_token_secret"];

        // send verification request to test access key
        $this->tmhOAuth->request("GET", $this->tmhOAuth->url("1/account/verify_credentials"));

        // store the user data returned from the API
        $this->userdata = json_decode($this->tmhOAuth->response["response"]);

        // HTTP 200 means we were successful
        return ($this->tmhOAuth->response["code"] == 200);
    }

    /**
     * Check the current state of authentication
     *
     * @return bool True if state is 2 (authenticated)
     */
    public function isAuthed() {
        return $this->state == 2;
    }

    /**
     * Remove user's access token cookies
     */
    public function endSession() {
        $this->state = 0;
        $_SESSION["authstate"] = 0;
        setcookie("access_token", "", 0);
        setcookie("access_token_secret", "", 0);
    }
    
    /**
     * Send a tweet on the user's behalf
     *
     * @param string $text Text to tweet
     * @return bool Tweet successfully sent
     */
    public function sendTweet($text) {

        // limit the string to 140 characters
        $text = substr($text, 0, 140);

        // POST the text to the statuses/update method
        $this->tmhOAuth->request("POST", $this->tmhOAuth->url("1/statuses/update"), array(
            'status' => $text
        ));
        
        return ($this->tmhOAuth->response["code"] == 200);
    }
}
Copy after login

我们的应用

现在我们有了一个处理所有 OAuth 任务的类,我们现在可以使用特定于我们的应用程序的功能来扩展它。这包括获取、更改和设置用户头像的能力。

我们将使用 TwitterAvatars 类扩展 TwitterApp 类。在名为 lib/TwitterAvatars.php 的新文件中开始以下代码:

 IMG_FILTER_GRAYSCALE,
        'negative'      => IMG_FILTER_NEGATE,
        'edgedetect'    => IMG_FILTER_EDGEDETECT,
        'embossed'      => IMG_FILTER_EMBOSS,
        'blurry'        => IMG_FILTER_GAUSSIAN_BLUR,
        'sketchy'       => IMG_FILTER_MEAN_REMOVAL
    );
    
    /**
     * Initialize a new TwitterAvatars object
     *
     * @param tmhOAuth $tmhOAuth A tmhOAuth object with consumer key and secret
     * @param string $path Path to store image files (default 'tmp")
     */
    public function  __construct(tmhOAuth $tmhOAuth, $path = 'tmp") {
        
        // call the parent class' constructor
        parent::__construct($tmhOAuth);

        // save the path variable
        $this->path = $path;
    }
}
Copy after login

正如你所看到的,扩展类包括一个 $path 属性,用于指向临时图像文件的位置,一个 $filters 属性,保存图像过滤器数组,以及一个带有要设置的参数的扩展构造函数路径。由于我们要重写原始构造函数,因此必须使用 parent::__construct() 显式调用父级构造函数。

现在我们可以开始添加我们的方法了。


下载

显然,我们需要能够下载图像才能操作它们。这是一个通用的 download() 方法,它接受 URL 并返回该位置的数据。该方法发出基本的 cURL 请求。

/**
 * Download data from specified URL
 *
 * @param string $url URL to download
 * @return string Downloaded data
 */
protected function download($url) {
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

    $ret = curl_exec($ch);
    curl_close($ch);

    return $ret;
}
Copy after login

查找 URL

现在我们可以下载文件了,我们需要找到所需文件的位置。我们感兴趣的是两种不同的图像,标准尺寸的缩略图和原始的全尺寸图像。因此,我们将创建一个方法来获取每个 URL。

为了获取标准尺寸的缩略图,我们将调用 users/profile_image/:screen_name API 方法,该方法通过 302 重定向响应指定用户的头像图像。这意味着 URL 将在 Location 标头中找到。这是该方法:

/**
 * Get the URL to the standard sized avatar
 *
 * @return string The URL to the image file
 */
protected function getImageURL() {

    // request user's 'bigger' profile image
    $this->tmhOAuth->request("GET", $this->tmhOAuth->url("1/users/profile_image/" . $this->userdata->screen_name), array(
        'screen_name'   => $this->userdata->screen_name,
        'size'          => 'bigger'
    ));

    if($this->tmhOAuth->response["code"] == 302) {

        // the direct URL is in the Location header
        return $this->tmhOAuth->response["headers"]["location"];
    }
    throw new Exception("Error locating image");
}
Copy after login

请注意,我们正在使用 tmhOAuth 发出 GET 请求,传递 screen_namesize 参数,然后返回 Location 标头的内容。

没有 API 方法可以获取完整尺寸的图像,因此对于下一个方法,我们将稍微作弊并编辑 URL。用户数据包含一个 profile_image_url 字段,该字段指向 avatar_normal.jpg 之类的内容,并且可以在 avatar.jpg 中找到不带后缀的原始图像。所以这个方法获取URL,去掉尺寸后缀并返回修改后的URL:

/**
 * Get the URL to the full sized avatar
 *
 * @return string The URL to the image file
 */
protected function getBuild a Twitter OAuth applicationImageURL() {

    // get the regular sized avatar
    $url = $this->userdata->profile_image_url;

    // save the extension for later
    $ext = strrchr($url, '.");

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

读取图像

现在我们可以找到并下载图像,我们需要一种方法来读取它们。我们将使用 GD 库来操作图像,因此此方法会将原始图像数据转换为 GD 图像资源。

/**
 * Convert raw image data to a GD resource
 *
 * @param string $data Binary image data to parse
 * @return resource A GD image resource identifier
 */
protected function readImage($data) {

    // read in the original image
    $src = imagecreatefromstring($data);

    if(!$src) {
        throw new Exception("Error reading image");
    }

    // get the dimensions
    $width = imagesx($src);
    $height = imagesy($src);

    // create a blank true color image of the same size
    $img = imagecreatetruecolor($width, $height);

    // copy the original image to this new canvas
    imagecopy($img, $src, 0, 0, 0, 0, $width, $height);

    // discard the source image
    imagedestroy($src);

    return $img;
}
Copy after login

描述上面发生的事情:

  1. 使用 imagecreatefromstring() 函数将图像数据转换为 GD 资源。
  2. 使用 imagesx()imagesy() 记录图像尺寸。
  3. 使用 imagecreatetruecolor() 创建具有相同尺寸的新空白真彩色图像。
  4. 使用 imagecopy() 函数将原始图像复制到新图像中。无论原始颜色模式如何,这都会产生原始图像的真彩色版本。
  5. 使用 imagedestroy() 销毁原始图像资源,并返回新图像的句柄。

保存图像

现在我们可以下载图像并创建 GD 资源,我们需要一种将图像保存到文件系统的方法。以下是使用 imagepng() 将提供的图像保存为具有指定名称的 PNG 文件的方法:

/**
 * Save a GD image resource to a PNG file
 *
 * @param resource $img GD image resource identifier
 * @param string $name Name of the image
 * @return string Path to the saved image
 */
protected function saveImage($img, $name) {
    $path = $this->path . "/' . $name . '.png';
    imagepng($img, $path);
    imagedestroy($img);
    return $path;
}
Copy after login

生成预览

现在我们已经拥有了为我们的应用程序提供支持的所有部分,我们可以开始将它们组合在一起。在我们的应用程序流程中,我们将为用户提供可供选择的预览选项。以下是生成这些预览的方法:

/**
 * Generate previews for each image filter
 *
 * @return array Associative array of image previews
 */
public function generatePreviews() {

    // we need valid user info to know whose avatar to handle
    if(!$this->isAuthed()) {
        throw new Exception("Requires oauth authorization");
    }
    $username = $this->userdata->screen_name;

    // cache the raw data to use
    $data = $this->download($this->getImageURL());

    // copy the original image
    $img = $this->readImage($data);
    $this->saveImage($img, $username . "_orig");

    // array to hold the list of previews
    $images = array();

    // loop through each filter to generate previews
    foreach($this->filters as $filter_name => $filter) {
        $img = $this->readImage($data);
        imagefilter($img, $filter);
        $images[$filter_name] = $this->saveImage($img, $username . "_' . $filter_name);
    }

    return $images;
}
Copy after login

我们要做的第一件事是检查用户是否已通过身份验证,然后获取用户名以便稍后在文件名中使用。

// we need valid user info to know whose avatar to handle
if(!$this->isAuthed()) {
    throw new Exception("Requires oauth authorization");
}
$username = $this->userdata->screen_name;
Copy after login

然后我们使用我们创建的 getImageURL()download() 方法下载用户的图像。该数据将在每次预览中重复使用,因此我们将其保存在 $data 变量中。

// cache the raw data to use
$data = $this->download($this->getImageURL());
Copy after login

接下来,我们使用 _orig 后缀保存未修改的副本。这是为了稍后进行视觉比较。

// copy the original image
$img = $this->readImage($data);
$this->saveImage($img, $username . "_orig");
Copy after login

该方法的最后一部分是我们循环遍历 $filters 属性中列出的图像过滤器,为每个过滤器生成一个图像。在每次迭代中,我们创建一个图像并应用 imagefilter() 函数,该函数接受我们在 $filters 数组中列出的常量之一。然后,对于我们保存的每个图像,我们将其路径添加到该方法最后返回的关联数组(使用过滤器名称作为键)。

// array to hold the list of previews
$images = array();

// loop through each filter to generate previews
foreach($this->filters as $filter_name => $filter) {
    $img = $this->readImage($data);
    imagefilter($img, $filter);
    $images[$filter_name] = $this->saveImage($img, $username . "_' . $filter_name);
}

return $images;
Copy after login

我们的应用程序流程的下一部分要求用户确认他们的选择,因此我们需要一种方法来查找特定的预览。下面是根据作为参数传递的选项获取图像路径的简单方法,默认为原始图像:

/**
 * Get the path to a previously generated preview
 *
 * @param string $filter The image filter to get the preview for
 * @return string The path to the preview file or null if not found
 */
public function getPreview($filter = 'orig") {
    if(!$this->isAuthed()) {
        throw new Exception("Requires oauth authorization");
    }
    $path = $this->path . "/' . $this->userdata->screen_name . "_' . $filter . '.png';
    if(file_exists($path)) {
        return $path;
    }
    return null;
}
Copy after login

更改头像

我们的应用程序流程的最后阶段是实际更改用户的头像。首先,我们需要一种方法来获取全尺寸图像并对其应用特定的滤镜。这是:

/**
 * Process the user's full avatar using one of the filters
 *
 * @param string $filter The filter to apply to the image
 * @return string Path to the output file
 */
protected function processImage($filter = "grayscale") {

    // make sure the filter exists
    $filter = strtolower($filter);
    if(!array_key_exists($filter, $this->filters)) {
        throw new Exception("Unsupported image filter");
    }

    $username = $this->userdata->screen_name;

    // get the full sized avatar
    $data = $this->download($this->getBuild a Twitter OAuth applicationImageURL());
    $img = $this->readImage($data);

    // apply the filter to the image
    imagefilter($img, $this->filters[$filter]);

    // save the image and return the path
    return $this->saveImage($img, $username . "_' . $filter . "_full");
}
Copy after login

这应该很容易理解,因为它与 generatePreviews() 方法非常相似。它接受一个参数来指定图像过滤器并检查它是否存在。然后它下载原始图像并对其应用过滤器,将生成图像的路径作为返回值传回。

现在我们需要将生成的图像实际发送到 Twitter 的方法,以更新用户的头像。该方法调用 processImage() 方法创建图像并通过 1/account/update_profile_image API 方法上传到 Twitter:

/**
 * Update user's avatar with a filtered version
 *
 * @param string $filter The filter to use
 * @return bool Operation successful
 */
public function commitAvatar($filter) {
    if(!$this->isAuthed()) {
        throw new Exception("Requires oauth authorization");
    }

    // generate the image and get the path
    $path = $this->processImage($filter);
    if(file_exists($path)) {

        // send a multipart POST request with the image file data
        $this->tmhOAuth->request("POST", $this->tmhOAuth->url("1/account/update_profile_image"), array(
            // format: @local/path.png;type=mime/type;filename=file_name.png
            'image' => '@' . $path . ';type=image/png;filename=' . basename($path)
        ), true, true);

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

    return false;
}
Copy after login

这里棘手的部分是实际的 tmhOAuth POST 请求,它是一个包含原始图像数据的多部分请求。为此,我们必须将 tmhOAuth::request() 方法的最后一个参数设置为 true,并以特殊格式传递 image 变量:

@[图像路径];type=[mime_type];filename=[file_name]

例如,如果我们要上传 tmp/username_grayscale_full.png,则值为 @tmp/username_grayscale_full.png;type=image/png;filename=username_grayscale_full.png

这又是那部分代码:

// send a multipart POST request with the image file data
$this->tmhOAuth->request("POST", $this->tmhOAuth->url("1/account/update_profile_image"), array(
    // format: @local/path.png;type=mime/type;filename=file_name.png
    'image' => '@' . $path . ';type=image/png;filename=' . basename($path)
), true, true);
Copy after login

清理

所有这些文件操作的副作用是留下大量临时文件。下面是清理临时目录的方法:

/**
 * Delete leftover image files
 */
public function cleanupFiles() {

    // file to track when we last checked
    $flag = $this->path . "/.last_check';

    $time = time();

    // have we checked within the last hour?
    if(!file_exists($flag) || $time - filemtime($flag) > 3600) {

        // get an array of PNG files in the directory
        $files = glob($this->path . "/*.png");

        // loop through files, deleting old files (12+ hours)
        foreach($files as $file) {
            if($time - filemtime($file) > 60*60*12) {
                unlink($file);
            }
        }

        // update the timestamp of our flag file
        touch($flag);
    }
}
Copy after login

这只是循环遍历 PNG 文件,删除那些超过 12 小时的文件。它还检查自我们使用 .last_check 文件上的时间戳进行检查以来已经过去了多长时间,从而允许我们将检查限制为每小时一次。这样我们就可以在每个请求上调用这个方法,而不会浪费资源。

注意:我们在 PHP 中使用 glob() 函数,这是获取与模式匹配的文件数组的简单方法。


完整的 TwitterAvatars 类

&?php
class TwitterAvatars extends TwitterApp {
    
    /**
     * The path to our temporary files directory
     *
     * @var string Path to store image files
     */
    public $path;
    
    /**
     * These are all the GD image filters available in this class
     *
     * @var array Associative array of image filters
     */
    protected $filters = array(
        'grayscale'     => IMG_FILTER_GRAYSCALE,
        'negative'      => IMG_FILTER_NEGATE,
        'edgedetect'    => IMG_FILTER_EDGEDETECT,
        'embossed'      => IMG_FILTER_EMBOSS,
        'blurry'        => IMG_FILTER_GAUSSIAN_BLUR,
        'sketchy'       => IMG_FILTER_MEAN_REMOVAL
    );
    
    /**
     * Initialize a new TwitterAvatars object
     *
     * @param tmhOAuth $tmhOAuth A tmhOAuth object with consumer key and secret
     * @param string $path Path to store image files (default 'tmp")
     */
    public function  __construct(tmhOAuth $tmhOAuth, $path = 'tmp") {
        
        // call the parent class' constructor
        parent::__construct($tmhOAuth);

        // save the path variable
        $this->path = $path;
    }

    /**
     * Download data from specified URL
     *
     * @param string $url URL to download
     * @return string Downloaded data
     */
    protected function download($url) {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

        $ret = curl_exec($ch);
        curl_close($ch);

        return $ret;
    }

    /**
     * Get the URL to the standard sized avatar
     *
     * @return string The URL to the image file
     */
    protected function getImageURL() {

        // request user's 'bigger' profile image
        $this->tmhOAuth->request("GET", $this->tmhOAuth->url("1/users/profile_image/' . $this->userdata->screen_name), array(
            'screen_name'   => $this->userdata->screen_name,
            'size'          => 'bigger'
        ));

        if($this->tmhOAuth->response["code"] == 302) {
            
            // the direct URL is in the Location header
            return $this->tmhOAuth->response["headers"]["location"];
        }
        throw new Exception("Error locating image");
    }

    /**
     * Get the URL to the full sized avatar
     *
     * @return string The URL to the image file

     */
    protected function getBuild a Twitter OAuth applicationImageURL() {

        // get the regular sized avatar
        $url = $this->userdata->profile_image_url;

        // save the extension for later
        $ext = strrchr($url, '.");

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

    /**
     * Convert raw image data to a GD resource
     *
     * @param string $data Binary image data to parse
     * @return resource A GD image resource identifier
     */
    protected function readImage($data) {

        // read in the original image
        $src = imagecreatefromstring($data);

        if(!$src) {
            throw new Exception("Error reading image");
        }

        // get the dimensions
        $width = imagesx($src);
        $height = imagesy($src);

        // create a blank true color image of the same size
        $img = imagecreatetruecolor($width, $height);

        // copy the original image to this new canvas
        imagecopy($img, $src, 0, 0, 0, 0, $width, $height);

        // discard the source image
        imagedestroy($src);

        return $img;
    }

    /**
     * Save a GD image resource to a PNG file
     *
     * @param resource $img GD image resource identifier
     * @param string $name Name of the image
     * @return string Path to the saved image
     */
    protected function saveImage($img, $name) {
        $path = $this->path . "/' . $name . '.png';
        imagepng($img, $path);
        imagedestroy($img);
        return $path;
    }

    /**
     * Generate previews for each image filter
     *
     * @return array Associative array of image previews
     */
    public function generatePreviews() {
        
        // we need valid user info to know whose avatar to handle
        if(!$this->isAuthed()) {
            throw new Exception("Requires oauth authorization");
        }
        $username = $this->userdata->screen_name;

        // cache the raw data to use
        $data = $this->download($this->getImageURL());

        // copy the original image
        $img = $this->readImage($data);
        $this->saveImage($img, $username . "_orig");
        
        // array to hold the list of previews
        $images = array();

        // loop through each filter to generate previews
        foreach($this->filters as $filter_name => $filter) {
            $img = $this->readImage($data);
            imagefilter($img, $filter);
            $images[$filter_name] = $this->saveImage($img, $username . "_' . $filter_name);
        }

        return $images;
    }

    /**
     * Get the path to a previously generated preview
     *
     * @param string $filter The image filter to get the preview for
     * @return string The path to the preview file or null if not found
     */
    public function getPreview($filter = 'orig") {
        if(!$this->isAuthed()) {
            throw new Exception("Requires oauth authorization");
        }
        $path = $this->path . "/' . $this->userdata->screen_name . "_' . $filter . '.png';
        if(file_exists($path)) {
            return $path;
        }
        return null;
    }

    /**
     * Process the user's full avatar using one of the filters
     *
     * @param string $filter The filter to apply to the image
     * @return string Path to the output file
     */
    protected function processImage($filter = 'grayscale") {
        
        // make sure the filter exists
        $filter = strtolower($filter);
        if(!array_key_exists($filter, $this->filters)) {
            throw new Exception("Unsupported image filter");
        }

        $username = $this->userdata->screen_name;

        // get the full sized avatar
        $data = $this->download($this->getBuild a Twitter OAuth applicationImageURL());
        $img = $this->readImage($data);

        // apply the filter to the image
        imagefilter($img, $this->filters[$filter]);
        
        // save the image and return the path
        return $this->saveImage($img, $username . "_' . $filter . "_full");
    }

    /**
     * Update user's avatar with a filtered version
     *
     * @param string $filter The filter to use
     * @return bool Operation successful
     */
    public function commitAvatar($filter) {
        if(!$this->isAuthed()) {
            throw new Exception("Requires oauth authorization");
        }

        // generate the image and get the path
        $path = $this->processImage($filter);
        if(file_exists($path)) {

            // send a multipart POST request with the image file data
            $this->tmhOAuth->request("POST", $this->tmhOAuth->url("1/account/update_profile_image"), array(
                // format: @local/path.png;type=mime/type;filename=file_name.png
                'image' => '@' . $path . ';type=image/png;filename=' . basename($path)
            ), true, true);

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

        return false;
    }

    /**
     * Delete leftover image files
     */
    public function cleanupFiles() {
        
        // file to track when we last checked
        $flag = $this->path . "/.last_check';

        $time = time();

        // have we checked within the last hour?
        if(!file_exists($flag) || $time - filemtime($flag) > 3600) {
            
            // get an array of PNG files in the directory
            $files = glob($this->path . "/*.png");

            // loop through files, deleting old files (12+ hours)
            foreach($files as $file) {
                if($time - filemtime($file) > 60*60*12) {
                    unlink($file);
                }
            }

            // update the timestamp of our flag file
            touch($flag);
        }
    }
}
Copy after login

前端

我们将应用程序的所有组件放在一起,所以现在我们需要的只是用户界面。这里的所有代码都将进入根目录中的index.php文件。我们将首先包含库并设置配置:


Copy after login

注意:请务必将 CONSUMER_KEYCONSUMER_SECRET 替换为您自己的。

我们将把代码放在 try-catch 块中,这样我们就可以优雅地处理任何错误,将它们的消息分配给 $error 变量。

try {
    
} catch(Exception $e) {

    // catch any errors that may occur
    $error = $e;
}
Copy after login

在 try 块中,我们可以开始编写代码,首先使用配置的 tmhOAuth 对象初始化名为 $ta 的 TwitterAvatars 对象:

    // our tmhOAuth settings
    $config = array(
        'consumer_key'      => CONSUMER_KEY,
        'consumer_secret'   => CONSUMER_SECRET
    );

    // create a new TwitterAvatars object
    $ta = new TwitterAvatars(new tmhOAuth($config));
Copy after login

此时我们可以清除所有旧的临时文件:

    // check for stale files
    $ta->cleanupFiles();
Copy after login

接下来,我们检查用户是否已通过身份验证,或者如果用户未通过身份验证,则检查用户是否已请求身份验证,在这种情况下,我们通过调用 auth() 方法来启动该过程:

    // check our authentication status
    if($ta->isAuthed()) {
        
    }
    // did the user request authorization?
    elseif(isset($_POST["auth"])) {

        // start authentication process
        $ta->auth();
    }
Copy after login

如果用户已通过身份验证,我们需要检查是否已选择某个选项,否则我们将生成预览:

    // check our authentication status
    if($ta->isAuthed()) {

        // has the user selected an option?
        if(isset($_POST["filter"])) {
            
        }
        // generate previews if the user has not chosen
        else {

            // $previews will be a list of images
            $previews = $ta->generatePreviews();
        }
    }
Copy after login

如果选择了某个选项,我们需要获取要显示的旧图像和新图像的路径:

        // has the user selected an option?
        if(isset($_POST["filter"])) {

            // get the image paths for display
            $original = $ta->getPreview();
            $newimage = $ta->getPreview($_POST["filter"]);
        }
Copy after login

最后,我们检查用户是否确认了他们的选择并应用更改。如果需要,我们还会发送一条推文,并将 $success 变量设置为 true:

        // has the user selected an option?
        if(isset($_POST["filter"])) {

            // is the user sure?
            if(isset($_POST["confirm"])) {

                // change the user's avatar
                $ta->commitAvatar($_POST["filter"]);

                // tweet if the user chose to
                if(isset($_POST["tweet"])) {
                    $ta->sendTweet("I just updated my avatar using Avatar Effects...");
                }

                $success = true;
            }

            // get the image paths for display
            $original = $ta->getPreview();
            $newimage = $ta->getPreview($_POST["filter"]);
        }
Copy after login

这是我们迄今为止所拥有的:

 CONSUMER_KEY,
        'consumer_secret'   => CONSUMER_SECRET
    );

    // create a new TwitterAvatars object
    $ta = new TwitterAvatars(new tmhOAuth($config));

    // check for stale files
    $ta->cleanupFiles();

    // check our authentication status
    if($ta->isAuthed()) {

        // has the user selected an option?
        if(isset($_POST["filter"])) {

            // is the user sure?
            if(isset($_POST["confirm"])) {

                // change the user's avatar
                $ta->commitAvatar($_POST["filter"]);

                // tweet if the user chose to
                if(isset($_POST["tweet"])) {
                    $ta->sendTweet("I just updated my avatar using Avatar Effects...");
                }

                $success = true;
            }

            // get the image paths for display
            $original = $ta->getPreview();
            $newimage = $ta->getPreview($_POST["filter"]);
        }
        // generate previews if the user has not chosen
        else {

            // $previews will be a list of images
            $previews = $ta->generatePreviews();
        }
    }
    // did the user request authorization?
    elseif(isset($_POST["auth"])) {

        // start authentication process
        $ta->auth();
    }
} catch(Exception $e) {

    // catch any errors that may occur
    $error = $e;
}
?>
Copy after login

HTML

在 PHP 代码之后,我们将输出适当的 HTML,从这个模板开始,它设置标题和主标题:



  
    
    Twitter Avatar Effects
    
  
  
      

Twitter Avatar Effects

Copy after login

这里是我们显示带有每个预览的图像输入的表单的地方:

  
      

Choose your weapon...

$path): ?>

Select one of the images above to change your Twitter avatar.

Copy after login

这是成功页面:

  
      

Success! Your Twitter avatar is now:

" alt="Your Avatar" width="73" style="max-width:90%">

Go see it

Copy after login

这是确认页面,我们在其中显示比较并提供取消的机会:

  
      

Are you sure?

" alt="Build a Twitter OAuth application" width="73" style="max-width:90%"> " alt="">
"> Cancel

Copy after login

请注意,确认表单在隐藏字段中包含所选的过滤器。

如果出现错误,我们会显示:

  
      

Error. Try again?

Copy after login

默认显示的是“连接到 Twitter”按钮作为图像输入(从本页底部下载一张图像到 img 目录):

  
      

Connect to Twitter to use this app.

Copy after login

这是完整的 HTML 部分:



  
    
    Twitter Avatar Effects
    
  
  
      

Twitter Avatar Effects

Choose your weapon...

$path): ?>

Select one of the images above to change your Twitter avatar.

Success! Your Twitter avatar is now:

" alt="Your Avatar" width="73" style="max-width:90%">

Go see it

Are you sure?

" alt="Build a Twitter OAuth application" width="73" style="max-width:90%"> " alt="">
"> Cancel

Error. Try again?

Connect to Twitter to use this app.

Copy after login

CSS

这里是一些使界面看起来更漂亮的基本 CSS,保存在 css/style.css 中:

html {
    background-color: #eee;
    text-align: center;
    font-family: "Lucida Grande",Verdana, sans-serif;
    font-size: 16px;
    color: #224;
}
body {
    width: 700px;
    margin: 30px auto;
    background-color: #acf;
    padding: 10px;
    border-radius: 10px;
    -moz-border-radius: 10px;
    -webkit-border-radius: 10px;
}
p {
    font-size: 1em;
}
h1 {
    font-size: 2em;
}
h2 {
    font-size: 1.6em;
}
.arrow {
    font-size: 4em;
    font-weight: bold;
}
Copy after login

结果

这是一个视频,详细介绍了我们完成的应用程序的外观:


结论

如果您完全按照本教程进行操作,您应该对 OAuth 以及创建简单 Twitter Web 应用程序所需的内容有很好的了解。一旦您了解了基本概念,使用 Twitter API 就会很容易 - 特别是如果您使用像 tmhOAuth 这样的库来处理次要细节。

我们在本教程中创建的简单示例可以轻松修改或扩展以执行任何操作。因此,如果您对一款很酷的新 Twitter 应用程序有好主意,请随意使用它作为基础。

感谢您的阅读。如果您对本教程有任何疑问或意见,请留言!

The above is the detailed content of Build a Twitter OAuth application. For more information, please follow other related articles on the PHP Chinese website!

source:php.cn
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact [email protected]
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
About us Disclaimer Sitemap
php.cn:Public welfare online PHP training,Help PHP learners grow quickly!