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('#(?<!=[\'"])(?<=[*\')+.,;:!&$\s>])(\()?([\w]+?://(?:[\w\\x80-\\xff\#%~/?@\[\]-]{1,2000}|[\'*(+.,;:!=&$](?![\b\)]|(\))?([\s]|$))|(?(1)\)(?![\s<.,;:]|$)|\)))+)#is', '_make_url_clickable_cb', $ret); if (null !== $retval ) $ret = $retval; @ini_set('pcre.recursion_limit', $save); $ret = preg_replace_callback('#([\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( [^>]+?>|>))<a [^>]+?>([^>]+?)</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('#(?<!=[\'"])(?<=[*\')+.,;:!&$\s>])(\()?([\w]+?://(?:[\w\\x80-\\xff\#%~/?@\[\]-]{1,2000}|[\'*(+.,;:!=&$](?![\b\)]|(\))?([\s]|$))|(?(1)\)(?![\s<.,;:]|$)|\)))+)#is', '_make_url_clickable_cb', $ret); $retval = preg_replace_callback('#(?<!=[\'"])(?<=[*\')+.,;:!&$\s>])(\()?([\w]+?://(?:[\w\-\.!~*\'\(\);\/?:\@&=+\$,%\#]{1,2000}|[\'*(+.,;:!=&$](?![\b\)]|(\))?([\s]|$))|(?(1)\)(?![\s<.,;:]|$)|\)))+)#is', '_make_url_clickable_cb', $ret); if (null !== $retval ) $ret = $retval; @ini_set('pcre.recursion_limit', $save); $ret = preg_replace_callback('#([\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( [^>]+?>|>))<a [^>]+?>([^>]+?)</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 に投げておこうと思います。