- 追加された行はこの色です。
- 削除された行はこの色です。
* #showrss の不具合?? [#a8595d9f]
#author("2017-10-21T14:37:13+09:00","","")
* #showrss の文字コード判定が正しくない、他 [#a8595d9f]
- ページ: [[BugTrack2]]
- 投稿者: [[matsuo2]]
- ページ: [[BugTrack]]
- 投稿者: [[matsuo]]
- 優先順位: 普通
- 状態: 着手
- 状態: 完了
- カテゴリー: プラグイン
- 投稿日: 2006-04-29 (土) 21:03:31
- バージョン:
- バージョン: 1.4.7
- リリース予定バージョン: 1.5.2
#contents
** メッセージ [#s9666085]
[[official:質問箱3/378]]
天気予報を表示しようとしたのですが
◆天気 愛知 西部(名古屋)
#showrss(http://weather.goo.ne.jp/area/5110.rdf,,0,1)
#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)
#showrss(http://weather.goo.ne.jp/area/5120.rdf,,0,1)
↑こちらはうまく表示される。
他のrssリーダではうまく表示されていたので#showrssに原因があるのではないかと・・・
***回答 [#f75079e5]
- mb_detect_encoding() が EUCのところをUTF-8と誤検出するのが原因でした。判定部分に手を入れてみました。 -- [[matsuo2]] &new{2006-04-16 (日) 19:43:15};
--- 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'));
- なるほど、rss自身が申告しているエンコーディングがあるならばそれを尊重するべき、という事ですね :) -- [[henoheno]] &new{2006-04-30 (日) 23:02:10};
- ふむ、例示されたshowrssの例は、引数の判定を厳密にするとエラーになる気がします(というかそうした作業を既に始めていて、実際にそうなっています) -- [[henoheno]] &new{2006-05-02 (火) 11:54:55};
こうすると
#showrss(http://weather.goo.ne.jp/area/5110.rdf,0,1)
こうなる
#showrss: Template not found: 0
-- 恐らくはテンプレートの部分 (default等) を省略される意図で書かれているのに、showrssの実装は引数の省略を許さないつくりである(二番目の引数は必ずテンプレートの名称でなければならない)ため、評価がずれているのでしょう。 -- [[henoheno]] &new{2006-05-02 (火) 11:58:37};
本来の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.inc.php (1.18以前)にて上記天気予報 2例共、正常に表示されます。
#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]] &new{2006-05-02 (火) 21:19:48};
-- 第2引数を書くか、カンマで区切れば良いのですね。引数の省略は不可ってことか。 -- [[hirokasa]] &new{2006-05-03 (水) 09:03:53};
#showrss(http://weather.goo.ne.jp/area/5110.rdf,,0,1)
-- [[hirokasa]]さん、CVS版のコードの確認ありがとうございます。showrssは以前から引数の省略を許していない様に見えます。私の勘違いでなければ、default|menubar|referという第二引数を省いた場合、プラグインに引数が意図通りに渡っていないかもしれません。簡便な記法としては、カンマで区切る(空文字列)のが良い様です。 -- [[henoheno]] &new{2006-05-05 (金) 12:28:53};
-- 第二引数を省いた場合、default表示の「Last-Modified:2006/05/05 ・・・」が表示されない状態でrss-htmlが表示されます。 &br; ・・・これはうちの環境((turbolinu10server php4.3.8 , turbolinu10server_x86_64 php4.3.9))だけの症状ようですか・・・ラッキーと言うかなんと言うか。第二引数を忘れずにですね。 -- [[hirokasa]] &new{2006-05-05 (金) 13:12:43};
--- showrss.inc.php (1.18 以前) では、テンプレートが存在しなければdefault のテンプレートを適用して表示していました。それに対してshowrss.inc.php (1.19 以降) では、テンプレートが存在しなければエラーが返るようになっているので、明示的に'default' または'' (空文字)を指定する必要があります。 -- &new{2009-10-18 (日) 17:48:43};
--- commit:55f96efe2a (CVS: showrss.inc.php 1.18→1.19) で変更になっていた動作を元に戻しました。テンプレートが見つからない場合はデフォルトテンプレートを使います commit:ba85be1fa7 -- [[umorigu]] &new{2017-10-21 (土) 14:34:29};
- 上記パッチ(を少々直して)cvsに投入しました。変更点としては、split()は恐らく意図通りに動いておらず無くても変らないので外し、正規表現で意図を語るようにし、正規表現で意図しない文字列を拾わない様にしました(".+"で拾うスタイルは危ないので)。結果として、上にある例も(引数は直しましたが)表示される様になっています。 -- [[henoheno]] &new{2006-05-05 (金) 13:32:56};
-- [[cvs:plugin/showrss.inc.php]] (r1.18-r1.21)
- 「Last-Modified:1970/01/01 18:00:00」と表示されますが・・・ -- &new{2006-05-11 (木) 17:00:21};
-- 前後の状況を詳しく -- &new{2006-05-11 (木) 23:39:02};
-- あ、上の例か。 -- &new{2006-05-11 (木) 23:39:19};
-- plugin_showrss_convertのget_date('Y/m/d H:i:s', $time)となっている部分は、get_date('Y/m/d H:i:s', NULL) としないと、$time = ''が渡されて、おかしくならないのかな? -- [[Yuu]] &new{2006-06-13 (火) 21:51:01};
-- 1.4.7ではshowrss.inc.php(65)にて
$time = '';
としちゃだめだと思います。
ここでいう$timeとは plugin_showrss_get_rssから返された$time。~
get_date('Y/m/d H:i:s', NULL) だと「現時点の時間」になってしまうのでキャッシュされた時間にならないような。 -- [[拓郎]] &new{2006-07-02 (日) 02:38:19};
--- $time = ''; でget_date('Y/m/d H:i:s', $time); ですから、第2引数には'' (空文字)が渡されるはずですが・・・。 -- &new{2009-10-18 (日) 17:48:43};
//#comment
**r1.21 で気がついたことをいくつか (2009-10-18 (日) 17:48:43) [#i070954e]
特に説明もなく上の回答のところのパッチに書かれているのでスルーされている部分など、現状の問題点をいくつか挙げます。
簡易で書いた差分は、[[cvs:plugin/showrss.inc.php]] (r1.21) 基準です。
***plugin_showrss_get_rss() の返り値について [#l5a95492]
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 エラーが出るような事はなくなります。
表示されるエラーメッセージが妥当かは別の話で
- この対応を追加しました ( commit:a04c9be0cc ) -- [[umorigu]] &new{2017-10-21 (土) 13:49:42};
***クリーンアップで発生したバグ(Show-timestamp オプション関連) [#p575b00f]
[[showrss.inc.php>cvs:plugin/showrss.inc.php?view=diff&r1=1.18&r2=1.19]] (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>';
}
- 表示用の変数を $time_display に変更しました ( commit:a04c9be0cc ) -- [[umorigu]] &new{2017-10-21 (土) 13:50:42};
***%%わざと%% 間違ってバイナリファイルを指定した場合などに発生するエラー [#ced1ae23]
一応、どんな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 やバイナリファイルを表示となるとさすがに・・・、といった感じです。
- 先頭の500文字のみ表示するようにしました ( commit:a04c9be0cc ) -- [[umorigu]] &new{2017-10-21 (土) 13:51:53};
***クリーンアップで発生したバグ(plugin_showrss_get_timestamp() 内) [#mea26d66]
1970-01-01T18:00:00 のようにマッチの結果$matches[3] がセットされない場合に、PHP エラーが出ます。(r1.18 以前は、empty() を使って判別していました)
ついでに、PHP 5.1.0 以降の[[strtotime()>PHP関数: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;
}
- この対応(isset)を追加しました。 strtotime() は[[BugTrack/2414]]の修正で直っていました ( commit:a04c9be0cc ) -- [[umorigu]] &new{2017-10-21 (土) 13:54:33};
***キャッシュを使っている場合でも、feed を読み取るときに使う基準時間が現在の時刻になっている [#v7307944]
$time = strtotime($str);
return ($time == -1) ? UTIME : $time - LOCALZONE;
のように読み取りに失敗した時に現在の時刻(UTIME)を使っていますが、キャッシュを使っている場合、本来はキャッシュした時間を使うのではないでしょうか?(キャッシュした時点で未来まで見ていたのかよ、とツッコミたくなります)
- キャッシュ利用時にはファイルの変更日時を使うようにしました ( commit:a04c9be0cc ) -- [[umorigu]] &new{2017-10-21 (土) 13:55:53};
***[[Atom]] feed を読み込めない [#p0c8799f]
show''rss'' プラグインなので、場違いな気もしますが、[[official:質問箱4/435]] のような質問もあるので一応。
(2009-10-20 (火) 00:08:18 に、ソースの書き忘れ部分を加筆。これが無いと、<link href="URL" /> のhref="URL" 部分を読み込めない・・・)
// Tag start
function start_element($parser, $name, $attrs)
{
- if ($this->is_item) {
+ if ($this->is_item !== FALSE) {
$this->tag = $name;
+ if ($this->is_item == 'ENTRY' && $name == 'LINK' && isset($attrs['HREF'])) {
+ if (! isset($this->item[$name])) $this->item[$name] = '';
+ $this->item[$name] .= $attrs['HREF'];
+ }
} else if ($name == 'ITEM') {
- $this->is_item = TRUE;
+ $this->is_item = 'ITEM';
+ } else if ($name == 'ENTRY') {
+ $this->is_item = 'ENTRY';
}
}
// Tag end
function end_element($parser, $name)
{
- if (! $this->is_item || $name != 'ITEM') return;
+ if ($this->is_item === FALSE || $name != $this->is_item) return;
$item = array_map(array(& $this, 'escape'), $this->item);
$this->item = array();
if (isset($item['DC:DATE'])) {
$time = plugin_showrss_get_timestamp($item['DC:DATE']);
} else if (isset($item['PUBDATE'])) {
$time = plugin_showrss_get_timestamp($item['PUBDATE']);
+ } else if (isset($item['UPDATED'])) {
+ $time = plugin_showrss_get_timestamp($item['UPDATED'], $this->time);
+
} else if (isset($item['DESCRIPTION']) &&
($description = trim($item['DESCRIPTION'])) != '' &&
- ($time = strtotime($description)) != -1) {
+ ($time = strtotime($description)) != -1 && $time !== FALSE) {
$time -= LOCALZONE;
} else {
$time = time() - LOCALZONE;
}
$item['_TIMESTAMP'] = $time;
$date = get_date('Y-m-d', $item['_TIMESTAMP']);
$this->items[$date][] = $item;
$this->is_item = FALSE;
}
function character_data($parser, $data)
{
- if (! $this->is_item) return;
+ if ($this->is_item === FALSE) return;
if (! isset($this->item[$this->tag])) $this->item[$this->tag] = '';
$this->item[$this->tag] .= $data;
}
plugin_showrss_get_timestamp(''); だとUTIME を返してくるので
$str = '';
foreach(array('DC:DATE', 'PUBDATE', 'UPDATED', 'DESCRIPTION') as $key) {
if (isset($item[$key]) && $item[$key] != '') {
$str = $item[$key];
break;
}
}
$time = plugin_showrss_get_timestamp($str, $this->time);
でもいいかなとも思いましたが、とりあえず放置。(今は、'DESCRIPTION' だけtrim() してから比較してますし)
strtotime() のところはとりあえず変更。
(内容を [[BugTrack/2444]] に移動しました)
***キャッシュを有効にした場合、必ずファイルチェックが入る [#m598886b]
現状では、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);
- キャッシュ利用対象のファイルのみ、有効期限チェック(古ければ削除)をするようにしました。また、不要なキャッシュが残ってしまうのを防ぐため、5%の確率で以前の全削除ロジックも走るようにしています ( commit:a04c9be0cc ) -- [[umorigu]] &new{2017-10-21 (土) 13:57:44};
***XML 宣言でUTF-8 以外のエンコーディングを宣言した場合に、パース失敗になる [#i6f2e5db]
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);
}
- この対応を入れました ( commit:a04c9be0cc ) -- [[umorigu]] &new{2017-10-21 (土) 13:58:23};
----
- 『r1.21 で気がついたことをいくつか (2009-10-18 (日) 17:48:43)』で挙げられている問題をすべて修正しました commit:a04c9be0cc -- [[umorigu]] &new{2017-10-21 (土) 13:49:04};
- タイトルを変更しました: 「#showrss の不具合??」→「#showrss の文字コード判定が正しくない、他」 -- [[umorigu]] &new{2017-10-21 (土) 14:07:46};
#comment