curl(curl_multi) で PEAR_Error 対処

エラー

No unserialized data available. Use XML_Unserializer::unserialize() first.

curl_multiでRSSを取得していると、たまにこんなエラーが出る。

対策

以下2つの対策をとった。(2011/5/6 もうひとつ追記)

backtrace 省略

エラーになったときに膨大な backtrace が返却される。
その膨大さが何か影響してFatalで落としたくないし、JS側へ返却するのに膨大な不要データはいらない。
そこでbacktraceを省略させる。
@see http://blog.koshigoe.jp/mt-search.cgi?IncludeBlogs=3&tag=pear&limit=20

require_once 'PEAR.php';
$isSkiptrace = &PEAR::getStaticProperty('PEAR_Error', 'skiptrace');
$isSkiptrace = true;
レスポンスデータの文字コード強制変換

想定外の文字コードとしてXMLのパースが落ちているようなので、強制的にUTF-8とするようにした。
@see http://moviesearch1.blog15.fc2.com/blog-entry-7.html

/*
 * $dはURLとかが入っているものとする
 */

// result data
$result = array(); 

// array of curl_multi handles
$ha = array();
// curl_multi handle
$mh = curl_multi_init();

//
// set
//
for ($i=0,$l=count($d); $i<$l; $i++) {

    $p = $d[$i];
    // initialize
    $ha[$p['name']] = curl_init($p['url']);

    // set options
    curl_setopt($ha[$p['name']], CURLOPT_HEADER,         0);
    curl_setopt($ha[$p['name']], CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ha[$p['name']], CURLOPT_TIMEOUT,        1);    // timeout(sec)

    // add
    curl_multi_add_handle($mh, $ha[$p['name']]);

}

//
// execute
//
$running = null;
do {
    curl_multi_exec($mh, $running);
} while($running);

// get content and remove handles
foreach($ha as $name => $c) {

    // confirm status
    $status = curl_getinfo($ha[$name]);

    // expected status code of 2xx
    if ( substr($status['http_code'], 0, 1) == '2' ) {

        $Unserializer = new XML_Unserializer();
        $Unserializer->setOption('parseAttributes',TRUE);

        $xml = curl_multi_getcontent($c);
        // ここで強制的に文字コードを変換
        $xml = mb_convert_encoding($xml, "UTF-8", "auto");

        if ( $status = $Unserializer->unserialize( $xml ) ) {
            $result[$name] = $Unserializer->getUnserializedData();
        }

        curl_multi_remove_handle($mh, $c);

    }

}

// all done
curl_multi_close($mh);
2011/5/6 追記 データサイズと実行時間について

海外のホスティングでプログラムを実行した際に同様のエラーが発生した。
問題を確認すると、どうもcurlで取得しているデータが不完全な状態で終わっている。
原因としてはcurl_multiのオプション指定で、許容実行時間内でデータの取得が終わらなかったことによるものだった。

// curlの実行時間を1秒する
//curl_setopt($ha[$p['name']], CURLOPT_TIMEOUT, 1);

// 上記では取得途中で切断するものがあったため3秒に伸ばす
curl_setopt($ha[$p['name']], CURLOPT_TIMEOUT, 3);

エラーが無くなったきがする。