PHP4環境でのTwitter API1.1対応

  • このエントリーをはてなブックマークに追加

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対応ブログが大変参考になりました。

  • このエントリーをはてなブックマークに追加

SNSでもご購読できます。

ads

コメントを残す

*