素のphpでOAuthする
概要
なるべく素のphpでOAuthしてみる。
これで他のライブラリ等に依存しなくても、twitterのAPIや、tumblerのAPIが使える。
アクセストークンを取得するまでの流れはまた別な機会に。アクセストークンが取得済みの状況で、APIを使うところをやってみる。
phpの5.4ならば、やりやすい条件はそろっている。
- php5.4では、rawurlencode()関数がRFC 3986方式になっている。
- php5.4では、http_build_query()関数がRFC 3986に対応した(オプションで指定すれば)。
20行目までは必要な関数の準備。メインの処理がその後に。
サンプル スクリプト
<?php //ライブラリは要らないけど、これだけは使いたい //OAuth式 パラメータのソート用比較関数 function oauthcmp($a, $b) { return strcmp($a[0], $b[0]) ? strcmp(rawurlencode($a[0]), rawurlencode($b[0])) : strcmp(rawurlencode($a[1]), rawurlencode($b[1])); } //OAuth式 パラメータのソート関数 function oauthsort($a) { $b = array_map(null, array_keys($a), $a); usort($b, 'oauthcmp'); $c = array(); foreach ($b as $v) { $c[$v[0]] = $v[1]; } return $c; } //OAuthのいつもの $consumer_key = '**********************'; $consumer_secret = '****************************************'; $oauth_token = '********-*****************************************'; $oauth_token_secret = '*****************************************'; //どの twitter API $url = 'https://api.twitter.com/1/statuses/update.json'; //POST or GET $method = 'POST'; //パラメータ $parameters = array('status' => 'テストのツイート'); $oauth_parameters = array( 'oauth_version' => '1.0', 'oauth_nonce' => microtime(), 'oauth_timestamp' => time(), 'oauth_consumer_key' => $consumer_key, 'oauth_token' => $oauth_token, 'oauth_signature_method' => 'HMAC-SHA1', ); //署名を作る //パラメータを仕様のとおりに並び替える(これが少々手間) $base_string = implode('&', array( rawurlencode($method), rawurlencode($url), rawurlencode(http_build_query(oauthsort(array_merge($oauth_parameters, $parameters)), '', '&', PHP_QUERY_RFC3986)) )); $key = implode('&', array(rawurlencode($consumer_secret), rawurlencode($oauth_token_secret))); $oauth_parameters['oauth_signature'] = base64_encode(hash_hmac('sha1', $base_string, $key, true)); //POSTリクエストはfile_get_contents()で十分可能 $options = array( 'http'=>array( 'method' => $method, 'header' => array( 'Authorization: OAuth ' . http_build_query($oauth_parameters, '', ',', PHP_QUERY_RFC3986), ), 'content' => http_build_query($parameters) ) ); $result = file_get_contents($url, false, stream_context_create($options)); //結果を確認してみる var_dump($result);
注意など
oauthcmpでなにやら複雑な比較をしているのは、パラメータをキーの順で並び替える部分で、キーが同じだったら値の順で並び替えることになっているため。
そのように作ってみたものの、パラメータをphpの連想配列で表現していたら、キーを重複させたくても重複できない。
また、一般にパラメータに配列を受け取るようなAPIが(たぶん)ないと思うので、あまり問題にならないと思う。
問題が発生したらまたそのときに。。。
追記: GETリクエストも考慮した
上の例ではGET渡しするパラメータを考えていなかった。GET渡しするパラメータも取り扱うようにすると、こんな感じ。
<?php //OAuthのいつもの $consumer_key = '**********************'; $consumer_secret = '****************************************'; $oauth_token = '**************************************************'; $oauth_token_secret = '*****************************************'; //どのAPI?(URL) $url = 'https://api.twitter.com/1.1/search/tweets.json'; //POST or GET $method = 'GET'; //パラメータ $post_parameters = array( ); $get_parameters = array( 'q' => 'あああ', 'lang' => 'ja', ); $oauth_parameters = array( 'oauth_consumer_key' => $consumer_key, 'oauth_nonce' => microtime(), 'oauth_signature_method' => 'HMAC-SHA1', 'oauth_timestamp' => time(), 'oauth_token' => $oauth_token, 'oauth_version' => '1.0', ); //署名を作る //署名の計算にはoauth_*とAPIのパラメータを含めて全部使う //GETリクエストのときでもURLにはクエリ文字列を含めない //キーと値でソートする仕様なのだが、キーが重複したり英数字以外が混じったりしないのでksort関数で十分 $a = array_merge($oauth_parameters, $post_parameters, $get_parameters); ksort($a); $base_string = implode('&', array( rawurlencode($method), rawurlencode($url), rawurlencode(http_build_query($a, '', '&', PHP_QUERY_RFC3986)) )); $key = implode('&', array(rawurlencode($consumer_secret), rawurlencode($oauth_token_secret))); $oauth_parameters['oauth_signature'] = base64_encode(hash_hmac('sha1', $base_string, $key, true)); //APIにリクエスト送信 $options = array( 'http'=>array( 'method' => $method, 'header' => array( 'Authorization: OAuth ' . http_build_query($oauth_parameters, '', ',', PHP_QUERY_RFC3986), ), 'content' => http_build_query($post_parameters) ) ); $result = file_get_contents($url . ($get_parameters ? '?' . http_build_query($get_parameters) : ''), false, stream_context_create($options)); //結果を確認してみる var_dump($result);
コメント