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 では以下のように記述されています。

1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
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 等に以下のようなコードを追記してやればおっけのはずです。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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 を使っています。コメントデータの処理方法の詳細はこちらをご覧ください