PHPでURLのリンク置換、外部サイトとの処理を分岐した正規表現

Updated / Published

Twitterのハッシュタグとアカウント名(スクリーンネーム)をリンクに置き換える正規表現と同じ正規表現の言明サブパターンを用いたネタです。今回は、http://〜 の一連のURLを構成する文字列をリンクに置き換える場合の正規表現について紹介します。

ユーザからのデータ入力を受け付けるシステムであれば、http://〜 のURLを構成する文字列をリンクに置き換えたりすることは少なくないはずです。また、自サイト内のURLが貼付けられた場合と他サイトのURLが貼付けられた場合とで、ただリンクに置換するだけでなく、他サイトには target="_blank" や rel="nofollw" を付けたいなど、処理を別けたいという場合も少なくないかと思います。

大概は if文に preg_match関数等を用いて、自サイトのドメインのパターンにマッチするかしないかで、マッチしなかった else の方に target="_blank" や rel="nofollow" を付けるというのが一般的に多い作法かと思いますが、こんなときにも言明のサブパターンを使えば簡便な繰り返しだけの記述で済みます。

$test という変数にユーザから受け付けたデータが入ってるとして、一応、HTTPとHTTPSの2つのプロトコルをサポートした場合は

//自サイト以外(他サイト)のURLの場合
$test = preg_replace("/(https?:\/\/)(?!w3g.jp)([-_.!~*'()a-zA-Z0-9;\/?:\@&=+\$,%#]+)/", "<a href=\"\\1\\2\" target=\"_blank\" rel=\"nofollow\">\\1\\2</a>", $test);
//自サイトのURLの場合
$test = preg_replace("/(https?:\/\/w3g.jp)([-_.!~*'()a-zA-Z0-9;\/?:\@&=+\$,%#]+)/", "<a href=\"\\2\">\\1\\2</a>", $test);

このように2つ続けて書くだけで、if文などを使う必要もありません。もちろん w3g.jp とある部分を自分のサイトのドメインに変えてもらう必要はありますが、一つずつ順を追って解説していきましょう。

  • まず、(https?:\/\/) は、http:// と https:// のHTTPとHTTPSの2つのプロトコルをサポートした記述です。shttp:// のSHTTPプロトコルもサポートするのであれば、(s?https?:\/\/) と記述します。
  • 続いて、(?!w3g.jp) が今回のポイントです。(? の部分が先読み言明で、(?= だと肯定、(?! だと否定を意味します。ここでは (?! を使っているので先読み言明の否定を意味します。これで w3g.jp があるURLにはマッチしないことになります。つまり、w3g.jp のつくURLについては次の2つ目の正規表現の方でマッチすることになります。
  • ([-_.!~*'()a-zA-Z0-9;\/?:\@&=+\$,%#]+) は、ハイフン「-」、アンダーバー「_」、ドット「.」、エクスクラメーションマーク「!」、チルダ「~」、アスタリスク「*」、シングルクォーテション「'」、左小括弧「(」、右小括弧「 )」、小文字アルファベット「a-z」、大文字アルファベット「A-Z」、数字「0-9」、セミコロン「;」、スラッシュ「/」、クエスチョンマーク「?」、コロン「:」、アットマーク「@」、アンパサド「&」、イコール「=」、プラス「+」、ドルマーク「$」、カンマ「,」、パーセント「%」、ハッシュ「#」を一連の文字クラスとして、+があるのでこれらのいずれかの文字が1回以上続くパターンにマッチするという意味です。
  • 2つ目の方の正規表現が自サイトのみにマッチする方で、(https?:\/\/w3g.jp) とあるように、http://w3g.jp か https://w3g.jp 始まりの以降に先の一連の文字クラスが一文字以上ある文字列にマッチします。

なお、参考までに上の正規表現だと、当然 http:// や https:// に一文字でも指定している文字が続けばマッチしてしまいます。ここでは簡単な正規表現の紹介で済ませましたが、正確な正規表現としては下記リンクのURI(URL) の正規表現を参照されることをおすすめします。
http://www.din.or.jp/~ohzaki/perl.htm#httpURL