ホームページ >バックエンド開発 >PHPチュートリアル >Twitter OAuth アプリケーションを構築する
OAuth は最初は理解するのが難しい概念かもしれませんが、現在では Twitter API でその使用が必要になっているため、Twitter アプリケーションを作成する前にそれを理解する必要があります。このチュートリアルでは、OAuth について紹介し、基本的なアプリケーションを作成するプロセスを順を追って説明します。
アプリケーションのフローは次のようになります:
システムはユーザーに Twitter に接続するように求めます。
ディレクトリ (これはサーバーによって書き込み可能である必要があります)、および css
スタイルシートを保持するディレクトリと、画像を格納する img
ディレクトリ。
ディレクトリ ツリーは次のようになります:
###チュートリアル
CSSS
###写真###アプリに関連する詳細をフォームに記入します。この場合、
アプリケーション タイプは
Browser であり、デフォルトの コールバック URL を設定する必要があります。 URL は有効な形式であれば何でも構いません。実際の URL かどうかは問題にならないように、コード内のコールバックを書き換えます。便宜上、デフォルトのアクセス タイプ は 読み取りおよび書き込み にする必要があります。 登録して規約に同意すると、新しいアプリケーションに関する情報が表示されます。必要な重要な詳細は、Consumer Key と Consumer Secret で、次のようになります。
tmhOAuth ライブラリをダウンロード ライブラリを利用して、OAuth リクエストの背後にあるすべての詳細を処理します。このチュートリアルでは、ファイルのアップロードをサポートする @themattharris の tmhOAuth ライブラリを使用します。
GitHub から tmhOAuth をダウンロードしますtmhOAuth.php
を抽出しますアプリケーションはリクエスト トークンをアクセス トークンに交換します。アプリケーションが承認されたので、ステップ 1 のリクエスト トークンをアクセス トークンに交換できます。
ここでは 3 つのプロパティと単純なコンストラクターを作成します。 $tmhOAuth
属性は、クラス全体で使用される tmhOAuth オブジェクトになります。 $userdata
プロパティは、Twitter ユーザー名やステータスなどのユーザー情報を含むオブジェクトを保持します。 $state
プロパティは、現在の認証状態を追跡します。
コンストラクターは単に tmhOAuth オブジェクトを受け入れ、それを $tmhOAuth
プロパティに割り当てます。
リクエスト トークンを取得する方法は次のとおりです:
リーリー最初の部分を理解するには、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])
- API メソッド (拡張子なし)
(オプション、デフォルトは「json」) - 必要な応答形式 (JSON、XML など)
tmhOAuth::url() メソッドを使用する場合は形式を空白に設定する必要があります。また、
oauth_callback という変数を渡す必要があります。これは、Twitter によって承認されるとユーザーが返される場所です。
tmhOAuth::php_self() メソッドを使用して現在のページを参照します。これはコードです:
リーリー
tmhOAuth::response 属性に配列として保存されます。この属性には次のキー データが含まれます:
- HTTP応答コード
- 実際に返されたデータ
- 応答ヘッダー
oauth_token と
oauth_token_secret をセッション変数に入れます。これらは後で必要になるためです。 。これらは、応答に含まれるデータの配列を返す
tmhOAuth::extract_params() メソッドを使用して応答データから抽出されます。また、認証の次の段階にいることを示すために、
authstate セッション変数を設定します。これはコードです:
リーリー
oauth_token を含めて、ユーザーを oauth/authorize URL にリダイレクトする必要があります。これはコードです:
リーリー
リーリー
最初に行う必要があるのは、tmhOAuth::config 配列の
user_token と
user_secret を、前に取得したリクエスト トークンに設定することです。
リーリー
oauth_verifier をこのリクエストのパラメーターとして渡します。
リーリー
リーリー
最後のリダイレクトは、Twitter が残した URL パラメータをクリアし、Cookie が有効になるようにすることです。
リーリー
このコードには見覚えがあるはずです。ここで行うことは、user_token と
user_secret を設定し、1/account/verify_credentials に対して GET リクエストを行うことだけです。 Twitter が 200 コードで応答した場合、アクセス トークンは有効です。
$userdata プロパティに設定していることです。データは JSON 形式であるため、
json_decode() を使用して PHP オブジェクトに変換します。これもその行です:
リーリー
/** * 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 getTwitter 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->getTwitter 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 getTwitter 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->getTwitter 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 中国語 Web サイトの他の関連記事を参照してください。