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( '#(?<!=[\'"])(?<=[*\')+.,;:!&$\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 等に以下のようなコードを追記してやればおっけのはずです。
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('#(?<!=[\'"])(?<=[*\')+.,;:!&$\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 に投げておこうと思います。