一開始,OAuth 可能是一個難以理解的概念,但隨著 Twitter API 現在需要使用它,您在創建 Twitter 應用程式之前需要了解它。本教學將向您介紹 OAuth,並引導您完成建立基本應用程式的過程。
在本教程中,我們將建立一個簡單的應用程序,允許用戶對其 Twitter 頭像應用不同的效果。為了使用 Twitter API,我們必須使用 OAuth 來授權我們的應用程式代表使用者發出請求。
我們的應用程式流程將是這樣的:
首先,我們應該設定來源目錄。我們需要一個lib
目錄來存放我們的PHP 庫(類別)文件,一個tmp
目錄來保存臨時文件(這需要伺服器可寫),一個css
目錄來存放我們的樣式表,以及一個img
目錄任何圖像的目錄。
您的目錄樹應如下所示:
為了使用 OAuth,您需要所謂的消費者金鑰和秘密來識別您的應用程式。要獲取這些信息,您必須按照以下步驟向 Twitter 註冊應用程式。
前往註冊頁面,如有必要請登入。您將看到如下所示的表格:
#填寫表單,其中包含與您的應用程式相關的詳細資訊。在我們的例子中,應用程式類型是瀏覽器,我們需要設定預設回調 URL。該 URL 可以是任何內容,只要格式有效即可。我們將重寫程式碼中的回調,因此它是否是真實的 URL 並不重要。為了方便起見,預設存取類型應為讀取和寫入。
註冊並接受條款後,您將看到有關新申請的資訊。我們需要的重要詳細資訊是消費者金鑰和消費者秘密,它們應該如下所示:
#我們將利用一個函式庫來處理 OAuth 請求背後的所有細節。在本教程中,我們將使用 @themattharris 的 tmhOAuth 庫,它支援檔案上傳。
使用 OAuth 進行身份驗證基本上是一個三步驟過程。有關更深入的解釋,請參閱 Twitter 上有關身份驗證的頁面,但這裡有一個摘要:
那麼讓我們開始寫一些程式碼。我們將在名為 TwitterApp
的類別中處理所有驗證任務。在名為 lib/TwitterApp.php
的新檔案中開始以下程式碼:
<?php class TwitterApp { /** * This variable holds the tmhOAuth object used throughout the class * * @var tmhOAuth An object of the tmhOAuth class */ public $tmhOAuth; /** * User's Twitter account data * * @var array Information on the current authenticated user */ public $userdata; /** * Authentication state * * Values: * - 0: not authed * - 1: Request token obtained * - 2: Access token obtained (authed) * * @var int The current state of authentication */ protected $state; /** * 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; } }
這裡我們建立了三個屬性和一個簡單的建構子。 $tmhOAuth
屬性將是一個 tmhOAuth 對象,它將在整個類別中使用。 $userdata
屬性將保存一個包含使用者資訊的對象,例如他們的 Twitter 使用者名稱和狀態。 $state
屬性追蹤目前的身份驗證狀態。
建構函數僅接受 tmhOAuth 物件並將其指派給 $tmhOAuth
屬性。
以下是取得請求令牌的方法:
/** * 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; }
要理解第一部分,您需要了解 tmhOAuth::request()
方法。此方法允許我們發出啟用 OAuth 的 HTTP 請求,其用法如下:
tmhOAuth::request($method, $url[, $params[, $useauth[, $multipart]]])
#string $method
- 要使用的請求方法(GET、POST 等)string $url
- 要存取的 URLarray $params
(可選) - 要包含在請求中的參數的關聯數組bool $useauth
(可選,預設 true) - 是否需要身份驗證? bool $multipart
(可選,預設 false)- 檔案上傳設定為 true對於 $url
參數,我們使用 tmhOAuth::url()
方法根據我們呼叫的 API 方法建立 URL:
tmhOAuth::url($request[, $format])
string $request
- api 方法(不含副檔名)string $format
(可選, 預設 'json") - 所需的回應格式(JSON、XML 等)現在您已經熟悉了這些方法,我們必須向 oauth/request_token API 方法發出 POST 請求。這將以特殊格式傳回 OAuth 數據,因此當我們使用 tmhOAuth::url()
方法時,需要將格式設為空白。我們還需要傳遞一個名為 oauth_callback
的變量,這是用戶在 Twitter 授權後將返回的位置。我們將使用 tmhOAuth::php_self()
方法來引用目前頁面。這是該程式碼:
// 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() ));
一旦我們發出請求,回應就會以陣列的形式儲存在 tmhOAuth::response
屬性中,其中包含以下關鍵資料:
code
- HTTP 回應代碼response
- 實際傳回的資料headers
- 回應標頭因此,我們程式碼的下一部分檢查回應程式碼(200 表示成功),然後將我們收到的oauth_token
和oauth_token_secret
放入會話變數中,因為稍後我們將需要它們。這些是使用 tmhOAuth::extract_params()
方法從回應資料中提取的,該方法會傳回回應中包含的資料數組。我們也設定了 authstate
會話變數來表示我們正處於身份驗證的下一階段。這是程式碼:
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; }
完成後,我們現在必須將使用者重新導向到 oauth/authorize URL,包括 GET 參數中的 oauth_token
。這是該程式碼:
// redirect the user to Twitter to authorize $url = $this->tmhOAuth->url("oauth/authorize", "") . '?oauth_token=' . $response["oauth_token"]; header("Location: ' . $url); exit;
以下是將請求令牌交換為存取權杖的方法:
/** * 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; }
我們要做的第一件事就是將tmhOAuth::config
陣列中的user_token
和user_secret
設定為我們先前取得的請求令牌。
// set the request token and secret we have stored $this->tmhOAuth->config["user_token"] = $_SESSION["authtoken"]; $this->tmhOAuth->config["user_secret"] = $_SESSION["authsecret"];
下一部分是我們向 oauth/access_token 發出 POST 請求的地方。我們將在 GET 變數中收到的 oauth_verifier
作為此請求中的參數傳遞。
// 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"] ));
Twitter 將使用存取權杖和機密進行回應,我們需要保存這些令牌以供將來的請求使用。因此,下一段程式碼採用這些並將每個內容保存在 cookie 中,然後將狀態設為 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; }
最後的重定向是為了清除Twitter留下的URL參數,讓cookie生效。
獲得存取令牌後,我們應該檢查以確保其有效。這是執行此操作的方法:
/** * 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); }
現在這段程式碼看起來應該很熟悉了。我們在這裡所做的就是設定 user_token
和 user_secret
並向 1/account/verify_credentials 發出 GET 請求。如果 Twitter 回應 200 代碼,則存取令牌有效。
另一個需要注意的細節是,我們在這裡使用此 Twitter 請求返回的資料填充 $userdata
屬性。資料是JSON格式的,所以我們使用json_decode()
將其轉換為PHP物件。這又是那一行:
// store the user data returned from the API $this->userdata = json_decode($this->tmhOAuth->response["response"]);
我們的 OAuth 元件就位後,是時候將所有內容整合在一起了。我們需要一個面向公眾的方法來允許我們的客戶端程式碼啟動身份驗證過程,如下所示:
/** * 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(); }
大多数 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; }
我们还可以使用一种方法来删除用户的身份验证。此 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); }
现在我们需要向 __construct()
方法添加一些内容,以确定应用程序在初始化时处于哪种身份验证状态。另外,由于我们的代码使用会话变量,因此我们应该确保会话是使用以下代码启动的:
// start a session if one does not exist if(!session_id()) { session_start(); }
下一部分是我们确定状态的地方。状态从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"]; }
如果状态为 1,则表示我们正在进行身份验证。所以我们现在可以继续这个过程:
// if we are in the process of authentication we continue if($this->state == 1) { $this->auth(); }
如果状态为2,我们应该验证访问令牌。如果身份验证失败,此代码将清除 cookie 并重置状态:
// verify authentication, clearing cookies if it fails elseif($this->state == 2 && !$this->auth()) { $this->endSession(); }
这是进行了这些更改的新构造函数:
/** * 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(); } }
现在所有授权代码都已完成,我们可以向我们的类添加一些常用功能。以下是通过 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); }
sendTweet()
方法接受一个字符串,将其限制为 140 个字符,然后在 POST 请求中将其发送到 1/statuses/update。这种模式现在应该非常熟悉了。
<?php class TwitterApp { /** * This variable holds the tmhOAuth object used throughout the class * * @var tmhOAuth An object of the tmhOAuth class */ public $tmhOAuth; /** * User's Twitter account data * * @var array Information on the current authenticated user */ public $userdata; /** * Authentication state * * Values: * - 0: not authed * - 1: Request token obtained * - 2: Access token obtained (authed) * * @var int The current state of authentication */ protected $state; /** * 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(); } } /** * 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); } }
现在我们有了一个处理所有 OAuth 任务的类,我们现在可以使用特定于我们的应用程序的功能来扩展它。这包括获取、更改和设置用户头像的能力。
我们将使用 TwitterAvatars 类扩展 TwitterApp 类。在名为 lib/TwitterAvatars.php
的新文件中开始以下代码:
<?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; } }
正如你所看到的,扩展类包括一个 $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; }
现在我们可以下载文件了,我们需要找到所需文件的位置。我们感兴趣的是两种不同的图像,标准尺寸的缩略图和原始的全尺寸图像。因此,我们将创建一个方法来获取每个 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"); }
请注意,我们正在使用 tmhOAuth 发出 GET 请求,传递 screen_name
和 size
参数,然后返回 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 get建立 Twitter OAuth 應用程式ImageURL() { // 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; }
现在我们可以找到并下载图像,我们需要一种方法来读取它们。我们将使用 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; }
描述上面发生的事情:
imagecreatefromstring()
函数将图像数据转换为 GD 资源。imagesx()
和 imagesy()
记录图像尺寸。imagecreatetruecolor()
创建具有相同尺寸的新空白真彩色图像。imagecopy()
函数将原始图像复制到新图像中。无论原始颜色模式如何,这都会产生原始图像的真彩色版本。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; }
现在我们已经拥有了为我们的应用程序提供支持的所有部分,我们可以开始将它们组合在一起。在我们的应用程序流程中,我们将为用户提供可供选择的预览选项。以下是生成这些预览的方法:
/** * 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; }
我们要做的第一件事是检查用户是否已通过身份验证,然后获取用户名以便稍后在文件名中使用。
// 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;
然后我们使用我们创建的 getImageURL()
和 download()
方法下载用户的图像。该数据将在每次预览中重复使用,因此我们将其保存在 $data
变量中。
// cache the raw data to use $data = $this->download($this->getImageURL());
接下来,我们使用 _orig 后缀保存未修改的副本。这是为了稍后进行视觉比较。
// copy the original image $img = $this->readImage($data); $this->saveImage($img, $username . "_orig");
该方法的最后一部分是我们循环遍历 $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;
我们的应用程序流程的下一部分要求用户确认他们的选择,因此我们需要一种方法来查找特定的预览。下面是根据作为参数传递的选项获取图像路径的简单方法,默认为原始图像:
/** * 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->get建立 Twitter OAuth 應用程式ImageURL()); $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"); }
这应该很容易理解,因为它与 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; }
这里棘手的部分是实际的 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);
所有这些文件操作的副作用是留下大量临时文件。下面是清理临时目录的方法:
/** * 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); } }
这只是循环遍历 PNG 文件,删除那些超过 12 小时的文件。它还检查自我们使用 .last_check 文件上的时间戳进行检查以来已经过去了多长时间,从而允许我们将检查限制为每小时一次。这样我们就可以在每个请求上调用这个方法,而不会浪费资源。
注意:我们在 PHP 中使用 glob()
函数,这是获取与模式匹配的文件数组的简单方法。
&?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 get建立 Twitter OAuth 應用程式ImageURL() { // 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->get建立 Twitter OAuth 應用程式ImageURL()); $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); } } }
我们将应用程序的所有组件放在一起,所以现在我们需要的只是用户界面。这里的所有代码都将进入根目录中的index.php文件。我们将首先包含库并设置配置:
<?php // include our libraries include 'lib/tmhOAuth.php'; include 'lib/TwitterApp.php'; include 'lib/TwitterAvatars.php'; // set the consumer key and secret define("CONSUMER_KEY", 'qSkJum23MqlG6greF8Z76A"); define("CONSUMER_SECRET", 'Bs738r5UY2R7e5mwp1ilU0voe8OtXAtifgtZe9EhXw"); ?>
注意:请务必将 CONSUMER_KEY
和 CONSUMER_SECRET
替换为您自己的。
我们将把代码放在 try-catch 块中,这样我们就可以优雅地处理任何错误,将它们的消息分配给 $error
变量。
try { } catch(Exception $e) { // catch any errors that may occur $error = $e; }
在 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));
此时我们可以清除所有旧的临时文件:
// check for stale files $ta->cleanupFiles();
接下来,我们检查用户是否已通过身份验证,或者如果用户未通过身份验证,则检查用户是否已请求身份验证,在这种情况下,我们通过调用 auth()
方法来启动该过程:
// check our authentication status if($ta->isAuthed()) { } // did the user request authorization? elseif(isset($_POST["auth"])) { // start authentication process $ta->auth(); }
如果用户已通过身份验证,我们需要检查是否已选择某个选项,否则我们将生成预览:
// 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(); } }
如果选择了某个选项,我们需要获取要显示的旧图像和新图像的路径:
// has the user selected an option? if(isset($_POST["filter"])) { // get the image paths for display $original = $ta->getPreview(); $newimage = $ta->getPreview($_POST["filter"]); }
最后,我们检查用户是否确认了他们的选择并应用更改。如果需要,我们还会发送一条推文,并将 $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"]); }
这是我们迄今为止所拥有的:
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; } ?>
在 PHP 代码之后,我们将输出适当的 HTML,从这个模板开始,它设置标题和主标题:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Twitter Avatar Effects</title> <link rel="stylesheet" href="css/style.css"> </head> <body> <h1>Twitter Avatar Effects</h1> </body> </html>
这里是我们显示带有每个预览的图像输入的表单的地方:
<?php if(isset($previews)): ?> <h2>Choose your weapon...</h2> <form action="index.php" method="post"> <?php foreach($previews as $filter => $path): ?> <input type="image" src="<?php echo $path; ?>" alt="<?php echo ucfirst($filter); ?>" width="73" height="73" name="filter" value="<?php echo $filter; ?>"> <?php endforeach; ?> </form> <p>Select one of the images above to change your Twitter avatar.</p>
这是成功页面:
<?php elseif(isset($success)): ?> <h2>Success! Your Twitter avatar is now:</h2> <img src="<?php echo $newimage; ? alt="建立 Twitter OAuth 應用程式" >" alt="Your Avatar" width="73" style="max-width:90%"> <p><a href="http://twitter.com/<?php echo $ta->userdata->screen_name; ?>">Go see it</a></p>
这是确认页面,我们在其中显示比较并提供取消的机会:
<?php elseif(isset($newimage)): ?> <h2>Are you sure?</h2> <img src="<?php echo $original; ? alt="建立 Twitter OAuth 應用程式" >" alt="建立 Twitter OAuth 應用程式" width="73" style="max-width:90%"> <span class="arrow">⇒</span> <img src="<?php echo $newimage; ? alt="建立 Twitter OAuth 應用程式" >" alt="<?php echo ucfirst($_POST["filter"]); ?>"> <form action="index.php" method="post"> <input type="hidden" name="filter" value="<?php echo $_POST["filter"]; ?>"> <input type="submit" name="confirm" value="Confirm"> <a href="index.php">Cancel</a> <p><label>Tweet about your new avatar? <input type="checkbox" name="tweet" value="true"></label></p> </form>
请注意,确认表单在隐藏字段中包含所选的过滤器。
如果出现错误,我们会显示:
<?php elseif(isset($error)): ?> <p>Error. <a href="index.php">Try again?</a></p>
默认显示的是“连接到 Twitter”按钮作为图像输入(从本页底部下载一张图像到 img 目录):
<?php else: ?> <form action="index.php" method="post"> <input type="image" src="img/sign-in-with-twitter-l.png" alt="Connect to Twitter" name="auth" value="1"> </form> <p>Connect to Twitter to use this app.</p> <?php endif; ?>
这是完整的 HTML 部分:
Twitter Avatar Effects Twitter Avatar Effects
<?php if(isset($previews)): ?> <h2>Choose your weapon...</h2> <form action="index.php" method="post"> <?php foreach($previews as $filter => $path): ?> <input type="image" src="<?php echo $path; ?>" alt="<?php echo ucfirst($filter); ?>" width="73" height="73" name="filter" value="<?php echo $filter; ?>"> <?php endforeach; ?> </form> <p>Select one of the images above to change your Twitter avatar.</p> <?php elseif(isset($success)): ?> <h2>Success! Your Twitter avatar is now:</h2> <img src="<?php echo $newimage; ? alt="建立 Twitter OAuth 應用程式" >" alt="Your Avatar" width="73" style="max-width:90%"> <p><a href="http://twitter.com/<?php echo $ta->userdata->screen_name; ?>">Go see it</a></p> <?php elseif(isset($newimage)): ?> <h2>Are you sure?</h2> <img src="<?php echo $original; ? alt="建立 Twitter OAuth 應用程式" >" alt="建立 Twitter OAuth 應用程式" width="73" style="max-width:90%"> <span class="arrow">⇒</span> <img src="<?php echo $newimage; ? alt="建立 Twitter OAuth 應用程式" >" alt="<?php echo ucfirst($_POST["filter"]); ?>"> <form action="index.php" method="post"> <input type="hidden" name="filter" value="<?php echo $_POST["filter"]; ?>"> <input type="submit" name="confirm" value="Confirm"> <a href="index.php">Cancel</a> <p><label>Tweet about your new avatar? <input type="checkbox" name="tweet" value="true"></label></p> </form> <?php elseif(isset($error)): ?> <p>Error. <a href="index.php">Try again?</a></p> <?php else: ?> <form action="index.php" method="post"> <input type="image" src="img/sign-in-with-twitter-l.png" alt="Connect to Twitter" name="auth" value="1"> </form> <p>Connect to Twitter to use this app.</p> <?php endif; ?>
这里是一些使界面看起来更漂亮的基本 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; }
这是一个视频,详细介绍了我们完成的应用程序的外观:
如果您完全按照本教程进行操作,您应该对 OAuth 以及创建简单 Twitter Web 应用程序所需的内容有很好的了解。一旦您了解了基本概念,使用 Twitter API 就会很容易 - 特别是如果您使用像 tmhOAuth 这样的库来处理次要细节。
我们在本教程中创建的简单示例可以轻松修改或扩展以执行任何操作。因此,如果您对一款很酷的新 Twitter 应用程序有好主意,请随意使用它作为基础。
感谢您的阅读。如果您对本教程有任何疑问或意见,请留言!
以上是建立 Twitter OAuth 應用程式的詳細內容。更多資訊請關注PHP中文網其他相關文章!