PHP4の環境でTwitter API1.1対応した時の備忘録です。
現状
http://api.twitter.com/1/statuses/followers.format
- ↑APIを使ってアカウント毎(都道府県の)に、フォローワーを取得
- ユーザーのscreen_nameが有るかチェック
- screen_nameが有ったら特定の処理
という事をやっていました。
今後
まずはoauth対応が必要
developerサイトでコンシューマ登録(Consumer key、Consumer secret、Access token、Access token secret取得)し、oauthが使えるようにする。
自分のアカウント情報わかればいいので、コールバックは設定なし(developerサイトで取得したAccess tokenのみでOK)
どのAPIを使うか
もともと、statuses/followers.formatは非推奨だったようで且つAPI1.1のドキュメントには使えると書いてないし、
API1のドキュメントにも GET followers/ids and GET users/lookup. 使ってねって書いてある。
https://api.twitter.com/1.1/friends/ids.json?cursor=-1&screen_name=twitterapi
↑からid一覧が取得できるのでそこから
https://api.twitter.com/1.1/users/lookup.json?screen_name=twitterapi,twitter
↑を使って、screen_nameを取得する。
※100ユーザーまで一度に調べれる(カンマ区切りでパラメーター)
疑問
APIのリクエストリミット制限に引っかかるのでは?
- 今までのリクエストリミット
- OAuthなしの場合はIPアドレス当たり1時間150回、OAuthありの場合は1アカウント当たり1時間350回
-
新しいAPIのリクエストリミット
- ・API 1.1では、1アカウント当たり15分で“エンドポイントごとに(API毎に)”15回または180回
https://dev.twitter.com/docs/api/1.1/get/application/rate_limit_status
↑で各エンドポイント毎のリミッター分かる。
結局
制限は仕方ないと割りきって、各アカウントでfollowers/ids(リミット:15回/15分)でフォローされているidを取得して、users/lookup(リミット:180回/15分)でscreen_nameを取得する。
users/lookupはパラメーターが長くなるのでPOSTがよさそう。
OAuthのパーミッションがRead-only だとPOST出来ないよ。
Utilクラス
<?php
require_once 'HTTP/Request.php';
require_once 'Jsphon/Decoder.php';
require_once 'twitter_config.php';
error_reporting(-1);
define('REQUEST_DOMAIN', 'https://api.twitter.com');
class TwitterUtil {
// ---------------------------------------------
// friendships/lookup
// ---------------------------------------------
function friendshipsLookup($follow_account, $screen_name) {
if (!isset($follow_account) || !isset($screen_name)) {
return;
}
// APIパラメーター
$endPointParam = array('screen_name' => $screen_name);
// アカウント情報取得
$accountConfig = $GLOBALS['TWITTER_CONFIG']['ACCOUNT'][$follow_account];
// 投げる変数類設定
$requestParam = $this->_initOAuthParam($accountConfig);
$requestParam += $endPointParam;
// Signatureベーステキストの作成
$requestUri = REQUEST_DOMAIN . '/1.1/friendships/lookup.json';
$signatureBase = $this->_createSignatureBase($requestUri, $requestParam, HTTP_REQUEST_METHOD_GET);
// 最初に定義したパラメータリストからscreen_name撤去
unset($requestParam['screen_name']);
// Signatureの作成
$keys = $this->_rawurlencode($accountConfig['CONSUMER_SECRET']) . '&' . $this->_rawurlencode($accountConfig['ACCESS_TOKEN_SECRET']);
$requestParam['oauth_signature'] = base64_encode($this->_hmacsha1($keys, $signatureBase));
// リクエスト
$result = $this->_authRequest($requestUri, $requestParam, HTTP_REQUEST_METHOD_GET, $endPointParam);
return $result;
}
// ---------------------------------------------
// followers/ids
// ---------------------------------------------
function followersIds($follow_account, $cursor) {
if (!isset($follow_account) || !isset($cursor)) {
return;
}
// APIパラメーター
$endPointParam = array('cursor' => $cursor, 'screen_name' => $follow_account,);
// アカウント情報取得
$accountConfig = $GLOBALS['TWITTER_CONFIG']['ACCOUNT'][$follow_account];
// 投げる変数類設定
$requestParam = $this->_initOAuthParam($accountConfig);
$requestParam += $endPointParam;
// Signatureベーステキストの作成
$requestUri = REQUEST_DOMAIN . '/1.1/followers/ids.json';
$signatureBase = $this->_createSignatureBase($requestUri, $requestParam, HTTP_REQUEST_METHOD_GET);
// 最初に定義したパラメータリストからcursor,screen_name撤去
unset($requestParam['cursor'], $requestParam['screen_name']);
// Signatureの作成
$keys = $this->_rawurlencode($accountConfig['CONSUMER_SECRET']) . '&' . $this->_rawurlencode($accountConfig['ACCESS_TOKEN_SECRET']);
$requestParam['oauth_signature'] = base64_encode($this->_hmacsha1($keys, $signatureBase));
// リクエスト
$result = $this->_authRequest($requestUri, $requestParam, HTTP_REQUEST_METHOD_GET, $endPointParam);
return $result;
}
// ---------------------------------------------
// users/lookup
// ---------------------------------------------
function usersLookup($follow_account, $user_id ) {
if (!isset($follow_account) || !isset($user_id)) {
return;
}
// APIパラメーター
$endPointParam = array('user_id' => $user_id);
// アカウント情報取得
$accountConfig = $GLOBALS['TWITTER_CONFIG']['ACCOUNT'][$follow_account];
// 投げる変数類設定
$requestParam = $this->_initOAuthParam($accountConfig);
$requestParam += $endPointParam;
// Signatureベーステキストの作成
$requestUri = REQUEST_DOMAIN . '/1.1/users/lookup.json';
$signatureBase = $this->_createSignatureBase($requestUri, $requestParam, HTTP_REQUEST_METHOD_POST);
// 最初に定義したパラメータリストからscreen_name撤去
unset($requestParam['user_id']);
// Signatureの作成
$keys = $this->_rawurlencode($accountConfig['CONSUMER_SECRET']) . '&' . $this->_rawurlencode($accountConfig['ACCESS_TOKEN_SECRET']);
$requestParam['oauth_signature'] = base64_encode($this->_hmacsha1($keys, $signatureBase));
// リクエスト
$result = $this->_authRequest($requestUri, $requestParam, HTTP_REQUEST_METHOD_POST, $endPointParam);
return $result;
}
// ---------------------------------------------
// application/rate_limit_status
// ---------------------------------------------
function status($follow_account, $resources) {
if (!isset($follow_account) || !isset($resources)) {
return;
}
// APIパラメーター
$endPointParam = array('resources' => $resources);
// アカウント情報取得
$accountConfig = $GLOBALS['TWITTER_CONFIG']['ACCOUNT'][$follow_account];
// 投げる変数類設定
$requestParam = $this->_initOAuthParam($accountConfig);
$requestParam += $endPointParam;
// Signatureベーステキストの作成
$requestUri = REQUEST_DOMAIN . '/1.1/application/rate_limit_status.json';
$signatureBase = $this->_createSignatureBase($requestUri, $requestParam, HTTP_REQUEST_METHOD_GET);
// 最初に定義したパラメータリストからresources撤去
unset($requestParam['resources']);
// Signatureの作成
$keys = $this->_rawurlencode($accountConfig['CONSUMER_SECRET']) . '&' . $this->_rawurlencode($accountConfig['ACCESS_TOKEN_SECRET']);
$requestParam['oauth_signature'] = base64_encode($this->_hmacsha1($keys, $signatureBase));
// リクエスト
$result = $this->_authRequest($requestUri, $requestParam, HTTP_REQUEST_METHOD_GET, $endPointParam);
return $result;
}
// ---------------------------------------------
// リクエスト(GET,POST)
// ---------------------------------------------
function _authRequest($uri, $param, $method, $addData) {
// HTTPヘッダーに追加するAuthorizationパラメータ作成
$requestHeaders = array();
foreach ($param as $k => $v) {
$requestHeaders[] = $k . '="' . $this->_rawurlencode($v) . '"';
}
$requestHeader = 'OAuth ' . implode(', ', $requestHeaders);
// PEARのRequestを使ってポスト
$request = &new HTTP_Request($uri);
$request->setMethod($method);
$request->addHeader('Authorization', $requestHeader);
if ($method == HTTP_REQUEST_METHOD_GET) {
foreach ($addData as $k => $v) {
$request->addQueryString($k, $v);
}
} else {
foreach ($addData as $k => $v) {
$request->addPostData($k, $v);
}
}
$result = $request->sendRequest();
if (PEAR::isError($result)) {
return;
}
$json = new Jsphon_Decoder();
return $json->decode($request->getResponseBody());
}
// ---------------------------------------------
// OAuth認証共通パラメーター初期化
// ---------------------------------------------
function _initOAuthParam($config) {
return array(
'oauth_consumer_key' => $config['CONSUMER_KEY'],
'oauth_nonce' => md5(uniqid(mt_rand(), TRUE)),
'oauth_signature_method' => 'HMAC-SHA1',
'oauth_timestamp' => time(),
'oauth_token' => $config['ACCESS_TOKEN'],
'oauth_version' => '1.0',
);
}
// ---------------------------------------------
// Signatureベーステキスト作成
// ---------------------------------------------
function _createSignatureBase($uri, $param, $method = 'GET') {
if (!isset($uri)) {
return;
}
// 変数名でソート
ksort($param);
// Sigunatureのベーステキストを作成
$queries = array();
foreach ($param as $k => $v) {
$queries[] = $k . '=' . $this->_rawurlencode($v);
}
$query = implode('&', $queries);
// それぞれをURLエンコードして接続
$signatureBase = "{$method}&";
$signatureBase .= $this->_rawurlencode($uri) . '&';
$signatureBase .= $this->_rawurlencode($query);
return $signatureBase;
}
// ---------------------------------------------
// URLエンコード RFC3986版
// ---------------------------------------------
function _rawurlencode($str) {
$str = rawurlencode($str);
$str = str_replace('+', ' ', $str);
$str = str_replace('%7E', '~', $str);
return $str;
}
// ---------------------------------------------
// hash_hmac('sha1', $data, $key, true)の代わり
// ---------------------------------------------
function _hmacsha1($key, $data) {
$blocksize = 64;
$hashfunc = 'sha1';
if (strlen($key) > $blocksize) {
$key = pack('H*', $hashfunc($key));
}
$key = str_pad($key, $blocksize, chr(0x00));
$ipad = str_repeat(chr(0x36), $blocksize);
$opad = str_repeat(chr(0x5c), $blocksize);
$hmac = pack('H*', $hashfunc(($key ^ $opad) . pack('H*', $hashfunc(($key ^ $ipad) . $data))));
return $hmac;
}
}
PEARのHTTP_REQUESTを使ってリクエストを送って、jsonはJsphonを使ってデコードしています。
PHP4の環境なので
PHP4でoAuth。Twitter APIでつぶやく。「マチルダさ~ん」 | motooLogue.
を参考に作りました。
twitter_config.phpには
<?php
$GLOBALS['TWITTER_CONFIG']['ACCOUNT']['aaa'] = array(
'CONSUMER_KEY' => 'asdfasdfeii83jeoasdfasd',
'CONSUMER_SECRET' => 'asdfase90ia3aksjdflkasdlfasdfasdfas',
'ACCESS_TOKEN' => '12103981-asdfasdJOE+Spassaspdoep',
'ACCESS_TOKEN_SECRET' => 'asdfDFIELKJF38293lsdsf89DKKKLL'
);
のような感じでアカウント情報を保持するようにしました。
呼び出し元は
<?php
require_once 'twitter_util.php';
$twitterUtil = new TwitterUtil();
$data = $twitterUtil->followersIds('aaa', '-1');
echo '<pre>';
print_r($data);
echo '</pre>';
exit;
のようにリクエスト送信、レスポンス取得ができるので、各アカウントでfollowers/ids(リミット:15回/15分)でフォローされているidを取得して、users/lookup(リミット:180回/15分)でscreen_nameを取得、チェックして特定の処理を行えば対応完了となりました。
PHP4用のOAuth対応ブログが大変参考になりました。