#pcommentでインラインプラグインを書いて閉じ忘れると表示が崩れる

メッセージ

タイトルの通りです。

#pcomment()ではコメントでwiki記法を書いてもそのまま反映がされますが、

&plugin(){~};

とやるところを

&plugin(){~
&plugin(){~}

という閉じられていない記述にすることで、コメントの挿入時に

&plugin(){~ -- 名無し &new{2022/12/24 (土) 08:45:56};
&plugin(){~} -- 名無し &new{2022/12/24 (土) 08:45:56};

となり、システムが自動で追加した文末の&newのセミコロンと合体して表示が崩れます。

ユーザーが入力したテキストがwikiシステムの記述と勝手に結合するのは問題だと思い報告したのですが、仕様らしいです。BugTrack/429
なお、BugTrack/429は「見出しに&a{~を書くと文末の&aname();と結合して表示が乱れる」という報告でした。

(例)

-&color(red){~ -- 名無し &new{2022/12/24 (土) 08:45:56};
-&color(red){~} -- 名無し &new{2022/12/24 (土) 08:45:56};
-&color(red){~}; -- 名無し &new{2022/12/24 (土) 08:45:56};

対処法

うちのwikiでは以下の閉じ忘れ }; 追加関数を追加することで対処することにしました。

/**
 * wikiソースの末尾にインラインプラグイン閉じ忘れの }; を適切な個数だけ追加する
 * @param  string $wiki_source_line wikiソース(1行)
 * @return string 末尾に }; を追加してもプラグインの範囲が延びないようにしたwikiソース
 */
function close_forgotten_brace_semicolon($wiki_source_line) {
	// '&' がなかったらさっさと抜ける
	if (strpos($wiki_source_line, '&') === false) {
		return $wiki_source_line;
	}

	// インラインプラグインのパターン
	// make_link.php Link_plugin->get_pattern() で定義
	$pattern = '/&\w+(?:\((?:(?!\)[;{]).)*\))?(?:\{(?:(?R)|(?!};).)*\})?;/u';

	// 最終的に返す文字列の格納用
	$return_str = $wiki_source_line;

	// プラグイン文字列が変化しなくなるまで }; を末尾に追加する
	$count1 = preg_match_all($pattern, $return_str, $matches1);
	while (true) {
		$count2 = preg_match_all($pattern, $return_str . '};', $matches2);

		// 変化がなければ返す
		if (
			$count1 === $count2
			&& ($count1 === 0 || end($matches1) === end($matches2))
		) {
			return $return_str;
		}

		// 変化があったので追加して継続
		$return_str .= '};';
		$count1 = $count2;
		$matches1 = $matches2;
	}
}

// テスト関数(雑)
function test_close_forgotten_brace_semicolon() {
	$s = [];
	$s[] = 'abcabc';
	$s[] = 'abc&size(12){@@@}abc';
	$s[] = 'abc&size(12){@@@};abc';
	$s[] = 'abc&size(12){@@@};abc&color(red){&aname(red){&ruby(red){@@@};abc';

	// ↑の close_forgotten_brace_semicolon() 内定義パターン
	$pattern = '/&\w+(?:\((?:(?!\)[;{]).)*\))?(?:\{(?:(?R)|(?!};).)*\})?;/u';
	
	foreach ($s as $msg1) {
		// }; を末尾に付加
		$msg2 = close_forgotten_brace_semicolon($msg1);  // 閉じ忘れ補完
		$msg3 = $msg2 . '};';  // 1個余分

		// カウント&インラインプラグイン文字列を取得
		$count1 = preg_match_all($pattern, $msg1, $matches1);
		$count2 = preg_match_all($pattern, $msg2, $matches2);
		$count3 = preg_match_all($pattern, $msg3, $matches3);

		// 補完がある場合は補完を1つ減らすと結果が変わることを確認
		if ($msg1 !== $msg2) {
			$msg4 = substr($msg2, 0, strlen($msg2) - 2);  // 補完後の }; を1個だけ除去したver
			$count4 = preg_match_all($pattern, $msg4, $matches4);
			if (
				$count4 === $count2
				&& ($count4 === 0 || end($matches4) === end($matches2))
			) {
				throw new Exception(
					'おかしい: '
					. json_encode($matches2)
					. ', ' . json_encode($matches4)
				);
			}
		}

		// 補完を1つ増やしても結果が変わらないことを確認
		if (
			$count3 !== $count2
			|| $count3 > 0 && end($matches3) !== end($matches2)
		) {
			throw new Exception(
				'おかしい: '
				. json_encode($matches2)
				. ', ' . json_encode($matches3)
			);
		}
	}
}

test_close_forgotten_brace_semicolon();

pcomment.inc.php中で

$date = (! isset($vars['nodate']) || $vars['nodate'] != '1') ?

の前あたりに

$msg = close_forgotten_brace_semicolon($msg);  // 追加

$date = (! isset($vars['nodate']) || $vars['nodate'] != '1') ?

とやりました。

これが適用されている状態では、

あいうえお&color(red){かきくけこ}さしすせそ
たちつてと&color(red){なにぬねの

とコメントすると自動的に

あいうえお&color(red){かきくけこ}さしすせそ};
たちつてと&color(red){なにぬねの};

と文末に }; が追加され、

あいうえおかきくけこ}さしすせそ -- 名無しさん &new{2023-01-01 (土) 00:00:00
たちつてとなにぬねの -- 名無しさん &new{2023-01-01 (土) 00:00:00

あいうえおかきくけこ}さしすせそ -- 名無しさん 2023-01-01 (土) 00:00:00
たちつてとなにぬねの -- 名無しさん 2023-01-01 (土) 00:00:00

というように、関係ない部分までプラグインの範囲が延びるのを防ぐことができます。




トップ   編集 凍結 差分 履歴 添付 複製 名前変更 リロード   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2023-01-07 (土) 22:42:25
Site admin: PukiWiki Development Team

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

SourceForge