markdown [wordpress:note] WordPress - 博客CMS由php。 #php #wordpress #cms

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了markdown [wordpress:note] WordPress - 博客CMS由php。 #php #wordpress #cms相关的知识,希望对你有一定的参考价值。

## OVERVIEW ##
> [WordPressとは? - WordPress 超初心者講座](https://wp-exp.com/blog/wordpress/)

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

### Features
- 「記事」を「管理画面」から作成して「ユーザ画面」で表示/インデックスできる
    - 記事は「カテゴリー」や「タグ」によって分類できる
    - 記事の「投稿者」や「購読者」などのアカウント/ロール/認証機能が予め備わっている
    - 記事には「コメント」がつけられる
- 記事ではない静的なページを「固定ページ」として登録できる
- WEB サイトとしてのビジュアルや機能について「テーマ」概念で拡張できる
    - テーマ内で「メニュー」「ウィジェット」「背景」などの登録をして WEB サイトを構築できる
    - 各ベンダーの提供テーマを独自拡張する「子テーマ」を作成できる
    - その他 `functions.php` など ”カスタマイズ可能なファイル群” によって CSS や PHP などコードレベルで独自拡張できる
- システム的な機能拡張について「プラグイン」概念で拡張できる
- [マルチサイト化機能](http://wpdocs.osdn.jp/%E3%83%8D%E3%83%83%E3%83%88%E3%83%AF%E3%83%BC%E3%82%AF%E3%81%AE%E4%BD%9C%E6%88%90) で 1 サーバ 1 プログラムで複数サイトが構築可能
    - [複数サイト運用方法(サブディレクトリ型)](https://www.templateking.jp/practical/build/sub_dir.html)
- [ロケール対応 i18n / L10n](https://wpdocs.osdn.jp/WordPress_%E3%81%AE%E7%BF%BB%E8%A8%B3/%E5%9B%BD%E9%9A%9B%E5%8C%96%E5%AF%BE%E5%BF%9C)

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

- [WordPress.org](https://wordpress.org/)
    - [Codex - codex.wordpress.org](https://codex.wordpress.org/)
    - [Developer Resources - developer.wordpress.org](https://developer.wordpress.org/)
        - **Codex 日本語版にない情報はこっち**
        - [Themes](https://developer.wordpress.org/themes/)
        - [Plugins](https://developer.wordpress.org/plugins/)
        - [Code Reference](https://developer.wordpress.org/reference)
        - [REST API](https://developer.wordpress.org/rest-api/)
        - [WP-CLI](https://developer.wordpress.org/cli/commands/)
- [WordPress.org 日本語版](https://ja.wordpress.org/)
    - [テーマ](https://ja.wordpress.org/themes/)
    - [プラグイン](https://ja.wordpress.org/plugins/)
- [Codex 日本語版](http://wpdocs.osdn.jp/Main_Page)
    - [投稿タイプ](http://wpdocs.osdn.jp/%E6%8A%95%E7%A8%BF%E3%82%BF%E3%82%A4%E3%83%97)
    - [テンプレート階層](http://wpdocs.osdn.jp/%E3%83%86%E3%83%B3%E3%83%97%E3%83%AC%E3%83%BC%E3%83%88%E9%9A%8E%E5%B1%A4)
    - [データベース構造](https://wpdocs.osdn.jp/%E3%83%87%E3%83%BC%E3%82%BF%E3%83%99%E3%83%BC%E3%82%B9%E6%A7%8B%E9%80%A0)
    - [テンプレートタグ](http://wpdocs.osdn.jp/%E3%83%86%E3%83%B3%E3%83%97%E3%83%AC%E3%83%BC%E3%83%88%E3%82%BF%E3%82%B0)
    - [条件分岐タグ](http://wpdocs.osdn.jp/%E6%9D%A1%E4%BB%B6%E5%88%86%E5%B2%90%E3%82%BF%E3%82%B0)
    - [インクルードタグ](http://wpdocs.osdn.jp/%E6%9D%A1%E4%BB%B6%E5%88%86%E5%B2%90%E3%82%BF%E3%82%B0)
    - [フィルターフック一覧](http://wpdocs.osdn.jp/%E3%83%97%E3%83%A9%E3%82%B0%E3%82%A4%E3%83%B3_API/%E3%83%95%E3%82%A3%E3%83%AB%E3%82%BF%E3%83%BC%E3%83%95%E3%83%83%E3%82%AF%E4%B8%80%E8%A6%A7)
    - [アクションフック一覧](http://wpdocs.osdn.jp/%E3%83%97%E3%83%A9%E3%82%B0%E3%82%A4%E3%83%B3_API/%E3%82%A2%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3%E3%83%95%E3%83%83%E3%82%AF%E4%B8%80%E8%A6%A7)
    - [関数リファレンス](https://wpdocs.osdn.jp/%E9%96%A2%E6%95%B0%E3%83%AA%E3%83%95%E3%82%A1%E3%83%AC%E3%83%B3%E3%82%B9)
    - [クラスリファレンス](http://wpdocs.osdn.jp/%E3%82%AF%E3%83%A9%E3%82%B9%E3%83%AA%E3%83%95%E3%82%A1%E3%83%AC%E3%83%B3%E3%82%B9)
    - [グローバル変数](http://wpdocs.osdn.jp/%E3%82%B0%E3%83%AD%E3%83%BC%E3%83%90%E3%83%AB%E5%A4%89%E6%95%B0)
    
#### For development
- [WordPress のインストール - codex](http://wpdocs.osdn.jp/WordPress_%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB)  
- [管理画面・基本設定](https://wp-exp.com/blog/category/dashboard/)
- [テーマ](https://wp-exp.com/blog/category/theme/)
    - [テンプレート階層の基本とテーマのカスタマイズ](https://wp-exp.com/blog/template-hierarchy/)
	- [テーマをカスタマイズするときに最低限知っておきたいこと](https://wp-exp.com/blog/theme-customize-base/)
    - [ループとは](https://wp-exp.com/blog/loop/)
    - [テンプレートタグとは](https://wp-exp.com/blog/template-tag/)
    - [インクルードタグとは](https://wp-exp.com/blog/include-tag/)
    - [条件分岐タグとは](https://wp-exp.com/blog/conditional-tag/)
- [プラグイン](https://wp-exp.com/blog/category/plug-in/)
    - [フィルターフックとアクションフック](http://liginc.co.jp/web/wp/customize/92030)


### Tips

#### ロケール対応
> [ロケール対応 i18n / L10n](https://wpdocs.osdn.jp/WordPress_%E3%81%AE%E7%BF%BB%E8%A8%B3/%E5%9B%BD%E9%9A%9B%E5%8C%96%E5%AF%BE%E5%BF%9C)  
> [WordPressの国際化](https://geek-memo.com/multilanguage/)  
> [WordPressの子テーマで翻訳多言語対応する方法](http://hirsky.com/363.html)
> [WordPress国際化(i18n)のメモ : WordPress](https://www.findxfine.com/programming/wp/995559497.html)

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

```sh
# 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')` とか指定しなくてよくなるので楽。

```php
// 子テーマ配下 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エクスポートメモ](https://qiita.com/xylitol45@github/items/cbda18e55c857ed9bb74)


-------


## Built-in Classes ##
> [クラスリファレンス](http://wpdocs.osdn.jp/%E3%82%AF%E3%83%A9%E3%82%B9%E3%83%AA%E3%83%95%E3%82%A1%E3%83%AC%E3%83%B3%E3%82%B9)  
> [Reference/Classes](https://developer.wordpress.org/reference/classes/)

- [WP_Post](http://wpdocs.osdn.jp/%E3%82%AF%E3%83%A9%E3%82%B9%E3%83%AA%E3%83%95%E3%82%A1%E3%83%AC%E3%83%B3%E3%82%B9/WP_Post)
    - テンプレートより `$post` でアクセス
- [WP_Query](http://wpdocs.osdn.jp/%E9%96%A2%E6%95%B0%E3%83%AA%E3%83%95%E3%82%A1%E3%83%AC%E3%83%B3%E3%82%B9/WP_Query)
    - テンプレートより `$wp_query` でアクセス
    - [get_queried_object()](https://wpdocs.osdn.jp/%E9%96%A2%E6%95%B0%E3%83%AA%E3%83%95%E3%82%A1%E3%83%AC%E3%83%B3%E3%82%B9/get_queried_object) で現在クエリの `$WP_Post / $WP_Term` など取得可能
- [WP_Term](https://developer.wordpress.org/reference/classes/wp_term/)


-----


## Built-in Functions ##
> [関数リファレンス](https://wpdocs.osdn.jp/%E9%96%A2%E6%95%B0%E3%83%AA%E3%83%95%E3%82%A1%E3%83%AC%E3%83%B3%E3%82%B9)  
> [Reference/Functions](https://developer.wordpress.org/reference/functions/)

### クエリ/ループ操作

#### サブループ追加 ( WP_Query / get_posts() )
> [WordPressで押さえておきたい!get_posts,WP_Query,query_posts の違いと用例](https://www.m-hand.co.jp/program/4119/)
> [WordPressで複数のループを使ってカスタム投稿一覧を自在に表示する方法](https://oxynotes.com/?p=1496)

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

```php
$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](https://wpdocs.osdn.jp/%E3%83%97%E3%83%A9%E3%82%B0%E3%82%A4%E3%83%B3_API/%E3%82%A2%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3%E3%83%95%E3%83%83%E3%82%AF%E4%B8%80%E8%A6%A7/pre_get_posts)
> [pre_get_postsを使いこなす!](http://emiac-works.com/coding/pre-get-posts-code-snipet/)

```php
/* 
 * カテゴリーページを表示した時に投稿を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` さんには聞きません。結果セットをイーガーロードせず既に保持した状態で降りてきてます。

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

### ここは ... 
```php
// 固定ページ
$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();
```


### ループの ... 
```php
// 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](https://wpdocs.osdn.jp/%E9%96%A2%E6%95%B0%E3%83%AA%E3%83%95%E3%82%A1%E3%83%AC%E3%83%B3%E3%82%B9/get_post)

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

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

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

### パーマリンク取得
```php
echo get_permalink(80);
echo get_permalink(get_page_by_title('Monthly Events'));
echo get_permalink($post);
```

### メディア
```php
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 );
```

### フィルターフック
```php
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
    - [多言語化プラグイン](https://www.webcreatorbox.com/tech/qtranslate-multilingual-wordpress-plugin)
- 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  のスクロール制御](https://zarigani-design-office.com/technology/cf7_add-confirm_scroll/)
        - 複数フォームがあるときは `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 
> 複雑な処理向けに [同梱のヘルパー](https://github.com/hissy/rs-csv-importer/blob/master/class-rscsv_import_post_helper.php) が提供されています。

```php
// ヘルパーのロード ( 単体使用時 ) 
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 'インポート完了。';
}
```

#### クラスのオーバライド
```php
/**
 * 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 とりたい
> [ひろいもの](https://wpscholar.com/blog/get-attachment-id-from-wp-image-url/)

```php
/**
 * 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](https://wordpress.org/plugins/rs-csv-importer-media-addon/) で一応 URL を受け付けるようになる。**が、複数値をカンマ区切りで受け付けられない。**

#### 複数値のカンマ区切り受付 & SCF 連携 & 画像の URL 登録
拾い物の `get_attachment_id()` と組み合わせて頑張ってみた。

```php
/**
 * 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 を直接呼び出す必要がある。

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

### ショートコードに引数を渡す
> [WordPressのショートコードの引数の使い方](https://whitebear-seo.com/wordpress-shortcode-parameter/)

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

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

#### 引数にキーを割り当て
```php
/**
 * 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のサイト上で簡単に並び替え&絞り込みする方法](https://magnets.jp/web_design/3124/)

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

### 固定ページにログインフォーム + SiteGuard プラグインの画像認証
> [【WordPress】WordPressでログインフォームを設置する方法](http://ku-su.com/%E3%80%90wordpress%E3%80%91wordpress%E3%81%A7%E3%83%AD%E3%82%B0%E3%82%A4%E3%83%B3%E3%83%95%E3%82%A9%E3%83%BC%E3%83%A0%E3%82%92%E8%A8%AD%E7%BD%AE%E3%81%99%E3%82%8B%E6%96%B9%E6%B3%95/)

```php
// 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](https://ja.wordpress.org/plugins/smart-custom-fields/)

- フィールドグループの繰り返し対応。
- メタデータのリビジョン対応。
- メタデータのプレビュー対応。

```
// 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()` であとからセットもできるよ。

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

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

### http://gmpg.org/xfn/11 は殺す
> [WordPressの http://gmpg.org/xfn/11 は削除して良い](https://qiita.com/setouchi/items/6e6ae19b2b96a6ff6279)

### デバッグモード
開発中には、エラー(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>)を制御する方法まとめ](https://webkikaku.co.jp/blog/wordpress/wordpress-automatic-forming-control/)

### Contact Form 7 プラグインでカスタムバリデート
> [Contact Form 7 のカスタムバリデーションの書き方(カスタマイズ方法)](https://qiita.com/kd9951/items/1717fc384c16e00d6458)

```php

/**
 * 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 ](https://netaone.com/wp/contact-form-7-add-confirm/)


### メールの SMTP 転送設定
> http://www.butlerblog.com/2013/12/12/easy-smtp-email-wordpress-wp_mail/

```php
// 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 の安全性を高める](https://wpdocs.osdn.jp/WordPress_%E3%81%AE%E5%AE%89%E5%85%A8%E6%80%A7%E3%82%92%E9%AB%98%E3%82%81%E3%82%8B)  
> [WordPressセキュリティ強化の基本 WP4.7対応](http://shared-blog.kddi-web.com/webinfo/211)

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

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

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

### XSS 対策
> [複雑な WordPress のエスケープ関数を整理してみる](https://www.mirucon.com/2017/07/11/the-complex-wordpress-escaping/)

```
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攻撃への対応方法](https://www.wapoo-custom.com/knowledge/wordpress-pingback-ddosatack/)
> [WordPressのxmlrpc.phpを無効にして不正アクセス対策](http://wordpress-customize.seesaa.net/article/428706593.html)
> [wordpressのxmlrpc.phpに対するブルートフォースアタックを防ぐ](http://denshikousaku.net/protect-wordpress-xmlrpc-php-from-brute-force-attack)


`functions.php` で `xmlrpc.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>
```

以上是关于markdown [wordpress:note] WordPress - 博客CMS由php。 #php #wordpress #cms的主要内容,如果未能解决你的问题,请参考以下文章

markdown PHPExcel Notes和代码片段

markdown [注意:HTML] #notes #html

markdown SEO调整note.md

markdown elixir_note.md

markdown _notes.md

markdown Appium note