WordPress 指定のキーワードの自動リンク

WordPress フォーラムに「指定のキーワードに自動的にリンクを貼るプラグインで日本語に対応したもを探しています」というような内容のトピックが起ったので調べてみました。

指定のキーワードに自動的にリンクを貼るプラグインを、いくつか見つけることができましたが、日本語 (分かち書なし) に対応しているものはありませんでした。(ご存知の方はコメントお願いします)

単語を正しく抽出(形態素解析)するのはパフォーマンスなど実装が難しいですが、単純文字列でもいいというケースは多いと思います。また、キーワードとリンクの設定も WordPress の標準機能であるリンクマネージャーを使用することで僅かなコードで実装することができそうだったので作ってみました。

functions.php

下記のコードをテーマの functions.php に追記するだけで完了です。これで WordPress のリンクマネージャーが使える (管理画面のメニューに「リンク」が追加される) ようになります。このリンクマネージャーに登録したリンクのタイトルをキーワードとして、投稿や固定ページの本文(コンテンツ)のキーワード(単語ではなく単純文字列)に自動的にリンクが貼られるようになります。

// Enable Link Manager.
add_filter( 'pre_option_link_manager_enabled', '__return_true' );

/**
 * Filters the post content.
 *
 * @since 0.0.1
 * @since 0.0.2 Changed the keyword to be set after sorting in descending order.
 * @since 0.0.3 Code refactoring.
 *
 * @param string $content Content.
 * @return string Content.
 */
function auto_linker_the_content( $content ) {
	$links = array();

	$bookmarks = get_bookmarks(
		array(
			'orderby' => 'name',
			'order'   => 'DESC',
		)
	);
	foreach ( (array) $bookmarks as $bookmark ) {
		$links[ $bookmark->link_name ] = array(
			'url'   => $bookmark->link_url,
			'blank' => '_blank' === $bookmark->link_target,
			'count' => 0,
		);
	}

	if ( 0 === count( $links ) ) {
		return $content;
	}

	$limit = -1; // Maximum number of links. -1: No limit.

	$regex = implode(
		'|',
		array_map(
			function( $key ) {
				return preg_quote( $key, '/' );
			},
			array_keys( $links )
		)
	);
	$regex = "/($regex)/i";

	$textarr = wp_html_split( $content );

	$ignore_elements = array( 'a', '/a', 'code', '/code', 'pre', '/pre', 'option', '/option' );
	$inside_block    = array();
	foreach ( $textarr as &$element ) {
		if ( 0 === strpos( $element, '<' ) ) {
			$offset     = 1;
			$is_end_tag = false;

			if ( 1 === strpos( $element, '/' ) ) {
				$offset     = 2;
				$is_end_tag = true;
			}

			preg_match( '/^.+(\b|\n|$)/U', substr( $element, $offset ), $matches );
			if ( $matches && in_array( $matches[0], $ignore_elements, true ) ) {
				if ( ! $is_end_tag ) {
					array_unshift( $inside_block, $matches[0] );
				} elseif ( $inside_block && $matches[0] === $inside_block[0] ) {
					array_shift( $inside_block );
				}
			}
		} else {
			if ( empty( $inside_block ) ) {
				if ( false === strpos( $element, 'http://' ) && false === strpos( $element, 'https://' ) ) {
					$element = preg_replace_callback(
						$regex,
						function( $matches ) use ( &$links, $limit ) {
							$key = $matches[0];
							if ( -1 === $limit || $links[ $key ]['count'] < $limit ) {
								$links[ $key ]['count']++;
								$target = $links[ $key ]['blank'] ? ' target="_blank" rel="noopener"' : '';
								$key = '<a href="' . esc_url( $links[ $key ]['url'] ) . '"' . $target . '>' . $key . '</a>';
							}
							return $key;
						},
						$element
					);
				}
			}
		}
	}

	return join( $textarr );
}

add_action( 'the_content', 'auto_linker_the_content', 12 );

プラグイン

同様の機能を持つプラグインも作成しました。プラグインで簡単に設定したいという方は、こちらをどうぞ。

auto-linker.0.0.4.zip

変更履歴

バージョン説明
0.0.1最初のリリース。
0.0.2キーワード (リンクのタイトル) を降順ソートしてから設定するようにしました。
0.0.3(リンクの上限数を変更しやすいように) コードリファクタリングしました。
0.0.4プラグインヘッダー(Text domain)を修正しました。

コメント

  • 初めまして。
    趣味で個人サイトを作っている者で、久遠と申します。
    指定のキーワードに自動的にリンクを貼るプラグインで、日本語に対応しているものを探していたのですが、なかなか見つからず、そんな折、こちらの記事を拝見いたしまして、たいへん助かりました。
    本当にありがとうございます。

    2つ教えていただきたい点があり、連絡いたしました。

    (1)
    当方、こちらのプラグインはWordPressの派生物であり、GPLライセンスが適用されるため、一部を改変して利用しても問題ない、という認識でおりますが、その認識で大丈夫でしたでしょうか。
    一部のみがGPLである(CSSやJSなどが含まれている場合?)などの場合はその限りではない、との文面も見かけ、こちらのプラグインは改変可能であるという認識で問題ないかを確認させていただければと思った次第です。
    (初歩的な質問でたいへん申し訳ありません)

    (2)
    もし、コードを編集して利用しても大丈夫な場合の話になるのですが、特定のカテゴリーに属する投稿ページのみ、自動的にリンクが作成されるようにするためには、どのようなコードを追加すればいいのかをご教授いただくことは可能でしょうか。
    有償依頼となる、受け付けていない、別途メールでのやり取りで行っているなどありましたら、教えていただければ幸いです。

    どうぞよろしくお願い申しいたします。

    • はじめまして、こんにちは。
      1) この投稿のコードは CC0 ライセンスです。ご自由にご利用ください。もちろん改変して配布しても問題ありません。プラグイン自体は GPL としていますが、この投稿のコードと同じコードなので、ライセンスはご自由に選択してください。
      2) 下記のようなコードで特定のカテゴリーのみを対象にできます。

      function auto_linker_the_content( $content ) {
      	$links = array();
      

      function auto_linker_the_content( $content ) {
      	if ( ! in_category( 'cat1' ) ) { return $content; }
      	$links = array();
      

      ※ コード内の cat1 は対象のカテゴリー名(スラッグ)です。

  • 石鷹さま

    こんばんは。お返事とコードまでありがとうございます!
    自分が作りたいサイトにまた一歩近づくことができました。
    本当に感謝いたします。

    追伸

    XO Featured Image Toolsもインストールさせていただきました。
    あったらいいな、と思っていたプラグインをいろいろ開発されていらっしゃって、サイトを作るのがまた楽しくなりそうです。
    (本当に無償で使わせていただいてもいいのだろうか、と思ってしまうぐらいです。ありがとうございます)

  • 石鷹様
    プラグインの開発ありがとうございます!キーワードリンクのプラグインをずっと探しているのですが、日本語未対応のが多く困っていました。本当にありがとうございます!
    ちなみにキーワードリンクの対象をページの最初のキーワードのみにしていただくことは可能でしょうか?もしくは改修は比較的簡単にできるものでしょうか。ご教授よろしくお願いいたします。

    • はじめまして、こんにちは。
      コード (35行目) を下記のように変更してみてください。
      $limit = -1; // Maximum number of links. -1: No limit.

      $limit = 1; // Maximum number of links. -1: No limit.

  • サイト内リンクのキーワードリンクで使用しているため、飛び先のページのキーワード(自己)は無効化していただけると助かります。ご対応いただけるのであれば有償でも問題ございません。ご検討よろしくお願いいたします。

  • 似た機能のプラグインを検索していたところ辿り着きました。大変有用コードありがとうございます。
    一点質問です。リンクを付与する対象を特定のタグに含まれてる時のみ、といった条件を追加することは可能でしょうか?現状、記事内の見出しタグにもリンクが追加されるので、pタグ内のキーワードのみリンクを追加したいと思っております。
    お手数おかけしますが、ご教授頂けますと助かります。よろしくお願いします。

    • はじめまして、こんにちは。
      現バージョンでは特定のタグのみのような設定はありません。PHP コードを変更することになります。
      特定のタグを除外(下記例では h1~h6)するのでよければ、下記のような僅かな変更ですみます。
      $ignore_elements = array( 'a', '/a', 'code', '/code', 'pre', '/pre', 'option', '/option' );

      $ignore_elements = array( 'a', '/a', 'code', '/code', 'pre', '/pre', 'option', '/option', 'h1', '/h1', 'h2', '/h2', 'h3', '/h3', 'h4', '/h4', 'h5', '/h5', 'h6', '/h6' );

  • キーワードにサイト内リンクを貼りたいと思い色々調べていたところ辿り着きました。とても有用なプラグインを開発いただきありがとうございます!

    1点質問なのですが、「投稿」だけでなく、「固定ページ」や「カスタム投稿」でも有効にすることは可能でしょうか?

    • こんにちは、はじめまして。
      そのままで「固定ページ」および「カステム投稿」を含むすべての投稿に対して有効です。
      有効になりませんか?

      • 大変失礼いたしました!
        「固定ページ」「カステム投稿」などリンク貼られました!

        ありがとうございました!

  • 石鷹様

    プラグインの開発ありがとうございます!
    質問なのですが、1つの送り先のURLに対して複数のキーワードを登録することは可能でしょうか?例えば「ジュリアス・シーザー」と「ユリウス・カエサル」をまとめて同じページに送るようにキーワードを登録したいです。

コメントを残す

メールアドレスが公開されることはありません。

日本語でコメントを入力してください。(スパム対策)