URLの正規表現とpreg_replace_callback関数について

今はphpを使って開発しているんだけど相変わらず正規表現につまずく。
後、Javaに慣れてしまっているんで相変わらずphpの標準関数とかにもつまずく。
というわけで備忘録として書いてみました。

まずはURLの正規表現についてですが、
何をしたかったかというとテキスト本文中にURLがあるとリンクにしたかった訳です。
どういうことかと言うと、、、

ヤホーでぐぐれ。 http://www.google.co.jp 分かったか。

これを、、

ヤホーでぐぐれ。 http://www.google.co.jp 分かったか。

というようにしたいわけです。


まずはURLを正規表現で抽出する方法ですが、どっかからのサイトのパクリです。

/https?:\/\/[-_.!~*'()a-zA-Z0-9;\/?:\@&=+\$,%#]+/i

先頭と最後に/がついてますが、他の言語だと違った筈なので適宜読み替えて下さい。

これで本文中にあるURLを抽出できます。


更にもう一歩進んでこんな表記がされていた場合、

ヤホーでぐぐれ。 www.google.co.jp 分かったか。

とhttp://が省かれていますが、これも先ほどと同じく、

ヤホーでぐぐれ。 www.google.co.jp 分かったか。

とリンクにしたい場合、
先ほどの正規表現に対して更に条件を加えます。

/https?:\/\/[-_.!~*'()a-zA-Z0-9;\/?:\@&=+\$,%#]+|w{3}[.][-_.!~*'()a-zA-Z0-9;\/?:\@&=+\$,%#]+/i

これで先ずは本文中にURL表記されいてる箇所を抽出できるようになりました。

本来ならwwwがついてないURLに対しても抽出したかったんだけど、
今回はそこまでする必要がなかったので断念します。
後は、http://やwwwから始まったらその後に半角英数が続くまでURLの抽出条件にされてしまうのでその考慮も今後は必要かなと。



じゃあ次にURL部分をリンクにする方法ですが、
簡単な方法は該当する箇所があればaタグに置き換えるという方法です。
じゃあ早速やってみましょう。
一番簡単なやり方はpreg_replace関数を利用するという方法です。
説明がめんどくさいのでソース載せます。

class StringUtils {
    public function urlRegex($str) {
        $pattern = "/https?:\/\/[-_.!~*'()a-zA-Z0-9;\/?:\@&=+\$,%#]+|w{3}[.][-_.!~*'()a-zA-Z0-9;\/?:\@&=+\$,%#]+/i";
        $replacement = '<a href="\0" target="_blank">\0</a>';
        $strText = preg_replace($pattern, $replacement, $str);
        return $strText;
    }
}

これで$strTextにタグ付きとして変換された本文になりました。
良かった良かった。。。。と思っていたところ、
wwwから始まるURLだと不味い事が分かりました。困っていたらpreg_replace_callbackという関数があったので早速これを使って解決してみましょう。


じゃあ早速

class StringUtils {
    public function urlRegex($str) {
        $pattern = "/https?:\/\/[-_.!~*'()a-zA-Z0-9;\/?:\@&=+\$,%#]+|w{3}[.][-_.!~*'()a-zA-Z0-9;\/?:\@&=+\$,%#]+/i";
        $strText = preg_replace_callback($pattern, 'replace', $str);
        return $strText;
    }

    public function replace($matches) {
        〜
        〜
    }
}

と修正していざ実行してみると、、、、

Warning: preg_replace_callback(): Requires argument 2, 'replace', to be a valid callback

というエラーが発生してしまいました。
なぜだ、どこも間違っていないのに、、、と悩んでいたのですがどうやら
preg_replace_callbackの関数の呼び出し方法がまずかったみたいです。
という事で修正したのがこちら、、、

class StringUtils {
    public function urlRegex($str) {
        $pattern = "/https?:\/\/[-_.!~*'()a-zA-Z0-9;\/?:\@&=+\$,%#]+|w{3}[.][-_.!~*'()a-zA-Z0-9;\/?:\@&=+\$,%#]+/i";
        $strText = preg_replace_callback($pattern, array(get_class($this), 'replace'), $str);
        return $strText;
    }

    public function replace($matches) {
        〜
        〜
    }
}

関数を呼び出すときは

array(get_class($this), 'replace')

と書かないと駄目みたいです。詳しい説明は省きますがclassを使っている時はこう書きます。


以上でwwwから始まるURLも無事に変換出来ました。
いやぁ良かった良かった。

間違いがあればコメントくだせぇ。