WordPressで簡単な会員制サイトを自作!プラグイン開発の基本コード解説

2019.03.29

今回は、WordPressで作られたWebサイトを、簡単に会員制サイトとして利用できるようにする自作プラグインについてご紹介します。

このプラグインは、設定ページで会員限定にしたい投稿タイプ(例:「投稿」や「カスタム投稿」)をチェックするだけで、対象の投稿すべてにアクセス制限をかけられるシンプルな仕組みです。

プラグインの使い方

基本的な使い方は以下の4ステップです。

  1. プラグインを有効化します。
  2. プラグインの設定ページで、アクセスを制限したい投稿タイプを選択します。
  3. 会員登録フォーム用のショートコード [ilm_membership_form] を任意の固定ページ(例:「会員登録」ページ)に設定します。
  4. 設定した「会員登録」ページのIDを、プラグインの設定画面に入力します。

同様に、ログインフォームを独自に設定したい場合は、ログインフォーム用のショートコード [ilm_membership_login_form] を任意のページに設定し、そのページIDを入力してください。

設定が完了すると、未ログインのユーザーがアクセス制限のかかった投稿を閲覧しようとした場合、強制的にログインフォームへリダイレクトされるようになります。

コードの解説

ここからは、このプラグインがどのような仕組みで動作しているのか、主要なPHPコードを解説していきます。

1. フォームの設定(ショートコード化)

まず、会員登録フォームとログインフォームは、ショートコードで実装します。

ショートコード化する最大のメリットは、管理画面から好きなページに [shortcode] という短いコードを貼り付けるだけで、簡単にフォームを設置できる点です。これにより、テーマファイルを直接編集する必要がなくなり、管理やカスタマイズが容易になります。

public function __construct()
{
    //ショートコード
    add_shortcode('ilm_membership_form', array($this, 'ilm_membership_form'));
    add_shortcode('ilm_membership_login_form', array($this, 'ilm_membership_login_form'));
}


//会員登録フォーム
public function ilm_membership_form()
{
    $nonce = wp_nonce_field('ilm_membership_form', 'nonce_ilm_membership_form', true, false);
    $html = <<< EOT
    <form class="ilm_membership_system_form" action="{$_SERVER['REQUEST_URI']}" method="POST">
        <div class="user_login">
            ログインID
            <input class="ilm_membership_system_form_check" name="user_login" type="text" value="">
            <span class="error"></span>
        </div>

        <div class="user_email">
            メールアドレス
            <input class="ilm_membership_system_form_check" name="user_email" type="email" value="">
            <span class="error"></span>
        </div>

        <div class="user_pass">
            パスワード
            <input class="ilm_membership_system_form_check" name="user_pass" type="password" value="">
            <span class="error"></span>
        </div>

        {$nonce}
        <button type="submit">登録する</button>
    </form>
EOT;
    return $html;
}


//ログインフォーム
public function ilm_membership_login_form()
{
    $args = array(
        'echo' => false,
    );
    $html = wp_login_form($args);
    return $html;
}

会員登録フォーム用のショートコード: [ilm_membership_form]

ログインフォーム用のショートコード: [ilm_membership_login_form]

2. 独自ロールの追加とユーザー登録

次に、会員登録の仕組みです。まず、会員専用の「ロール(権限グループ)」を新設します。

WordPressには予め「管理者」「編集者」「購読者」といったロールが設定されていますが、既存のロールを使うと他の機能と干渉する可能性があるため、プラグイン独自のロールを設ける方が安全です。

ここでは「購読者」とほぼ同等の権限(閲覧のみ可能)を持つ、ilm_membership_system_user(表示名:会員)というロールを追加します。

ロール追加のタイミングは、プラグインを有効化した時が最適です。register_activation_hook()フックを使えば、プラグイン有効化の瞬間に指定した関数(ここではadd_role)を実行できます。

//ユーザーroleの追加(プラグイン有効化時に実行)
function add_role()
{
    add_role('ilm_membership_system_user', '会員', array('read' => true));
}

新規ユーザーの登録には、WordPress標準のwp_insert_user()関数を利用します。この関数の引数に、フォームからPOSTされた値を渡すことで、新しいユーザーがデータベースに追加されます。

このユーザー追加処理は、「フォームからPOSTデータが送信された」かつ「そのページに会員登録のショートコードが設置されている」場合にのみ実行するように制御します。

ページコンテンツ内に特定のショートコードが含まれているか(has_shortcode())をチェックし、意図しない場所で処理が実行されるのを防ぎます。

public function __construct()
{
    $args = array(
        'user_login' => FILTER_SANITIZE_ENCODED,
        'user_pass' => FILTER_SANITIZE_ENCODED,
        'user_email' => FILTER_SANITIZE_EMAIL,
    );
    $this->post_data = filter_input_array(INPUT_POST, $args);
    add_action('get_header', array($this, 'get_header'));
}


//投稿で実行
public function get_header()
{
    global $post;
    if (has_shortcode($post->post_content, 'ilm_membership_form')
    && $this->post_data) {

        //会員登録の処理(バリデーション等は次項で解説)
        $this->set_add_member_form();
    }
}


//会員登録フォーム
private function set_add_member_form()
{
    if ($this->post_data['user_login']
    && $this->post_data['user_pass']
    && $this->post_data['user_email']) {

        //ユーザー作成
        $userdata = array(
            'user_login' => $this->post_data['user_login'],
            'user_pass' => $this->post_data['user_pass'],
            'user_email' => $this->post_data['user_email'],
            'role' => 'ilm_membership_system_user', //ここで独自ロールを指定
        );
        $user_id = wp_insert_user($userdata);
    }
}

3. 登録前のバリデーション(重複チェック)

ユーザー登録処理を実行する前に、バリデーション(入力値の検証)を行います。セキュリティとデータ整合性のために不可欠な処理です。

具体的には、「nonce(ワンタイムトークン)が正しいか」そして「入力されたIDやメールアドレスが既に使用されていないか」をチェックします。

get_user_by()関数を使い、POSTされたlogin(ID)やemail(メールアドレス)で既存ユーザーを検索し、もし存在すれば登録処理を中断(リダイレクト)させます。

public function __construct()
{
    $args = array(
        'user_login' => FILTER_SANITIZE_ENCODED,
        'user_email' => FILTER_SANITIZE_EMAIL,
        'nonce_ilm_membership_form' => FILTER_SANITIZE_ENCODED,
    );
    $this->post_data = filter_input_array(INPUT_POST, $args);
    add_action('get_header', array($this, 'get_header'));
}


//投稿で実行
public function get_header()
{
    global $post;
    if (has_shortcode($post->post_content, 'ilm_membership_form')
    && $this->post_data) {

        //重複とnonceのバリデーション
        $this->set_validation_member_form($post);

        //会員登録の処理
        $this->set_add_member_form();
    }
}


//会員登録フォームのバリデーション
private function set_validation_member_form($post)
{
    $user_login_check = get_user_by('login', $this->post_data['user_login']);
    $user_email_check = get_user_by('email', $this->post_data['user_email']);

    // IDが重複している、またはEmailが重複している、またはNonceが無効
    if ($user_login_check
    || $user_email_check
    || !wp_verify_nonce($this->post_data['nonce_ilm_membership_form'], 'ilm_membership_form')) {
        wp_redirect(get_the_permalink($post->ID)); // 登録ページにリダイレクト
        exit;
    }
}

4. ユーザー登録後の通知メール送信

ユーザー登録が成功したら、登録内容を記載した通知メールを送信するのが親切です。これにより、ユーザーは自分の登録情報を確認できます。

WordPressには標準でwp_mail()という便利なメール送信関数が用意されているので、これを利用します。

wp_insert_user()は、ユーザー登録に成功するとユーザーID(数値)を、失敗するとfalseを返します。この戻り値をチェックし、成功した場合のみメール送信関数(set_notification_mail)を実行します。

public function __construct()
{
    $args = array(
        'user_login' => FILTER_SANITIZE_ENCODED,
        'user_pass' => FILTER_SANITIZE_ENCODED,
        'user_email' => FILTER_SANITIZE_EMAIL,
    );
    $this->post_data = filter_input_array(INPUT_POST, $args);
    add_action('get_header', array($this, 'get_header'));
}


//会員登録の処理
private function set_add_member_form()
{
    //(バリデーションは通過済みとする)
    
    //ユーザー作成
    $userdata = array(
        'user_login' => $this->post_data['user_login'],
        'user_pass' => $this->post_data['user_pass'],
        'user_email' => $this->post_data['user_email'],
        'role' => 'ilm_membership_system_user',
    );
    $user_id = wp_insert_user($userdata);

    //ユーザー追加後の処理
    if ($user_id && !is_wp_error($user_id)) { // 成功したら

        //通知メール送信
        $url = wp_login_url();
        $ilm_membership_login_form_id = get_option('ilm_membership_login_form_id');
        if ($ilm_membership_login_form_id) {
            $url = get_the_permalink($ilm_membership_login_form_id);
        }
        $this->set_notification_mail($url);
    }
}


//通知メールの送信
private function set_notification_mail($url)
{
    $sub = get_bloginfo('name') . 'の会員登録が完了しました。';
    $message = <<‎ EOT
会員登録ありがとうございます。
下記内容で会員登録を承りました。

ログインID : {$this->post_data['user_login']}
メールアドレス : {$this->post_data['user_email']}
パスワード : {$this->post_data['user_pass']}

以下のURLからログインできます。
{$url}
EOT;
    wp_mail($this->post_data['user_email'], $sub, $message);
}

5. アクセス制限の設定

最後に、中核機能であるアクセス制限の実装です。

(ここでは詳細を省きますが、プラグインの設定ページはadmin_menuアクションフックとadd_menu_page()関数を利用して作成し、「アクセス制限をかけたい投稿タイプ」をoptionsテーブルに保存します)

そして、保存された設定値(get_option('ilm_membership_restrict_post_type'))を元に、実際のアクセス制限処理を行います。

ロジックは非常にシンプルで、「①ユーザーがログインしていない」かつ「②閲覧しようとしている投稿タイプが制限対象に指定されている」場合に、強制的にログインページへリダイレクトさせる、というものです。

この際、会員登録ページやログインページ自体がリダイレクト対象になると無限ループに陥ってしまうため、それらのページを除外する条件分岐が非常に重要です。

public function __construct()
{
    add_action('get_header', array($this, 'get_header'));
}

//投稿で実行
public function get_header()
{
    //アクセス制限
    global $post;
    $this->set_page_redirect($post);

    //(会員登録処理などもここに入る)
}


//アクセス制限
private function set_page_redirect($post)
{
    $ilm_membership_form_id = get_option('ilm_membership_form_id'); // 登録ページID
    $ilm_membership_login_form_id = get_option('ilm_membership_login_form_id'); // ログインページID
    
    // ログインURL(カスタムログインページがなければデフォルト)
    $login_url = wp_login_url();
    if ($ilm_membership_login_form_id) {
        $login_url = get_the_permalink($ilm_membership_login_form_id);
    }

    // 表示しようとしているページが「登録ページ」または「ログインページ」なら、何もしない
    if ($post->ID == $ilm_membership_form_id
    || $post->ID == $ilm_membership_login_form_id) {
        return;
    }

    // ログインしておらず、かつ制限対象の投稿タイプならリダイレクト
    if (!is_user_logged_in()
    && in_array($post->post_type, get_option('ilm_membership_restrict_post_type'))) {
        wp_redirect($login_url);
        exit;
    }
}

よくある質問(FAQ)

Q1. ショートコードを設置しましたが、フォームのデザインが崩れてしまいます。
A1. このプラグインのコードは、フォームのHTML構造を出力するだけです。デザイン(CSS)は含まれていません。お使いのWebサイトのテーマに合わせて、style.cssなどに独自のCSSを記述し、.ilm_membership_system_formクラスや各inputタグにスタイルを適用してください。
Q2. 会員登録フォームに「名前」や「フリガナ」の項目を追加したいです。
A2. フォーム項目を増やすには、PHPコードのカスタマイズが必要です。まず、ilm_membership_form関数内の<form>タグに、名前用の<input name="first_name">などを追加します。次に、set_add_member_form関数内でPOSTデータを受け取り、$userdata配列に'first_name' => $this->post_data['first_name']のように追加してwp_insert_user()に渡す必要があります。
Q3. このプラグインはそのまま本番サイトで使っても安全ですか?
A3. この記事のコードは、会員制サイトの基本的な仕組みを学ぶためのサンプルです。Nonce検証や入力値のサニタイズ(FILTER_SANITIZE_ENCODEDなど)といった基本的なセキュリティ対策は含まれていますが、本番環境で本格的に運用するには、パスワード強度チェッカー、reCAPTCHA(ボット対策)、より厳密なエラーハンドリングなどを追加実装することを強く推奨します。

CONTACT

webサイト制作、デザインに関するご相談、御見積のご依頼など、弊社へのお問い合わせはこちら