天気予報を表示しようとしたのですが
◆天気 愛知 西部(名古屋)
#showrss(http://weather.goo.ne.jp/area/5110.rdf,,0,1)
↑表示されない。(06/04/06)
◆天気 愛知 東部(豊橋)
#showrss(http://weather.goo.ne.jp/area/5120.rdf,,0,1)
↑こちらはうまく表示される。
他のrssリーダではうまく表示されていたので#showrssに原因があるのではないかと・・・
--- showrss.inc.php.org +++ showrss.inc.php @@ -52,9 +51,13 @@ if (! class_exists($class)) $class = 'ShowRSS_html'; list($rss, $time) = plugin_showrss_get_rss($rssurl, $usecache); + if ($rss === FALSE) return '<p>showrss: cannot get rss from server.</p>' . "\n"; + if (is_string($rss)) + return "<p>showrss: error ${rss}\n"; + $obj = new $class($rss); $timestamp = ''; @@ -210,12 +213,20 @@ $this->is_item = FALSE; $this->tag = ''; - // Detect encoding - $this->encoding = mb_detect_encoding($buf); + /* Detect encoding ex. <?xml version="1.0" encoding="EUC-JP" ?> */ + $lines = split(">", $buf, 1); + if (preg_match( "/encoding=\"(.+)\"/i", $lines[0], $encoding)) { + $this->encoding = $encoding[1]; + } else { + $this->encoding = mb_detect_encoding($buf); + } + + // Convert to utf-8 if (! in_array(strtolower($this->encoding), array('us-ascii', 'iso-8859-1', 'utf-8'))) { $buf = mb_convert_encoding($buf, 'utf-8', $this->encoding); $this->encoding = 'utf-8'; } + $xml_parser = xml_parser_create($this->encoding); xml_set_element_handler($xml_parser, array(& $this, 'start_element'), array(& $this, 'end_element')); xml_set_character_data_handler($xml_parser, array(& $this, 'character_data'));
こうすると #showrss(http://weather.goo.ne.jp/area/5110.rdf,0,1) こうなる #showrss: Template not found: 0
本来のusageはこのようだ #showrss(URI-to-RSS[,default|menubar|recent[,Cache-lifetime[,Show-timestamp]]]) こうされたいのかな? #showrss(http://weather.goo.ne.jp/area/5110.rdf,default,1) officialサイトを読む例 (templateはmenubar) #showrss(http://pukiwiki.sourceforge.jp/?cmd=rss10,menubar,1,0)
#showrss(http://weather.goo.ne.jp/area/5110.rdf,0,1) #showrss(http://weather.goo.ne.jp/area/5120.rdf,0,1)showrss.inc.php (1.19)ではTemplate not found・・です。 -- hirokasa 2006-05-02 (火) 21:19:48
#showrss(http://weather.goo.ne.jp/area/5110.rdf,,0,1)
$time = '';としちゃだめだと思います。 ここでいう$timeとは plugin_showrss_get_rssから返された$time。
特に説明もなく上の回答のところのパッチに書かれているのでスルーされている部分など、現状の問題点をいくつか挙げます。 簡易で書いた差分は、cvs:plugin/showrss.inc.php (r1.21) 基準です。
list($rss, $time) = plugin_showrss_get_rss($uri, $cachehour);
で$rss に返ってくる値は、FALSE と配列だけではなく、文字列型のエラーメッセージが ShowRSS_XML::parse() から返ってくる場合があります。 そこで、
list($rss, $time) = plugin_showrss_get_rss($uri, $cachehour); if ($rss === FALSE) return '#showrss: Failed fetching RSS from the server<br />' . "\n"; + if (! is_array($rss)) return '#showrss: ' . htmlspecialchars($rss) . '<br />' . "\n"; // Show XML error message
のように追加すれば、とりあえず後ろの作業でforeach に文字列を放り込んでPHP エラーが出るような事はなくなります。 表示されるエラーメッセージが妥当かは別の話で
showrss.inc.php (r1.18 → r1.19) のクリーンアップ時に
$time → $time $timestamp → $time $usetimestamp → $timestamp
としてしまったため、Show-timestamp オプションを有効としたときに表示される時間がUnix タイムスタンプでの0 (表示用に変換される時に、ZONETIME とLOCALZONE の補正がかかりますが・・・)となってしまいます。 上で「Last-Modified:1970/01/01 18:00:00」と(常に)表示される原因となっています。
- list($rss, $time) = plugin_showrss_get_rss($uri, $cachehour); + list($rss, $rss_time) = plugin_showrss_get_rss($uri, $cachehour); if ($rss === FALSE) return '#showrss: Failed fetching RSS from the server<br />' . "\n"; $time = ''; if ($timestamp > 0) { $time = '<p style="font-size:10px; font-weight:bold">Last-Modified:' . - get_date('Y/m/d H:i:s', $time) . '</p>'; + get_date('Y/m/d H:i:s', $rss_time) . '</p>'; }
一応、どんなurl でも指定できるようになっているので、mbstring が扱えない文字エンコーディングをファイルが指定していたり、mb_detect_encoding() がFALSE を返す(バイナリファイルなど、検出に失敗する)場合、変換作業時にPHP エラーが出ます。
// Normalize to UTF-8 / ASCII if (! in_array(strtolower($this->encoding), array('us-ascii', 'iso-8859-1', 'utf-8'))) { - $buf = mb_convert_encoding($buf, 'utf-8', $this->encoding); + $buf = @mb_convert_encoding($buf, 'utf-8', $this->encoding); $this->encoding = 'utf-8'; }
また変換に成功していても、想定外の文字キャラクタをxml_parse() が検出すると、PHP エラーが出てしまいます。(上の差分がエラー無視なので、想定外が送られてくるのは当然なのですが・・・)
// Parsing $xml_parser = xml_parser_create($this->encoding); xml_set_element_handler($xml_parser, array(& $this, 'start_element'), array(& $this, 'end_element')); xml_set_character_data_handler($xml_parser, array(& $this, 'character_data')); - if (! xml_parse($xml_parser, $buf, 1)) { + if (! @xml_parse($xml_parser, $buf, 1)) { return(sprintf('XML error: %s at line %d in %s', xml_error_string(xml_get_error_code($xml_parser)), xml_get_current_line_number($xml_parser), $buf)); } xml_parser_free($xml_parser);
ちなみに、sprintf() で作られるエラーメッセージですが、相手から送られてきた内容($buf)すべてを表示するようになっています。短い文章ならまだいいのでしょうが、HTML やバイナリファイルを表示となるとさすがに・・・、といった感じです。
1970-01-01T18:00:00 のようにマッチの結果$matches[3] がセットされない場合に、PHP エラーが出ます。(r1.18 以前は、empty() を使って判別していました) ついでに、PHP 5.1.0 以降のstrtotime() が失敗した時にFALSE を返すようになった対策を簡易で。(負の値が返ってくるほど古い日付を発信するfeed は無い、と決め付けで)
if (preg_match('/(\d{4}-\d{2}-\d{2})T(\d{2}:\d{2}:\d{2})(([+-])(\d{2}):(\d{2}))?/', $str, $matches)) { $time = strtotime($matches[1] . ' ' . $matches[2]); - if ($time == -1) { + if ($time == -1 || $time === FALSE) { $time = UTIME; - } else if ($matches[3]) { + } else if (isset($matches[3])) { $diff = ($matches[5] * 60 + $matches[6]) * 60; $time += ($matches[4] == '-' ? $diff : -$diff); } return $time; } else { $time = strtotime($str); - return ($time == -1) ? UTIME : $time - LOCALZONE; + return ($time == -1 || $time === FALSE) ? UTIME : $time - LOCALZONE; }
$time = strtotime($str); return ($time == -1) ? UTIME : $time - LOCALZONE;
のように読み取りに失敗した時に現在の時刻(UTIME)を使っていますが、キャッシュを使っている場合、本来はキャッシュした時間を使うのではないでしょうか?(キャッシュした時点で未来まで見ていたのかよ、とツッコミたくなります)
(内容を BugTrack/2444 に移動しました)
現状では、1つのページに設置した複数showrss プラグイン全てで同じCache-lifetime を指定していても、プラグインが呼び出された回数分ファイルチェックが行われます。(キャッシュ先がCACHE_DIR なので、.rel や.ref のファイルの数しだいでは重荷になるかもしれません) なので、未チェックであるか、これまでよりも短い時間を設定された場合だけ、チェックが入るようにしてみる。
// Remove cache if expired limit exeed function plugin_showrss_cache_expire($cachehour) { + static $check = NULL; + + if ($check !== NULL && $check <= $cachehour) return; + $check = $cachehour; + $expire = $cachehour * 60 * 60; // Hour $dh = dir(CACHE_DIR);
2009-10-20 (火) 01:48:07 にこの内容を追加
<?xml version="1.0" encoding="EUC-JP" ?>
のようにUTF-8 以外のエンコーディングを宣言した場合、xml_parse() が失敗してしまいます。(古いPHP ではそこまで細かくチェックしていないようですが) そこで、encoding="エンコード名" の部分を消す事で、この部分が原因でパース失敗とならないようにしてみる。(上の
#showrss(http://weather.goo.ne.jp/area/5110.rdf,,0,1)
の例で、中身が表示されない原因がコレみたいです)
// Detect encoding $matches = array(); if(preg_match('/<\?xml [^>]*\bencoding="([a-z0-9-_]+)"/i', $buf, $matches)) { $this->encoding = $matches[1]; + $buf = preg_replace('/<\?xml ([^>]*)\bencoding="[a-z0-9-_]+"/i', '<?xml $1', $buf); } else { $this->encoding = mb_detect_encoding($buf); }