WordPress コメント欄の URL 変換がおかしい気がする

WordPress ではコメント欄に URL が書き込まれると、自動的に <a> タグに変換してくれます。
これを実現しているのが、wp-includes\default-filters.php に記述してあるフィルターフック add_filter( 'comment_text', 'make_clickable', 9 ); と、wp-includes\formatting.php に記述してある make_clickable() 関数。
しかし、この make_clickable() 関数内で使用している正規表現が適切では無いため、全角文字も URL として処理されてしまいます。
これにより URL の後ろにスペースが入らず、そのままコメントが続いてしまうと、嫌な感じに整形されてしまいます。

URLに使える文字列を規定している RFC2396 によると、URLとして使える文字は reserved | unreserved | escaped の3種類です。
大雑把にまとめると

  • reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | ","
  • unreserved = alphanum (英数字) | mark ( "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")" )
  • escaped = "%" hex hex
    ※日本語などの unreserved 以外の文字は16進数のコードに変換後、頭に % をつけてエスケープする。

一方 wp-includes\formatting.php では以下のように記述されています。

function make_clickable($ret) {
	$ret = ' ' . $ret;
	// in testing, using arrays here was found to be faster
	$save = @ini_set('pcre.recursion_limit', 10000);
	$retval = preg_replace_callback('#(?<!=&#91;\'"&#93;)(?<=&#91;*\')+.,;:!&$\s>])(\()?([\w]+?://(?:[\w\\x80-\\xff\#%~/?@\[\]-]{1,2000}|[\'*(+.,;:!=&$](?![\b\)]|(\))?([\s]|$))|(?(1)\)(?![\s<.,;:&#93;|$)|\)))+)#is', '_make_url_clickable_cb', $ret);
	if (null !== $retval )
		$ret = $retval;
	@ini_set('pcre.recursion_limit', $save);
	$ret = preg_replace_callback('#(&#91;\s>])((www|ftp)\.[\w\\x80-\\xff\#$%&~/.\-;:=,?@\[\]+]+)#is', '_make_web_ftp_clickable_cb', $ret);
	$ret = preg_replace_callback('#([\s>])([.0-9a-z_+-]+)@(([0-9a-z-]+\.)+[0-9a-z]{2,})#i', '_make_email_clickable_cb', $ret);
	// this one is not in an array because we need it to run last, for cleanup of accidental links within links
	$ret = preg_replace("#(<a( &#91;^>]+?>|>))<a &#91;^>]+?>([^>]+?)</a></a>#i", "$1$3</a>", $ret);
	$ret = trim($ret);
	return $ret;
}

URL を判別している1396行目の正規表現中に [\w\\x80-\\xff\#%~/?@\[\]-]{1,2000} という記述がありますが、これだと RFC2396 に準拠してない。
正しくは [\w\-\.!~*\'\(\);\/?:\@&=+\$,%\#]{1,2000} のような気がするんですが

そんなわけで、これを是正するためにはテーマの functions.php 等に以下のようなコードを追記してやればおっけのはずです。

function my_make_clickable($ret) {
	$ret = ' ' . $ret;
	// in testing, using arrays here was found to be faster
	$save = @ini_set('pcre.recursion_limit', 10000);
//	$retval = preg_replace_callback('#(?<!=&#91;\'"&#93;)(?<=&#91;*\')+.,;:!&$\s>])(\()?([\w]+?://(?:[\w\\x80-\\xff\#%~/?@\[\]-]{1,2000}|[\'*(+.,;:!=&$](?![\b\)]|(\))?([\s]|$))|(?(1)\)(?![\s<.,;:&#93;|$)|\)))+)#is', '_make_url_clickable_cb', $ret);
	$retval = preg_replace_callback('#(?<!=&#91;\'"&#93;)(?<=&#91;*\')+.,;:!&$\s>])(\()?([\w]+?://(?:[\w\-\.!~*\'\(\);\/?:\@&=+\$,%\#]{1,2000}|[\'*(+.,;:!=&$](?![\b\)]|(\))?([\s]|$))|(?(1)\)(?![\s<.,;:&#93;|$)|\)))+)#is', '_make_url_clickable_cb', $ret);
	if (null !== $retval )
		$ret = $retval;
	@ini_set('pcre.recursion_limit', $save);
	$ret = preg_replace_callback('#(&#91;\s>])((www|ftp)\.[\w\\x80-\\xff\#$%&~/.\-;:=,?@\[\]+]+)#is', '_make_web_ftp_clickable_cb', $ret);
	$ret = preg_replace_callback('#([\s>])([.0-9a-z_+-]+)@(([0-9a-z-]+\.)+[0-9a-z]{2,})#i', '_make_email_clickable_cb', $ret);
	// this one is not in an array because we need it to run last, for cleanup of accidental links within links
	$ret = preg_replace("#(<a( &#91;^>]+?>|>))<a &#91;^>]+?>([^>]+?)</a></a>#i", "$1$3</a>", $ret);
	$ret = trim($ret);
	return $ret;
}
remove_filter( 'comment_text', 'make_clickable', 9 );
add_filter( 'comment_text', 'my_make_clickable', 9 );

バグのような気がするので、後で Trac に投げておこうと思います。

コメントを残す

メールアドレスが公開されることはありません。

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください