#showrss の文字コード判定が正しくない、他

メッセージ

official:質問箱3/378

天気予報を表示しようとしたのですが

◆天気 愛知 西部(名古屋)

#showrss(http://weather.goo.ne.jp/area/5110.rdf,,0,1)
#showrss: Failed fetching RSS from the server

↑表示されない。(06/04/06)

◆天気 愛知 東部(豊橋)

#showrss(http://weather.goo.ne.jp/area/5120.rdf,,0,1)
#showrss: Failed fetching RSS from the server

↑こちらはうまく表示される。

他のrssリーダではうまく表示されていたので#showrssに原因があるのではないかと・・・

回答

r1.21 で気がついたことをいくつか (2009-10-18 (日) 17:48:43)

特に説明もなく上の回答のところのパッチに書かれているのでスルーされている部分など、現状の問題点をいくつか挙げます。 簡易で書いた差分は、cvs:plugin/showrss.inc.php (r1.21) 基準です。

plugin_showrss_get_rss() の返り値について

	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 エラーが出るような事はなくなります。 表示されるエラーメッセージが妥当かは別の話で

クリーンアップで発生したバグ(Show-timestamp オプション関連)

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 やバイナリファイルを表示となるとさすがに・・・、といった感じです。

クリーンアップで発生したバグ(plugin_showrss_get_timestamp() 内)

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;
 	}

キャッシュを使っている場合でも、feed を読み取るときに使う基準時間が現在の時刻になっている

 		$time = strtotime($str);
		return ($time == -1) ? UTIME : $time - LOCALZONE;

のように読み取りに失敗した時に現在の時刻(UTIME)を使っていますが、キャッシュを使っている場合、本来はキャッシュした時間を使うのではないでしょうか?(キャッシュした時点で未来まで見ていたのかよ、とツッコミたくなります)

Atom feed を読み込めない

(内容を 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);

XML 宣言でUTF-8 以外のエンコーディングを宣言した場合に、パース失敗になる

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);
 		}



*1 turbolinu10server php4.3.8 , turbolinu10server_x86_64 php4.3.9

トップ   編集 凍結 差分 履歴 添付 複製 名前変更 リロード   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2017-10-21 (土) 14:37:13
Site admin: PukiWiki Development Team

PukiWiki 1.5.4+ © 2001-2022 PukiWiki Development Team. Powered by PHP 8.2.12. HTML convert time: 2.415 sec.

SourceForge