Twitter APIには、コマンドを投げたら、応答が一回帰ってくるだけのREST APIと、一度接続するとダラダラとデータが流れてくる Stream API があります。
REST API は専用ライブラリーがあって、そんなに考える必要はないんですが、Stream API は TCP ちうか、SSL セッションをつないでソケット読み出しをハンドルしてあげないといけない。
とまぁ、ここまでは紹介してあるページがあったりするわけですが、実際にこれを動かしてみたところ、たまにツィートを取りもらすのですよ。別のStream 対応 Twitterクライアントで Home を垂れ流しつつ、スクリプトの結果を流してみると、たまに欠損するわけです。
なので、変数ダンプだなんだして調べてみたところ、fgets() を使って変数に読み込むデータが途中で途切れてることが発覚しました。サーバは一塊のデータとしてデータを送ってるんですが、TCPストリームにはデータの区切りという概念がないので、受信側で勝手にデータを途切れさせて、fgets() が中途半端な位置で返ってしまうんですね。これは C で recv() した時にも起こります。
ちうわけで、読み込んだデータが、途中で途切れてないかを調べて、途切れてたらつなぎ合わせるという処理が必要になります。あと、なんでか分からんのですが、一個のツィートデータが途切れてしまと、続きのデータが来る前に「あと何バイト残ってるよ」という数字だけが書かれた情報が送られてくる。これは読み飛ばさないと、データとして壊れちゃうんですね。
そんなわけで、つなぎ合わせ処理を入れたスクリプトはこんな感じ
require_once('config.php');
$url = 'https://userstream.twitter.com/1.1/user.json';
#$url = 'https://userstream.twitter.com/1.1/statuses/firehose.json';
$method = 'GET';
$oauth_parameters = array(
'oauth_consumer_key' => CONSUMER_KEY ,
'oauth_nonce' => microtime(),
'oauth_signature_method' => 'HMAC-SHA1',
'oauth_timestamp' => time(),
'oauth_token' => ACCESS_TOKEN ,
'oauth_version' => '1.0',
);
$a = $oauth_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(ACCESS_TOKEN_SECRET)));
$oauth_parameters['oauth_signature'] = base64_encode(hash_hmac('sha1', $base_string, $key, true));
$fp = fsockopen("ssl://userstream.twitter.com", 443);
if ($fp) {
fwrite($fp, "GET " . $url . " HTTP/1.1\r\n"
. "Host: userstream.twitter.com\r\n"
. 'Authorization: OAuth ' . http_build_query($oauth_parameters, '', ',', PHP_QUERY_RFC3986) . "\r\n"
. "\r\n");
while (!feof($fp)) {
$read = fgets($fp);
if(strlen($read) > 5)
{
if(preg_match('/^\{/' , $read))
$line = $read;
else
$line .= $read;
}
$line = preg_replace('/[\s\n]+$/' , '' , $line);
if(preg_match('/\}$/' , $line))
{
$res = json_decode($line, true);
if($res["id_str"])
{
echo $res["id_str"] . ":";
echo $res["user"]["screen_name"] . ":";
echo $res["text"] . "\n";
}
$line = "";
}
}
fclose($fp);
}

コメントする