ラベル Php の投稿を表示しています。 すべての投稿を表示
ラベル Php の投稿を表示しています。 すべての投稿を表示

2011年6月6日月曜日

PHPでOAuthをやっつける方法

前々回のOAuthについてまとめてみた、前回のJavaでOAuthをやっつける方法に続いて今回はPHPでOAuth認証を利用したAPIへのアクセスを実装してみる。

今回も、天下のoauth.netのoauthライブラリ(Google Code)を使う。oauthのリポジトリからOAuth.phpを持ってくれば準備完了。


すぐ使いたい時

Java版と併せてライブラリにまとめてみたのでここ(Google Code)から取得してもらうと、簡単に実装できる。HowToUse.phpを見てもらうのが手っ取り早いけど、How to use for phpにも触りだけ書いてあるのでよかったら見てください。


やること

  • 各種の設定を定義
  • リクエストトークンを取得する
  • ユーザーにアプリケーションを承認してもらう
  • アクセストークンを取得する

各種の設定を定義

サービスプロバイダーのエンドポイント情報やコンシューマの情報を、下記のように定義する。

$TOMOKEY = array(
  'CONSUMER_KEY' => 'anonymous',
  'CONSUMER_SECRET' => 'anonymous',
  'CONSUMER_CALLBACK_URL' => 'oob',
  'REQUEST_TOKEN_URL'
    => 'https://www.google.com/accounts/OAuthGetRequestToken',
  'USER_AUTHORIZATION_URL'
     => 'https://www.google.com/accounts/OAuthAuthorizeToken',
  'ACCESS_TOKEN_URL'
     => 'https://www.google.com/accounts/OAuthGetAccessToken'
);
※Goole Data APIをクライアントアプリから利用する例なので、ウェブアプリの場合とかはJavaでOAuthをやっつけるを参照。

リクエストトークンを取得する

Step1.コンシューマ情報をオブジェクト化

// consumer info
$consumer_key
  = $consumer_config['CONSUMER_KEY'];
$consumer_secret
  = $consumer_config['CONSUMER_SECRET'];
$consumer_callback
  = $consumer_config['CONSUMER_CALLBACK_URL'];

// OAuthConsumerオブジェクトを構築
$consumer
  = new OAuthConsumer(
      $consumer_key,
      $consumer_secret,
      $consumer_callback);
Step2.署名ツールを生成
// 署名方式はHMAC-SHA1
$signature_method = new OAuthSignatureMethod_HMAC_SHA1();
※今回はよく使うSHA-1方式にしとく。
Step3.パラメータを生成
$params
  = array(
      'oauth_callback'
        => $consumer->callback_url,
      'scope'
        => 'https://www.google.com/calendar/feeds/');
※Google Calendar APIにアクセスする例なので、scopeというパラメータも設定してる。
Step4.リクエストオブジェクトを生成
$request
  = OAuthRequest::from_consumer_and_token(
      $consumer,
      NULL, // RequestToken取得時は不要
      'GET',
      $TOMOKEY['REQUEST_TOKEN_URL'],
      $params);

// 署名する
$request->sign_request($signature_method, $consumer, NULL);
※先につくっておいたOAuthConsumerオブジェクト、パラメータを使ってリクエストオブジェクトを生成し、署名ツールで署名を行う。
Step5.HTTP通信を行う。
// cURLリソースの生成
$ch = curl_init();
// Locationヘッダは無視
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
// サーバ証明書の検証を行わない
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
// レスポンスを文字列として取得する設定
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// (出力結果に)ヘッダを含めない
curl_setopt($ch, CURLOPT_HEADER, false);
// URLを指定
curl_setopt($ch, CURLOPT_URL, $request->to_url());

// 実行
$result = curl_exec($ch);

// close curl resource to free up system resources
curl_close($ch);

// レスポンスを解析
parse_str($result, $result_map);

// リクエストトークンを取得
$req_token
  = new OAuthToken(
      $result_map['oauth_token'],
      $result_map['oauth_token_secret']);
※POSTで送信する場合には、CURLOPT_URLには、to_url()の代わりにget_normalized_http_url()を使うのと、CURLOPT_HTTPHEADERにarray($request->to_header)ってな感じでAuthorizationパラメータ設定が必要。その他の詳細はコード(httpという名称のfunction)を参照。

ユーザーにアプリケーションを承認してもらう

下記のように作成したURLに対して、ブラウザでアクセスしてもらう。PHPならheaderでリダイレクトするのが一般的なのかな?

// サービスプロバイダーが定義しているエンドポイント
$auth_url = $TOMOKEY['USER_AUTHORIZATION_URL'];

// ここで作成したURLにアクセスしてもらう
$url
  = $auth_url .
    '?' .
    OAuthUtil::build_http_query(array('oauth_token' => $req_token->key));
※承認画面が表示されて、ポチっとした後はcallback URLにリダイレクト or 画面にverifier表示。

アクセストークンを取得する

Step1.色々

リクエストトークンの取得と同じ手順でコンシューマ情報のオブジェクト化と署名ツールの生成を行う。 ちなみに、取得しておいたリクエストトークンは、この後使うのでセッションとかデータストアとかを使って保存しておく必要がある。
Step2.パラメータを生成
$params
  = array('oauth_verifier' => $verifier);
※ここで使用している$verifierは、ユーザー承認により取得した文字列のこと。callback URLが指定されているなら、リダイレクト先にoauth_verifierとして送信される。
Step3.リクエストオブジェクトを生成
$request
  = OAuthRequest::from_consumer_and_token(
      $consumer,
      $req_token, // 保存しておいたリクエストトークンオブジェクト
      'GET',
      $TOMOKEY['ACCESS_TOKEN_URL'],
      $params);

// 署名する
$request->sign_request($signature_method, $consumer, $req_token);
※リクエストトークンの取得時と異なるのは3点。
  • URLがACCESS_TOKEN_URL
  • リクエストトークンを送信する
  • 署名鍵としてリクエストトークンのシークレットの方も利用する
Step4.HTTP通信を行う。
この手順は前述と同じ。最後に取得できるのがアクセストークンってとこだけ違う。

終わり

アクセストークンを使ってAPIアクセス方法は、アクセストークンの取得手順でリクエストトークンの代わりにアクセストークンを指定した場合と同じ。
だけど、リダイレクトの考慮をしないとGoogle Calendar APIとかは使えないので、コード(invokeという名称のfunction)をご参照あれ。

2011年5月17日火曜日

PHPでオブジェクトと文字列を比較する

PHPを使っていて、ずーっと腑に落ちないことがあったけど、今さっきすっきりしたのでメモっておくことにする。

まず、ここにXMLを読み込んでオブジェクト化するコードがある。

XMLをオブジェクト化

// XMLデータを適当に定義
$plain_xml = '<hoge><foo>bar</foo></hoge>';

// 解析してSimpleXMLElementオブジェクトにする
$xml_obj = simplexml_load_string($plain_xml);

// fooタグの型を調べてみる
//   SimpleXMLElementと出力される
echo get_class($xml_obj->foo);

今回は、<hoge>というルートタグに囲まれた<foo>タグに文字列barが格納されたXMLを用意した。これをオブジェクト化したものも、子要素の<foo>タグをオブジェクト化したものも、共にSimpleXMLElement型であることが確認できる。

この後が不思議。

不思議なコード

if ("bar" == $xml_obj->foo)
 echo "TRUE";
else
 echo "FALSE";

みたまんま。TRUEと出力される。直感的にはよく分かるし何も問題無い気がする。が、前項にあった通り$xml_obj->fooは、SimpleXMLElement型であり"bar"は見た通り文字列型である。つまり「===」で比較すればFALSEとなる関係にある。なぜすんなりと比較できているのか不思議ぃ。

ところでJavaの場合、上記の例は必ずFALSEになる。Javaでの「==」による比較は、ポインタが指し示すアドレスの比較であり「"bar"」と「$xml_obj->foo」が同じ位置を指し示すことはない。Javaに慣れ親しんでいると、「"文字列"」と「==」が隣同士に並ぶことはありえないといった感覚をもつ人が多いと思う。

そんなJavaな感覚に基づいた違和感と、PHPの基礎すら勉強していないという怠惰な自分のせいで不思議な感じがしていたけど、実はPHPにも__toString()というマジックメソッドが用意されている。以下マニュアルより引用。

__toString

__toString メソッドにより、 クラスが文字列に変換される際の動作を決めることができます。たとえば echo $obj; としたときに何を表示させるかといったことです。

また忘れた時のために、まとめておくと

2010年11月26日金曜日

PHPでcURLでWindowsでエラーで。

問題点

某仕事の都合上、PHPを使うことになり、HTTPでAPIを呼び出すためにcURL(Client URL Library)を使うことになった。手元のWindows XPマシンにApache 2 / 2.2とPHP5.2.14をインストールしてテストを試みたが、Unable to load Dynamic Library php_curl.dllってな感じで怒られた。


調査結果

Google先生によると、そもそも動作に必要であり、かつ今回のようなエラーがでる原因は、下記2つのDLLであるらしい。

  • ssleay32.dll
  • libeay32.dll

解決

結論としては、ssleay32.dllとlibeay32.dll以外で必要なDLL(zlib.dll、wldap32.dll)が読み込めなかったのが原因だった。
よって、この辺からzlib.dllをダウンロードしてC:\Windows\System32に放り込んだら問題は解決した。


試したこと

キーとなりそうな上記2つのDLLを探しだし、PATH設定で参照しないようにいらないものをアンインストールしてみたり、ApacheやらPHPやらを再インストールしてみた・・・けど解決せず。


そもそもcURLって

http, https, ftp, gopher, telnet, dict, file, ldap プロトコルをサポートしているクライアントライブラリである。