Skip to content

Instantly share code, notes, and snippets.

@yano3nora
Last active February 2, 2019 20:19
Show Gist options
  • Save yano3nora/e9448cf29a05bfd643637b79bb7a7c02 to your computer and use it in GitHub Desktop.
Save yano3nora/e9448cf29a05bfd643637b79bb7a7c02 to your computer and use it in GitHub Desktop.
[wordpress: note] WordPress - Blog CMS by php. #php #wordpress #cms

OVERVIEW

WordPressとは? - WordPress 超初心者講座

ブログサイト CMS だと思っていい。時系列ページの扱いに長けていて、固定ページの扱いは面倒。固定ページが10以下でブログ等の時系列ページをたくさん量産したい時には便利。あとは新規記事投稿をカスタマイズして、ポータルサイト的なものも作りやすい。

Features

  • 「記事」を「管理画面」から作成して「ユーザ画面」で表示/インデックスできる
    • 記事は「カテゴリー」や「タグ」によって分類できる
    • 記事の「投稿者」や「購読者」などのアカウント/ロール/認証機能が予め備わっている
    • 記事には「コメント」がつけられる
  • 記事ではない静的なページを「固定ページ」として登録できる
  • WEB サイトとしてのビジュアルや機能について「テーマ」概念で拡張できる
    • テーマ内で「メニュー」「ウィジェット」「背景」などの登録をして WEB サイトを構築できる
    • 各ベンダーの提供テーマを独自拡張する「子テーマ」を作成できる
    • その他 functions.php など ”カスタマイズ可能なファイル群” によって CSS や PHP などコードレベルで独自拡張できる
  • システム的な機能拡張について「プラグイン」概念で拡張できる
  • マルチサイト化機能 で 1 サーバ 1 プログラムで複数サイトが構築可能
  • ロケール対応 i18n / L10n

References

基本的には Codex で利用できる API が網羅されているので、やりたいこと別に API を探して云々 ... という形になる。

For development

Tips

ロケール対応

ロケール対応 i18n / L10n
WordPressの国際化
WordPressの子テーマで翻訳多言語対応する方法 WordPress国際化(i18n)のメモ : WordPress

  1. テーマファイル内の全フレーバーテキストについて翻訳関数 __() を通す
  2. gettext ツールで .pot ファイル ( Portable Object Template: 翻訳リソースファイルテンプレート ) を作成
  3. 上記 .pot を必要に応じて修正 → リネームして .po ( Portable Object: 翻訳リソースファイル ) を作成
  4. gettextmsgfmt コマンドで .mo ファイル ( Machine Object: 実翻訳ファイル ) 作成
  5. functions.php でテーマの翻訳ファイル参照先とドメインを設定
# gettext のインストール
$ yum install -y gettext

# 子テーマ配下 PHP を読み取り .pot ファイル作成
$ cd wp-content/themes/my-child-theme
$ mkdir languages
$ find . -iname "*.php" > /tmp/phplist.txt
$ xgettext --from-code=UTF-8 --language=php --keyword=__ --keyword=_e --keyword=_n:1,2 --keyword=_x -f /tmp/phplist.txt -o ./languages/default.pot
$ rm /tmp/phplist.txt

# 上記 default.pot より各言語用 .po ファイル作成
$ cd languages
$ cp -f default.pot ja.po

# .po を編集し .mo へ変換
$ vi languages/ja.po
$ msgfmt --statistics ja.po -o ja.mo

# 子テーマ functions.php で load_theme_textdomain() を after_setup_theme フックに仕込む
$ cd ..
$ vi functions.php

翻訳最上位ドメイン default ( __() の第二引数で指定するドメイン名のデフォルト値 ) に子テーマ配下の翻訳ファイルを追加すると __('msgid', 'domain') とか指定しなくてよくなるので楽。

// 子テーマ配下 functions.php で子テーマ内翻訳ファイルを最上位ドメイン default に追加
function add_child_theme_textdomain() {
  // e.g. __('my msgid');
  load_child_theme_textdomain('default', get_stylesheet_directory().'/languages');
}
add_action('after_setup_theme', 'add_child_theme_textdomain');

マルチサイト化せずに複数 WordPress で子テーマを共有

wp-content/themes/テーマ にシンボリックリンク貼れば管理画面からテーマ選択可能。

カスタム CSV エクスポート

WordPressの簡易CSVエクスポートメモ


Built-in Classes

クラスリファレンス
Reference/Classes


Built-in Functions

関数リファレンス
Reference/Functions

クエリ/ループ操作

サブループ追加 ( WP_Query / get_posts() )

WordPressで押さえておきたい!get_posts,WP_Query,query_posts の違いと用例 WordPressで複数のループを使ってカスタム投稿一覧を自在に表示する方法

new WP_Query($args)get_posts($args) でメインループに干渉しないサブループを作成できる。昔は query_posts() というのもあったがメインループに干渉するため現在非推奨です。それぞれグローバル変数を若干汚染するのでメインループに戻る前に wp_reset_postdata() でリセット処理が必要なので注意。

$sub_query = new WP_Query(['post_type' => 'post']);
while ($sub_query->have_posts()) : $sub_query->the_post();
  the_title();
endwhile;
wp_reset_postdata();  // 必須

pre_get_posts によるアクションフックでメインループカスタマイズ

アクションフック pre_get_posts - WordPress Codex pre_get_postsを使いこなす!

/* 
 * カテゴリーページを表示した時に投稿を5件表示する
 * @param  $query - $wp_query の参照
 * @return void
 */
function set_query_for_news($query) {
  // アクションフックのタイミングが早いのでかなり慎重におろしていかないと Notice る
  if (is_admin()) return;
  if (empty(get_queried_object())) return;
  if (!$query->is_main_query()) return;
  if ($query->is_page('news')) {
  if ($query->is_category()) {  
   // クエリの where 条件を確認
    // var_dump($query->query_vars);

    // クエリの paged ( ページネーション ) 番号を取得
    $paged = get_query_var('paged', 0);

    // クエリを初期化
    // $query->init();

    // クエリの where 条件を追加
    $query->set('posts_per_page', '5');
    
    // paged 番号を引き継ぎ
    set_query_var('paged', $paged);
  } 
}
add_action('pre_get_posts', 'set_query_for_news');

get / set

Get は基本いつでも可能。

これ対して Set はビュー側 ( テンプレートファイル ) まで下りてきた $wp_query さんには聞きません。結果セットをイーガーロードせず既に保持した状態で降りてきてます。

get_query_var('page', 1);  // 第二引数はなかった時のデフォルト値
set_query_var('page', 2);  // セットもあるよ

ここは ...

// 固定ページ
$bool = is_page();
$bool = is_page('about-us');

// ページテンプレート
$bool = is_page_template('about.php');

// カテゴリー/タグ
$bool = is_category('hang-out');
$bool = is_tag('video-game');

// タクソノミー/ターム
$bool = is_tax('brand');
$bool = has_term('tiffany');

// アーカイブ
$bool = is_archive();

// 検索
$bool = is_search();

// 404
$bool = is_404();

ループの ...

// ID
the_ID();

// タイトル
the_title('<h2>', '</h2>');

// カテゴリ
$categories = get_the_category($post->ID);

// アイキャッチ画像
<?php if (has_post_thumbnail()): ?>
  <?php the_post_thumbnail('thumbnail') ?>
<?php endif ?>

WP_Post 取得

get_post() - WordPress Codex

// ID から
$page = get_post(999);

// フロントページ
$pageID = get_option( 'page_on_front' );
$frontPageObj = get_post( $pageID );

// パスから
$frontPageObj = get_page_by_path('home');

パーマリンク取得

echo get_permalink(80);
echo get_permalink(get_page_by_title('Monthly Events'));
echo get_permalink($post);

メディア

echo      wp_get_attachment_image( $attachment_id, $size, $icon, $attr );
$images = wp_get_attachment_image_src( $attachment_id, $size, $icon ); 
$images = wp_get_attachment_metadata( $attachment_id, $unfiltered );

フィルターフック

add_filter($filter_hook_name, $function_to_add, $priority, $accepted_args_number);

PLUGINS

  • show-current-template
    • 開発用:現在の適用テンプレートファイルを管理バーへ表示する
  • debug-bar
    • 開発用:管理バーにデバッグツールを表示
  • custom-post-type-permalinks
    • カスタム投稿タイプのパーマリンク設定
  • tag-groups
    • タグにグループを持たせる
  • taxonomy-terms-order
    • タクソノミ/タームに順序 ( 順番 ) を持たせる
  • megamenu
    • ドロップダウン式グローバルナビプラグイン
  • really-simple-csv-importer
    • CSV で記事アップロード
  • add-to-any
    • SNS シェアボタン追加
  • simplicity-add-fields-search-engine
    • タクソノミ/タームによる検索を可能にする野良プラグイン
  • all-in-one-seo-pack
    • Google Analytics をはじめとした SEO 最適化プラグイン
  • qTranslate
  • Allow Multiple Accounts
    • 同じEmailアドレスでユーザーを作成できるようにする
  • Google Analytics Dashboard for WP
    • ダッシュボードに GoogleAnalytics 連携
  • RSS Image Feed
    • RSS に画像をのっけるやつ
  • Simple Local Avatars
    • なんかアバター(右上のやつ)勝手に作ってくれる
  • SiteGuard WP Plugin
    • とりあえずのセキュリティ対策
    • ログイン画面 URL 変更だけは絶対やれ
  • WP Admin UI Customize
    • ロールによって管理画面をカスタマイズするやつ
  • WP Super Cache
    • サイトのキャッシュを生成して体感速度向上
    • リリース後安定してから稼働させること
  • WPFront User Role Editor
    • ユーザーに権限を設定してできることを振り分けるやつ
  • backWPup
    • バックアッププラグイン
  • Smart Custom Fields
    • カスタムフィールドこねる
  • Contact Form 7
    • メールフォーム設置プラグイン
    • バリデーションは functions.php にてフックをしかける
    • 確認画面設置用の野良プラグイン Contact Form 7 add confirm もあるよ
      • add confirm のスクロール制御
      • 複数フォームがあるときは jQuery(this).parents('form').get(0).offsetTop; でポジションとれた
      • add_filter("wpcf7c_input_error_scroll", '__return_true'); でエラー時もスクロール

TIPS

Really Simple CSV Importer で CSV アップロード頑張る

RSCSV_Import_Post_helper

複雑な処理向けに 同梱のヘルパー が提供されています。

// ヘルパーのロード ( 単体使用時 ) 
require_once ABSPATH . 'wp-content/plugins/really-simple-csv-importer/class-rscsv_import_post_helper.php';

// 投稿データの設定
$data = array('post_title' => '投稿タイトル', 'post_content' => '本文', 'post_type' => 'post', 'post_status' => 'publish'); 

// データベースに追加
$h = RSCSV_Import_Post_Helper::add($data);    

// エラーの確認
if ($h->isError()) {
  // エラーメッセージの表示
  echo implode(' ', $h->getError()->get_error_messages());
} else {
  // カスタムフィールドの追加
  $meta = array('custom_field_key' => 'Custom Field Value');
  $h->setMeta($meta);
  // タグの追加
  $tags = 'Hello, World';
  $h->setPostTags($tags);
  // タクソノミーを指定してタームの追加
  $terms = array('Lorem', 'Ipsum');
  $h->setObjectTerms('category', $terms);
  // アイキャッチ画像をアップロード
  $image_uri = 'http://example.com/example.png';
  $h->addThumbnail($image_uri);
  echo 'インポート完了。';
}

クラスのオーバライド

/**
 * Override Really Simple CSV Importer Class.
 * @param  string $class
 * @return string - Class name.
 */
add_filter('really_simple_csv_importer_class', 'really_simple_csv_importer_class');
function really_simple_csv_importer_class($class) {
  if (!class_exists('RS_CSV_Importer')) {
    return $class;
  }
  // ↓ こいつで class RS_CSV_Importer_Override extends RS_CSV_Importer {} して拡張がんばる
  require_once "path/to/RS_CSV_Importer_Override.php";
  return 'RS_CSV_Importer_Override';
}

画像 URL から attachment_id とりたい

ひろいもの

/**
 * Get attachment ID by URL.
 * @param  string $url
 * @return int    $attachment_id - Attachment ID on success, 0 on failure.
 */
function get_attachment_id($url) {
  $attachment_id = 0;
  $dir = wp_upload_dir();
  if ( false !== strpos( $url, $dir['baseurl'] . '/' ) ) { // Is URL in uploads directory?
    $file = basename( $url );
    $query_args = array(
      'post_type'   => 'attachment',
      'post_status' => 'inherit',
      'fields'      => 'ids',
      'meta_query'  => array(
        array(
          'value'   => $file,
          'compare' => 'LIKE',
          'key'     => '_wp_attachment_metadata',
        ),
      )
    );
    $query = new WP_Query( $query_args );
    if ( $query->have_posts() ) {
      foreach ( $query->posts as $post_id ) {
        $meta = wp_get_attachment_metadata( $post_id );
        $original_file       = basename( $meta['file'] );
        $cropped_image_files = wp_list_pluck( $meta['sizes'], 'file' );
        if ( $original_file === $file || in_array( $file, $cropped_image_files ) ) {
          $attachment_id = $post_id;
          break;
        }
      }
    }
  }
  return $attachment_id;
}

Smart Custom Fields との連携

CSV ファイルのヘッダー ( フィールド名 ) の接頭辞に scf_ つけてあげれば勝手に連携する。が、複数値をカンマ区切りで受け付けられない。

カスタムフィールド系の画像を URL で登録したい

サムネイルは「メディアの ID または URL」で自動取得されるが、カスタムフィールド系の画像は ID しか受け付けない。アドオン RS CSV Importer Media Add-On で一応 URL を受け付けるようになる。が、複数値をカンマ区切りで受け付けられない。

複数値のカンマ区切り受付 & SCF 連携 & 画像の URL 登録

拾い物の get_attachment_id() と組み合わせて頑張ってみた。

/**
 * Save meta filter for Really Simple CSV Importer.
 * @param  array $meta
 * @param  array $post
 * @param  bool  $is_update
 * @return array $meta
 */
add_filter('really_simple_csv_importer_save_meta', function($meta, $post, $is_update) { 
  foreach ($meta as $key => $value) {
    // Optimize for multiple values of Smart Custom Fields.
    if (mb_strpos($key, 'scf_') !== false) {
      if (mb_strpos($value, ',') !== false) {
        $metaValues = explode(',', $value);
        $meta[$key] = $metaValues;
      }
    }
    // Convert to media-id from media url.
    if (preg_match('/.*(_file|_img)$/', $key)) {
      if (is_array($meta[$key])) {
        foreach ($meta[$key] ?? [] as $metaKey => $metaValue) {
          if (parse_url($metaValue, PHP_URL_SCHEME)) {
            $meta[$key][$metaKey] = get_attachment_id($metaValue);
          }
        }
      } else {
        if (parse_url($meta[$key], PHP_URL_SCHEME)) {
          $meta[$key] = get_attachment_id($meta[$key]);
        }
      }
    }
  }
  return $meta;
}, 10, 3);

テーマ組込 jQuery の利用

WordPress の一般的なテーマ ( インストールされた直後の標準テーマも含めて ) は jQuery 依存しており 1 系の jQuery を内部的に利用している。このとき、多くのテーマは開発者に対して jQuery のバージョンを強制しない目的で $ を JavaScript グローバルにしていない。よってテーマ組込の jQuery をそのまま利用する場合は、以下のように jQuery を直接呼び出す必要がある。

<script>
  jQuery(document).ready(function(){
    console.log('hoge')
  })
</script>

ショートコードに引数を渡す

WordPressのショートコードの引数の使い方

function pass_args($attr) {
  return "引数は{$attr[0]}でした。";
}
add_shortcode('pass_args', 'pass_args');

[pass_args hoge]  // 引数はhogeでした

引数にキーを割り当て

/**
 * Hello short code.
 * @param  string $name 
 * @return string $html
 */
add_shortcode('hello', 'hello');
function hello($args) {
	echo 'Hello '.$args['name'].PHP_EOL;
}

[hello name="bob"]  // Hello bob

WordPress がデフォルトで受け付ける Querystring

WordPressのサイト上で簡単に並び替え&絞り込みする方法

//example.com/?order=ASC
//example.com/?order=ASC&orderby=title

固定ページにログインフォーム + SiteGuard プラグインの画像認証

【WordPress】WordPressでログインフォームを設置する方法

// functions.php
/**
 * ログインフォーム出力ショートコード
 * ( 固定ページ出力想定 / Site Guard CAPTCHA 対応  )
 * @param  void 
 * @return string $html 
 */
add_shortcode('print_login', 'print_login');
function print_login() {
  ob_start();
  if (is_user_logged_in()) { ?>
    <div class="card mb-5">
      <div class="card-header">ユーザログイン</div>
      <div class="card-body">
        <h5 class="card-title">既にログインしています</h5>
        <p class="card-text">ログアウトする場合は「ログアウト」ボタンを押下してください。</p>
        <a class="btn btn-primary" href="<?php echo wp_logout_url().'?redirect_to='.home_url() ?>'">ログアウト</a>
      </div>
    </div>
  <?php } else { ?>
    <div class="card mb-5">
      <div class="card-header">ユーザログイン</div>
      <div class="card-body">
        <form method="post" action="<?php echo wp_login_url().'?redirect_to='.home_url()?>'">
          <dl class="row">
            <dt class="col-md-2">ユーザー名</dt>
            <dd class="col-md-10">
              <input type="text" class="form-control" name="log" id="login_username" value="">
            </dd>
            <dt class="col-md-2">パスワード</dt>
            <dd class="col-md-10">
              <input type="password" class="form-control" name="pwd" id="login_password" value="">
            </dd>
            <dt class="col-md-2">画像認証</dt>
            <dd class="col-md-10 pt-3">
              <?php
                $SiteGuard = new SiteGuard_CAPTCHA();  // SiteGuard プラグインのキャプチャ呼び出し
                $SiteGuard->handler_login_form();      // handler_***_form() メソッドで各種フォーム呼び出し
              ?>
            </dd>
            <dt class="col-md-2"></dt>
            <dd class="col-md-10 text-right">
              <input class="btn btn-primary" type="submit" value="ログイン" />
            </dd>
          </dl>
        </form>
      </div>
    </div>
  <?php }
  $html = ob_get_contents();
  ob_end_clean();
  echo $html;
}

SCF プラグインでカスタムフィールド実装

Smart Custom Fields はカスタムフィールドを管理するシンプルなプラグインです。 Smart Custom Fields - wordpress.org

  • フィールドグループの繰り返し対応。
  • メタデータのリビジョン対応。
  • メタデータのプレビュー対応。
// Get post meta in loop
SCF::get('custom-field-name');

// Get post meta out loop
SCF::get('custom-field-name', $post->ID);

// Get user meta
SCF::get_user_meta($user_id, 'custom-field-name');

// Get term meta
SCF::get_term_meta($term->term_id, $term->name, 'custom-field-name' );

複数のタクソノミーをアーカイブに渡したい

前提として /my_category/category_a のようにアーカイブを呼び出せるような状態で ... /my_category/category_a/my_tag/tag_b みたいなルーティングでアーカイブに渡したいができないみたい。

この場合は Querystring で /my_category/category_a?my_tag=tag_b みたいに渡してあげると WP_Query が条件を絞り込んだ状態でアーカイブにセットされるようです。

で、渡ってきたクエリ条件は get_query_var() で取得可能。ちなみに set_query_var() であとからセットもできるよ。

// TEMPLATE: archive.php
// URL:      /my_category/category_a?my_tag=tag_b

echo get_query_var('my_tag', '');  // tag_b

WordPressの http://gmpg.org/xfn/11 は削除して良い

デバッグモード

開発中には、エラー(notice)を表示するように、デバッグモードにすることができる。

# wp-config.php
define('WP_DEBUG', false);
define('WP_DEBUG_LOG', true);  // wp-content/debug.log への書き出し

データベースのマイグレーション

WordPress では以下のようなデータ構造になっている。

テーブル名 対応データ 開発上の分類
wp_options 管理画面 > 一般設定など 【定数】に近い
wp_term_* 投稿タイプ/タクソノミーなど 【スキーマ = テーブル定義】に近い
wp_posts / wp_terms / wp_users 投稿/画像 ( メディア ) /ターム/ユーザなど 【ジャーナル = 履歴】に近い
wp_*meta 投稿のカスタムフィールドやユーザの権限情報など 上記【ジャーナル = 履歴】に付随するメタ情報なので【ジャーナル】に近い
wp_${プラグイン名} プラグインの設定値や履歴情報など プラグインの DB 構造によって【スキーマ】にも【ジャーナル】にもなり得る

Git などでの複数人開発時には、管理画面から行った様々な変更は全て DB 変更となるため ...

  • 管理画面の設定手順を世代管理し更新していく
  • 常に最新の DB 全体ダンプを「真」としてこれを世代管理していく
    • このとき、DB の更新 ( 管理画面からの入力 ) を行えるのは常に 1 人だけとする

上記何れかのパターンで開発を進行させるしかない。

DB 全体ダンプを「真」とする際の開発ルール

  • プロビジョンにて DB 全 DROP → 必要なデータのみ再生成 を仕込む
  • ブランチのチェックアウトでは「必ず上記プロビジョン ( vagrant reload ) による DB の初期化を行う 」こととする
  • メインコミッターは「管理画面からのデータ変更が必要な作業」全てを受け持つ
  • メインコミッターは「管理画面からのデータ変更が必要な作業」を行った場合、DB ダンプファイルを更新する
    • ダンプファイルは「全テーブルの構造 + データ」( 全 DROP なので )
    • 出荷時に必要な画像類が増えた場合は .gitignore にて無視から外す
    • 前提として出荷時に不要な wp-content/uploads 配下の画像ファイルなどは無視されていることとする
  • メインコミッター以外は「管理画面からのデータ変更が必要な作業を行わない = ソースコード変更のみ」とする
  • メインコミッター以外は「次回 vagrant up までの一時データ」としてしか「管理画面からの入力」を行わない

WordPress 関数を WordPress 外で使いたい

WP を内包するサイト内で wp-header.php ( 違ったかも ? ) を require_once() すると API 使えるようになるみたい。ブログ機能だけを分離して配下にかかえたサイト ( http://example.com/blog この /blog に WordPress が入ってるとき ) とか、この手法で /blog 配下以外で WordPress の記事データを楽に扱える。

投稿時の HTML 自動整形を制御したい

WordPressでエディタに自動生成されるタグ(<p>とか<br>)を制御する方法まとめ

Contact Form 7 プラグインでカスタムバリデート

Contact Form 7 のカスタムバリデーションの書き方(カスタマイズ方法)

/**
 * Validations for Contact Form 7.
 * @param  array $result
 * @param  array $tags
 * @return array $result
 */
add_filter('wpcf7_validate', 'wpcf7_validate_customize', 11, 2);
function wpcf7_validate_customize($result, $tags) {
  $emailInputName        = 'your-email';
  $emailConfirmInputName = 'your-email-confirm';
  $email                 = '';
  $emailConfirm          = '';
  foreach ($tags ?? [] as $tag) {
    $type = $tag['type'];
    $name = $tag['name'];
    if (empty($_POST[$name])) continue;
    $post = trim(strtr((string)$_POST[$name], "\n", ""));
    switch ($type) {
      case 'email':
      case 'email*':
        if ($name == $emailInputName)        $email        = $post;
        if ($name == $emailConfirmInputName) $emailConfirm = $post;
        break;
      case 'tel':
      case 'tel*':
        if (preg_match( '/^[0-9]*$/', $_POST[$name]) == false) {
          $result->invalidate($name, 'ハイフンは省略し数字のみでご記入ください。');
        }
        break;
    }
  }
  if ($email != $emailConfirm) $result->invalidate('your-email', '確認用メールアドレスの入力に誤りがあります。');
  return $result;
}

確認画面を追加したい

野良プラグイン Contact Form 7 add confirm

メールの SMTP 転送設定

http://www.butlerblog.com/2013/12/12/easy-smtp-email-wordpress-wp_mail/

// functions.php
/**
 * This function will connect wp_mail to your authenticated
 * SMTP server. This improves reliability of wp_mail, and 
 * avoids many potential problems.
 */
add_action('phpmailer_init', 'send_smtp_email');
function send_smtp_email($phpmailer) {
  if (WP_DEBUG) {
    $phpmailer->isSMTP();
    $phpmailer->Host       = 'smtp.example.com';
    $phpmailer->SMTPAuth   = false;
    $phpmailer->Port       = '25';
    $phpmailer->Username   = '';
    $phpmailer->Password   = '';
    $phpmailer->SMTPSecure = '';
    $phpmailer->From       = 'no-reply@example.test';
    $phpmailer->FromName   = 'no-reply@example.test';   
  } else {
    $phpmailer->isSMTP();
    $phpmailer->Host       = 'smtp.example.com';
    $phpmailer->SMTPAuth   = true;
    $phpmailer->Port       = '25';
    $phpmailer->Username   = 'username';
    $phpmailer->Password   = 'password';
    $phpmailer->SMTPSecure = '';
    $phpmailer->From       = 'no-reply@example.com';
    $phpmailer->FromName   = 'no-reply@example.com';
  }
}

Security for WordPress

WordPress の安全性を高める
WordPressセキュリティ強化の基本 WP4.7対応

WordPress は世界的に広く利用されている CMS なことに加えて、インストール直後の初期設定状態が脆弱で攻撃の対象になりやすい。

本体/テーマ/プラグインのアップデートは必ず行う

本体のセキュリティアップデートは比較的頻繁に行われている。プラグインは開発者・利用者が多くアップデートの頻繁なものを選択するべき。

アップデートによりベンダーファイルは随時更新されてしまうので、カスタマイズの際は「子テーマ」などカスタマイズ用に用意されている枠の中で行うべき ( ベンダーファイルへの直接編集は NG ) 。

XSS 対策

複雑な WordPress のエスケープ関数を整理してみる

echo esc_html($post->post_content);

ログインページの URL を必ず変更する

WordPress は初期状態だと /admin/login でログインページ ( wp-login.php ) へリダイレクトされる。攻撃者はアプリケーションが WordPress を利用したサイトだと確認した場合、前述した特徴から容易にログインページへ遷移し総当たり攻撃を仕掛けられる。

SiteGuard WP Plugin などを利用して、ログインページの URL の変更と上記リダイレクトさせない設定にするべき。

wp-config.php を安全にする

DB のユーザ名/パスワードなどを記載することになる wp-config.php は普通にインストールすると Public な領域に設置されるため 必ず施策を行うべき。 WordPress 2.9 移行は「一つ上の階層に移動させる」ことが可能 ( WordPress 本体が探しに行ってくれる ) 。しかし上の階層がドキュメントルート配下なら意味がないので、別途 .htaccess などでファイル参照をブロックすること。

# .htaccess

<files wp-config.php>
order allow,deny
deny from all
</files>

wp-includes を安全にする

注: 下記のコードが WordPress によって上書きされないようにするためには、.htaccess ファイルの # BEGIN WordPress と # END WordPress タグの外側に書き込んでください。この2つのタグの間に何かを書き込むと WordPress が上書きしてしまうことがあります。 - WordPress Codex

.htaccess でリダイレクトルールを設定。

# Block the include-only files.
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^wp-admin/includes/ - [F,L]
RewriteRule !^wp-includes/ - [S=3]
RewriteRule ^wp-includes/[^/]+\.php$ - [F,L]
RewriteRule ^wp-includes/js/tinymce/langs/.+\.php - [F,L]
RewriteRule ^wp-includes/theme-compat/ - [F,L]
</IfModule>
# BEGIN WordPress

ピンバック/トラックバック ( Pingback ) が DDoS 攻撃対象になる?

WordPressを踏み台にするPingback機能を悪用したDDoS攻撃への対応方法 WordPressのxmlrpc.phpを無効にして不正アクセス対策 wordpressのxmlrpc.phpに対するブルートフォースアタックを防ぐ

functions.phpxmlrpc.php を無効化して ...

add_filter('xmlrpc_enabled', '__return_false');

.htaccess でリダイレクトルールを設定。

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^xmlrpc\.php$ "http\:\/\/0\.0\.0\.0\/" [R=301,L]
</IfModule>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment