【カスタムフィード】WordPressでカスタムフィードを作成する方法

【カスタムフィード】WordPressでカスタムフィードを作成する方法

今回はWordPressでカスタムフィードを作成する方法を紹介します。

通常のfeedでは対応できない場合や独自の処理を挟みたい場合に有効です。

今回紹介する方法はxmlファイルに出力する方法ではなくアクセスされたxmlを返す動的なタイプです。

ついでに特定のアクセス以外拒否するためのBasic認証も付けたいと思います。

feedを作成していくためのフック

phpで処理を一から記述してfeedを作成しても良いですが、WordPressを使用しているならfeedが標準で使用されていることを知っている人が多いと思います。

せっかく用意してあるので、それに追加する形での実装をしていきます。

想定する感じとしてはテーマ内実装よりもプラグインの方が多そうなのでクラスで記述しています。

class My_RSS {

    public function __construct() {
        add_action( 'init', [$this, 'custom_feed'] );
    }

    /**
     * feedの登録
     */
    public function custom_feed() {
        add_feed( 'my_rss', [$this, 'load_feed_template']);
    }

    /**
     * feedの読み込み
     */
    public function load_feed_template() {
        $feed_template = plugins_url( 'feeds/my_rss.php', __FILE__ );
        load_template( $feed_template );
    }
}

順を追ってみていくと「init」に「add_feed」関数を引っ掛けてることがわかります。

「add_feed」で登録を行うと、「https://kumatech-lab.com/feed/my_rss」のような形で読み込むことが可能になります。

WP-CLIで「wp rewrite list」など叩いてリライトルールを確認するとしっかり追加されていることが確認できます。

そしてテンプレートを読み込むだけです。

「load_template」で読み込むので、相対パスを使用することに気をつけて下さい。

テンプレートファイルの中身

テンプレートファイルは簡単でRSS2.0など規格に沿って書いていくだけです。

<?php 
/**
 * my rss
 */

$post_args = [
    'post_type'            => 'post',
    'orderby'              => 'date',
    'order'                => 'DESC',
    'ignore_stickey_posts' => true,
    'posts_per_page'       => 20,
    'post_status'          => 'publish',
    'meta_query'           => [
        'relation' => 'OR',
        [
            'key'     => 'my_rss_display',
            'value'   => 'not_display',
            'compare' => '!='
        ],
        [
            'key'     => 'my_rss_display',
            'compare' => 'NOT EXISTS'
        ]
    ]
];
$query = new WP_Query( $post_args );

header( 'Content-Type: ' . feed_content_type( 'rss2' ) . '; charset=' . get_option( 'blog_charset' ), true );
echo '<?xml version="1.0" encoding="' . get_option( 'blog_charset' ) . '"?' . '>';
?>
<rss version="2.0"
xmlns:content="http://purl.org/rss/1.0/modules/content/"
     xmlns:dc="http://purl.org/dc/elements/1.1/"
     xmlns:media="http://search.yahoo.com/mrss/">
<channel>
    <title><?php wp_title_rss(); ?></title>
    <link><?php bloginfo_rss('url'); ?></link>
    <description><?php bloginfo_rss('description'); ?></description>
    <lastBuildDate><?php echo get_feed_build_date( 'r' ); ?></lastBuildDate>
    <pubDate><?php echo get_feed_build_date( 'r' ); ?></pubDate>
    <language><?php bloginfo_rss( 'language' ); ?></language>
    <ttl>1</ttl>
    <?php if ( $query->have_posts() ) : while ( $query->have_posts() ) : $query->the_post(); ?>
    <item>
        <title><?php the_title_rss(); ?></title>
        <link><?php the_permalink_rss(); ?></link>
        <pubDate><?php echo get_post_time( 'r', true, $post, false ); ?></pubDate>
        <description><![CDATA[<?php the_excerpt_rss(); ?>]]></description>
        <content:encoded><![CDATA[<?php the_content(); ?>]]></content:encoded>
    </item>
    <?php endwhile; endif; ?>
</channel>
</rss>

<?php
wp_reset_postdata();

上記はあくまで一例です。

動的に出力するのであれば大体投稿から取得する形が多いと思います。

上記例ではカスタムフィールドで「feedに出力しない」というチェック項目がある前提でmeta_queryで絞り込みをしています。

「wp-includes/feed-rss2.php」などWordPressのデフォルトの表示方法をコピーして修正していくと捗ります。

Basic認証を追加する

RSSの収集クローラーがBasic認証に対応している場合、Basic認証設定しておくとコピーコンテンツなどを多少防ぐことができるかもしれません。

自分の中では「.htaccess」ファイルを使用して設定することが多かったのでコードから出来るのは意外でした。

特定のページのみブロックしたい場合はこちらの方がお手軽な気がします。

function basic_auth( $auth_list, $realm = 'Restricted Area', $failed_text = '認証に失敗しました' ){
    if ( isset( $_SERVER['PHP_AUTH_USER'] ) && isset( $auth_list[$_SERVER['PHP_AUTH_USER']] ) ) {
        if ( $auth_list[$_SERVER['PHP_AUTH_USER']] == $_SERVER['PHP_AUTH_PW'] ) {
            return $_SERVER['PHP_AUTH_USER'];
        }
    }
    header( 'WWW-Authenticate: Basic realm="' . $realm . '"' );
    header( 'HTTP/1.0 401 Unauthorized' );
    header( 'Content-type: text/html; charset=' . mb_internal_encoding() );
    die( $failed_text );
}

上記のコードは参考にさせていただいたサイトがあったのですが、どこか思い出せず、、、

詳しい仕組みについては「PHPを利用したBasic認証の仕組み – Qiita」がわかりやすく解説されています。

コード的にはgithubに出ていたコードがきれいかと。

おまけ:サムネイル画像をサポートしていてエンコードが必要な場合

収集サイト側で独自の規格が定められていてサムネイルなどサポートされている場合。

多くの場合はエンコードURLを渡せば済むと思います。

ただ、画像のファイル名が日本語でその部分だけエンコードしたいということがあったの追記で紹介しておきます。

function image_urlencode( $str ) {
    return preg_replace_callback(
    '/[^\x21-\x7e]+/',
    function( $matches ) {
        return urlencode( $matches[0] );
    },
    $str );
}

こちらのサイトを参考にさせていただきました。

PHPでGET等で受け取った文字列のうち日本語部分のみをurlencodeする

まとめ

作成する前は結構手間がかかるのかと思いましたが思いのほか簡単でした。

composerなど使用したい場合は「bhaktaraz/php-rss-generator」が便利かと思います。

テンプレートファイル一つで済むのでcomposer使うまでもないかなという印象ですね。