Help us understand the problem. What is going on with this article?

URLをリンクにする(但しすでにリンクになっていたら無視)

More than 3 years have passed since last update.

いまさら感の拭えないタイトルではありますが、意外と「すでにリンクになっている場合もある」想定のノウハウが見つかりませんでした。

preg_replace_callback でゴリ押しする方法

/**
 * @param string $body 処理対象のテキスト
 * @param string|null $link_title リンクテキスト
 * @return string
 */
function url2link($body, $link_title = null)
{
    $pattern = '/(href=")?https?:\/\/[-_.!~*\'()a-zA-Z0-9;\/?:@&=+$,%#]+/';
    $body = preg_replace_callback($pattern, function($matches) use ($link_title) {
        // 既にリンクの場合や Markdown style link の場合はそのまま
        if (isset($matches[1])) return $matches[0];
        $link_title = $link_title ?: $matches[0];
        return "<a href=\"{$matches[0]}\">$link_title</a>";
    }, $body);
    return $body;
}

使い方

// リンクタイトルなし
echo url2link($body);
// リンクタイトルあり
echo url2link($body, "詳細はこちら");

デモ

解説

preg_replace_callback はその名の通り、正規表現にマッチした対象をコールバックで逐次処理する関数です。
コールバックで処理するため、よりきめ細やかな置換処理ができるのはもちろんのこと、コールバックの中でさらに置換するかしないかの判断ができるというメリットがあります。

つまり上記コードで、正規表現状は (href=")? とあるように、href 属性の形になっていてもなっていなくてもマッチするように書いていますが、コールバックの中で href=" にマッチしている場合は対象を置換せずにそのまま返却することで、置換をキャンセルしています。

たとえばさらに Markdown スタイルのリンクもキャンセルしたいならば、先頭のマッチグループを (href="|]\()? とすればOKです。

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

否定戻り読みを使う方法

正規表現の高等テクニックとして

  • 肯定先読み (?=regex)
  • 否定先読み (?!regex)
  • 肯定戻り読み (?<=regex)
  • 否定戻り読み (?<!regex)

参考: 正規表現(肯定先読み、否定先読み、肯定戻り読み、否定戻り読み)

というものがあります。このうち否定戻り読みを使って href=" が前方にくっついている場合はそもそもマッチしない正規表現をつくることができます。
キャンセル処理が不要なのでこちらのほうがスッキリします。

/**
 * @param string $body 処理対象のテキスト
 * @param string|null $link_title リンクテキスト
 * @return string
 */
function url2link($body, $link_title = null)
{
    $pattern = '/(?<!href=")https?:\/\/[-_.!~*\'()a-zA-Z0-9;\/?:@&=+$,%#]+/';
    $body = preg_replace_callback($pattern, function($matches) use ($link_title) {
        $link_title = $link_title ?: $matches[0];
        return "<a href=\"{$matches[0]}\">$link_title</a>";
    }, $body);
    return $body;
}

デモ

こちらもさらに Markdown スタイルのリンクを除外する場合は (?<!href="|]\() とすればOKです。

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


否定戻り読みを使う方法のほうが正攻法だとは思いますが、あまり知られていなかったり覚えていないこともあるかと思います。preg_replace_callback を使えば、平易な表現でも高度な処理に手が届くなと思い、2つの方法を紹介しました。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした