Tracker_list プラグインの無駄な処理†
- ページ: BugTrack2
- 投稿者: 名無しさん
- 優先順位: 低
- 状態: 完了
- カテゴリー: プラグイン
- 投稿日: 2007-05-30 (水) 01:32:59
- バージョン:
[fixed] fixed heading anchor を削除する処理が過剰 (二回目は不要のはず)†
次の部分の処理が無駄だと思うのですが、いかがでしょう。
// $Id: tracker.inc.php,v 1.36 2007/03/29 15:05:52 henoheno Exp $
(中略)
class Tracker_list
{
(中略)
function add($page,$name)
{
(中略)
$source = plugin_tracker_get_source($page);
if (preg_match('/move\sto\s(.+)/',$source[0],$matches))
{
(中略)
return $this->add($page,$name);
}
$source = join('',preg_replace('/^(\*{1,3}.*)\[#[A-Za-z][\w-]+\](.*)$/','$1$2',$source)); //無駄だと思う部分
(中略)
function plugin_tracker_get_source($page)
{
$source = get_source($page);
// 見出しの固有ID部を削除
$source = preg_replace('/^(\*{1,3}.*)\[#[A-Za-z][\w-]+\](.*)$/m','$1$2',$source);
// #freezeを削除
return preg_replace('/^#freeze\s*$/im', '', $source);
}
plugin_tracker_get_source の途中、preg_replace でアンカー名を消す置き換えをしているのに、
plugin_tracker_get_source で呼び出したソースに、もう一度アンカー名を消す置き換えをしようとしています。
この部分は、
- $source = join('',preg_replace('/^(\*{1,3}.*)\[#[A-Za-z][\w-]+\](.*)$/','$1$2',$source));
+ $source = join('',$source);
と修正したほうが、よいと思います。
- コメントありがとうございます。この件、正規表現も完全一致のようですね。私も特に違和感を感じませんでした。 -- henoheno
- 確認しました。この部分は完了ですね。 --
[fixed] unset() すべきリソース†
- BugTrack2/159 を見たので、これも修正候補という事で追加。
// $Id: tracker.inc.php,v 1.36 2007/03/29 15:05:52 henoheno Exp $
// Copyright (C) 2003-2005 PukiWiki Developers Team
(中略)
class Tracker_list
{
(中略)
function add($page,$name)
{
(中略)
if ($this->rows[$name]['_match'] = preg_match("/{$this->pattern}/s",$source,$matches))
{
+ unset($source);
array_shift($matches);
foreach ($this->pattern_fields as $key=>$field)
{
$this->rows[$name][$field] = trim($matches[$key]);
}
}
}
(中略)
function toString($limit=NULL)
{
(中略)
foreach ($this->rows as $key=>$row)
{
if (!TRACKER_LIST_SHOW_ERROR_PAGE and !$row['_match'])
{
+ unset($this->rows[$key]);
continue;
}
$this->items = $row;
foreach ($body as $line)
{
if (trim($line) == '')
{
$source .= $line;
continue;
}
$this->pipe = ($line{0} == '|' or $line{0} == ':');
$source .= preg_replace_callback('/\[([^\[\]]+)\]/',array(&$this,'replace_item'),$line);
}
+ unset($this->rows[$key]);
}
return convert_html($source);
}
1つ目のTracker_list->add() でのunset は、「1ページ分のソース×2」を解消するためなんですが、これはページのデータ量しだいで効果が変わってしまいそうです*1。
2つ目のTracker_list->toString() でのunset は、「リストの各項目に置き換えたページのデータを順次消していく」です。これも総ページ数が少ないうちはあまり効果がないかもしれません*2。
って効果を実証できてない。テストを準備して~の時点でつまづいてるせいか。 --
- 一つ目の unset() の意図はわかりました。問題ないかと思います。二つ目と三つ目のunset() は $this-> rows を少しでも減らそうとしていますね。気持ちは解るのですが、これをやった場合、オブジェクトの toString() を二回以上呼・・・ぶ事なんてまず無いと思いますが、呼んだ時の挙動が不定になってしまいます。ちょっと破壊的に見えて怖いです。なおどのみちこのオブジェクトを作成した関数は toString() を呼んだ後すぐにオブジェクトを丸ごと開放するでしょう。 -- henoheno
- 確認しました。2つ目はreturn でtoString() を呼んでいるので、しなくてもいいかなとも思いつつ書いていたので、かまいませんよ。 --
[fixed] 不要なフィールドをテーブルに抱え込んでいる†
- そもそも、予約語やテンプレート名のページ*3で設定したもの以外の項目を、$this->rows に放り込む必要があるのかな?という修正案。
// $Id: tracker.inc.php,v 1.36 2007/03/29 15:05:52 henoheno Exp $
(中略)
class Tracker_list
{
(中略)
function Tracker_list($page,$refer,&$config,$list)
{
(中略)
while (count($pattern))
{
$this->pattern .= preg_replace('/\s+/','\\s*','(?>\\s*'.trim(array_shift($pattern)).'\\s*)');
if (count($pattern))
{
$field = array_shift($pattern);
$this->pattern_fields[] = $field;
$this->pattern .= '(.*)';
}
}
+ $this->pattern_fields = array_intersect($this->pattern_fields, array_keys($this->fields));
// ページの列挙と取り込み
$this->rows = array();
少しでもデータが減った分、後の処理が軽くならないかな~。 --
- この件は雰囲気は伝わったものの、(私がTracker_list を理解し切れていないこともあり) 実装の意味が解りませんでした。array_intersect() でいいのかどうかとか、説明では $this->rows とあるのにコードでは $this->pattern_fields であったりする点など。もし何かあれば追記願います。 -- henoheno
- 上の部分で、$this->pattern に各リスト対象ページを項目ごとに分割する正規表現を、$this->pattern_fields に$this->pattern の正規表現のサブパターンリストを収得しています。
Tracker_list->add() の最後で、preg_match('/' . $this->pattern . '/s',implode('', $source),$matches) を使って得た$matches から、
各項目名の配列に収納するために、foreach ($this->pattern_fields as $key=>$field) を使っています。
例として、デフォルトの:config/plugin/tracker/default/page から得られるのは、
$this->pattern = '(?>\s*\*\s*)(.*)(?>\s*-ページ\:\s*)(.*)(?>\s*-投稿者\:\s*)(.*)(?>\s*-優先順位\:\s*)(.*)'.
'(?>\s*-状態\:\s*)(.*)(?>\s*-カテゴリー\:\s*)(.*)(?>\s*-投稿日\:\s*)(.*)(?>\s*-バージョン\:\s*)(.*)'.
'(?>\s*\*\*\s*メッセージ\s*)(.*)(?>\s*----\s*)(.*)(?>\s*\s*)'
$this->pattern_fields = Array ( [0] => Summary [1] => _refer [2] => Proposer [3] => Severity [4] => Status
[5] => Category [6] => _date [7] => Version [8] => Messages
[9] => _block_comment )
見にくくなるので適当に加工してます。
です。
$this->pattern_fields = array_intersect($this->pattern_fields, array_keys($this->fields) の部分は、
「$this->pattern_fields の各値の中で、予約語など($this->fields の各キー名)に存在しないものがあれば、その値が入っているキーをunset した配列を、$this->pattern_fields に代入しなおす」
という処理です。
入力ミスがなければ、該当するのはブロック型プラグイン(_block_プラグイン名)のみとなります。
上の例で言えば、水平線以降のコメント部分が該当します。
上の例だと、実際に$this->rows に入るのは各ページ10項目ですが、
_block_comment をリストの表示につかわない*4であろう、ということと、
同じプラグインが複数回使われたとしても、今の置き換えルールではその判別ができない(同じフィールド名になってしまう)という理由から、
設定されていない項目のデータを得なくてもかまわないだろう、という判断でこの修正案を挙げました。 --
- :confing/tracker/default の場合、 array_intersect() で消えるのは [_block_comment] (#comment) 。同様に、ブロック型プラグインは消えるだろう -- henoheno
- このパッチの主張が「[_block_XXX] についての $this->pattern_fields は不要」であると仮定するとして -- henoheno
- そうならば array_intersect() を後からかけるより、whileの内部で isset() を使い、そもそも $this->pattern_fields に無駄な項目を追加しない様にするのが良さそうかな・・・ -- henoheno
- Tracker_list::add() を見てようやく理解できました。確かにblock pluginの名前は、tracker_list が抱えるテーブルのセルとして保持する必要がありません。 -- henoheno
- cvs:plugin/tracker.inc.php (r1.49) -- henoheno
- 元の提案のままだと block plugin などをかわすための仮想フィールド(pseudo fields とでも呼びましょう)を capture するように $pattern で指定したままだったので、block plugin の後ろにフィールドを配置した場合に、それ以降のデータがずれて格納される恐れがある様でしたので、pseudo fields をそもそもcaptureしない様に修正しました。これで、勘違いでもしていなければメモリのオーバーヘッドが減るはずです。 -- henoheno
- 動作の確認しました。この部分の変更については特に問題もなく、きちんと動作していました。offical のようにTracker_list プラグインをたくさん使っているサイトは、メモリの負荷対策にもなってしまいそうですよね。 --
[rejected] その他: $this->order†
[fixed] その他: 表示上限をマイナスで指定した時†
- 表示上限をマイナスで指定した時にメッセージがおかしくなります。
例
全50件中、上位-20件を表示しています。
array_splice()は負の値にも対応しているので、リストには、(50 - 20) つまり30件が表示される
表示上限としているので、負の値とした時に無効とするか、メッセージの側を対応させる必要があると思います。
下は該当箇所周辺です。
(中略)
class Tracker_list
{
(中略)
function toString($limit=NULL)
{
global $_tracker_messages;
$source = array();
$body = array();
$count = count($this->rows);
if ($limit !== NULL && $count > $limit)
{
$source[] = str_replace(
array('$1', '$2'),
array($count,$limit),
$_tracker_messages['msg_limit']) . "\n";
$this->rows = array_splice($this->rows,0,$limit);
}
if (empty($this->rows))
{
return '';
}
(中略)
foreach ($this->rows as $row)
{
if (! TRACKER_LIST_SHOW_ERROR_PAGE && ! $row['_match'])
{
continue;
}
(以降、略)
表示上限が0 のときは、if (empty($this->rows)) が拾ってくれるので大丈夫だと思います。
それとこの部分に関連して、「項目の取り出しに失敗したページを表示しない」としている時、
まずこの部分で、ソートした後の$this->rows を表示上限まで減らしてから、取り出しの成否を調べているので、
全50件中、上位20件を表示しています。
となっていても、実際にはリストが20件でない可能性があります。 --
- #tracker_list の引数「表示上限」にマイナスを入れると、「総数 - 指定した数」が表示された上で、メッセージが変になる様ですが、これは以前からあった症状みたいですね。 -- henoheno
- とりあえず現状、0とすると制限なしとして動作、0未満の値を入れると 1 と解釈して動作する様に直すまではやりました。 -- henoheno
その他: ヘルプの記載と実際の動作の違いについて†
- ページのタイトルとは少し外れますが、最近ここを中心にCleanup 、その他をしているようなのでここに書き込みます*6。
tracker_list のヘルプには昇順固定、昇順(閲覧者が変更可)、降順固定、降順(閲覧者が変更可)のように、
ヘッダ行やフッタ行に表示されるリンクの表示を制御できるかのように書いてありますが、
今のバージョンだと例えば昇順固定にしていてもその項目に、降順に変更するためのリンクが表示されます。
どちらにあわせて修正する方がいいのかがわからないのでとりあえず挙げます。 --
- 追記ありがとうございます。編集認証の話とか色々あって、もうやらざるをえないかな、という事で一人合宿中です。 -- henoheno
- 「昇順固定」「降順固定」という脚注は見逃していました。ASCは単にSORT_ASCの短縮形だろうと思ってスルーしていました。今のコードに「固定」に関する部分はあったかしら? -- henoheno
- Tracker_list->sort() でASCをSORT_ASCにといった様に置き換えてソート、置き換えたオプションを基にTracker_list->replace_title() でリンクを付けているので、今のコードにはないのではと。フラグもなさそうですし。
とはいえこの機能が正常に機能していても、plugin_tracker_list_action() を呼んだ後に、アドレスを書き換えれば回避できる程度の機能のはずですけど。 --
- 今の Tracker_list::sort() は ASC と SORT_ASC を完全に同一視していて、いずれも array_multisort() の引数として有効な定数 "SORT_ASC" としてしか保存していません。固定なのか固定でないのかすら保存していないので、入れ物を作らねばならないでしょう。また、sort()のTODO:に書いた通り、なぜかsort条件の判定がstringで統一されていない(stringの世界であるのに、定数(=int)を許さないと一部まともに動かない)ので、それを直してからでないと着手できないでしょう。plugin_tracker_list_action() の部分は御意の通り、全く問題にならないでしょう。 -- henoheno
- tracker_list 周りのこの辺りの下地は大体直しましたが、周辺の状況がどうも腑に落ちません。誤解しているかもしれませんが、「ユーザーの操作を受け付けない(強制できる)」状態を仮にfix(fixed)と呼びましょう。(1)この状況を例えばFIXED_ASC、FIXED_DESCといった名称で示すならまだ解るのですが、ASC と SORT_ASC という「略語と正式名称」を想起させる関係にあてはめるというのは直感的でない(奥が深い)デザインであるように思います。(2)固定したい時って実際にはどんな時なのでしょう。ユーザーがURLをちょっといじれば解除できますが、それでもいいものなのでしょうか。よろしかったら身近な例で構いませんので教えて下さい。 -- henoheno
- ええと、あまりいい答えじゃない気がしますが。
私がこの機能について疑問を抱いたのは、ぱんだ:tracker.inc.phpに設置されているTracker_list を見たからです。このページのリストにはリンクがついていなかったので、こんな事もできるんだと思ってヘルプを見て試してみたら、実際にはできなっかたのでこうして提示したのです。
どの機能が先で、どれが後から追加されたかがあまりわかっていないので、(1)のソートタイプ名(の由来やその他)についてはお役に立てそうもありません。
それで(2)の質問なのですが、今すぐぱっと思いつくのはBugTrack_list プラグインと同じような感じにするということしか、固定するニーズを思いつきませんでした。ヘッダやフッタは表示したいが、表示順を安易に変えたくないとか、リンクが無いほうがBugTrack_list っぽく見えるとか。でもそれだと項目ごとじゃなく、全項目一括でどうするかの方が楽な気がします。この例だと裏技(URL をいじる)をありとするかどうかは、設置者しだいということになります。
参考になるか微妙なんですが、ひとまずこんなところで。もっといい例を思いつかねば。 --
- 便利さを読み取れていないという点で同じ立場みたいですね (^^; テーブルのheaderにリンクをつけないだけなら、多分 :config/tracker/XXX/list をいじれば*7できます。 -- henoheno
- 別の場所でも触れましたが、ASC, DESC ともに、 SORT_ASC, SORT_DESC の短縮形として再定義した上、短いほう(の、小文字)をデフォルトにしました。 -- henoheno
- (少し話題からずれますが)tracker 系のヘルプの修正量がすごいことになりそうですね、定数の増加やその他で。まだ、仕様が固まっていないので手は出しませんが。 --
- バージョンつながりから今気づいたので、ついでにメモ。2007年に更新したcvs:lib/init.php に記載のフッタに表示されるCopyright 表記。1.4.7_1_alpha と1.4.8_alphaはいまだに、「2001-2006 PukiWiki Developers Team」となっていますね。ぱっと見た目、最新の物が入っているのかが判らないんで、2001-2007 に直したほうがいいと思うんですけど(って今年の残りがあと3ヶ月になってから言っても意味無いかな)。 --
改行の扱い (tracker_listにおいて、表が崩れたり、意図しないWiki textが生成される原因)†
コメント†
- tracker, tracker_list 両プラグインから呼び出されるものの、
cvs:plugin/tracker.inc.php (v 1.38) には特にコメントがないclass Tracker_field の内容について、
蛇足な気はしますが、簡単に役割の説明を。
- Tracker_field->Tracker_field()
- plugin_tracker_get_fields() で、クラスのインスタンスを生成する時に、呼び出される。
- Tracker_field->get_tag()
- plugin_tracker_convert() が、入力フォームを形成するために、呼び出す。
- Tracker_field->get_style()
- Tracker_list->replace_item() が、リストの各項目に応じてテンプレートで設定したスタイルを適用するために、呼び出す。
例えば、defaultの[Status]で、提案のスタイルを適用すると、BGCOLOR(#ffccff):データとなる。
単純に%s を各データに置き換えるだけなので、
表形式以外でこの機能を使うには、「&color(,#ffccff){%s};」のように設定する必要がある。
- Tracker_field->format_value()
- plugin_tracker_action() が、フォームから送信されたデータをページに書き込むデータに変換するために、呼び出す。
- Tracker_field->format_cell()
- Tracker_list->replace_item() が、ページに記録されたデータをリストに表示する形式に変換するために、呼び出す。
例えば、textarea のデータを表形式に対応させるため改行を削除する(表形式以外でもこの処理は固定ですけど)
例えば、[_update]等の日時データを人が読める形式にする
など...
- Tracker_field->get_value()
- Tracker_list->sort() が、ソート専用の値を得るために、呼び出す。
例えば、defaultの[Status]は、提案は0番、着手は1番、...、却下は5番、これ以外は値そのまま、を数値的比較でソートする。
ただ、その他の値は、意図した順番(最初か最後)にならない可能性があります。
- 今ソースに記載されている順と、実際の呼び出し順が違ったりしますがこんなところです。 --
test
TEST†
*test&br;TEST
- test
TEST
- test
TEST
:test&br;TEST|test&br;TEST
|test&br;TEST|test&br;TEST|
,test&br;TEST,test&br;TEST
-test&br;TEST
test
TEST
&size(22){test&br;TEST};