WordPressでのショートコードの使い方

|[../](https://qiita.com/taconana/items/3f5424f9abfc755772c2) |
|---|

WordPressにPHPのコードを差し込んで利用する方法には、ショートコードを使うのが一般的なようだ。その他にもプラグインや`<iframe>`タグの利用もあるようだ。まずは、ショートコードの使い方を段階的に試してみた。分かったことをメモしておく。

## ショートコードの基本操作

(1) function.php にハンドラ関数を定義する。wp-includes/function.php にある。実際には、直接 function.php に記述するのではなく、自身のサイト用の.phpファイルを別に作成して、function.php から include(require) して利用する。シグニチャは、一般に以下の感じになる。関数名は好きな名前でよい。引数は、HTMLの要素(element)を生成するイメージになっている。
```php
/**
 * 遷移先のHTMLやHTMLの要素(element)を生成して返す。
 * @param array $attributes 属性の連想配列
 * @param string $content 囲みショートコードの場合のテキスト
 * @param string $tag ショートコード名（複数のショートコードが同じ関数を使用する場合）
 */
function shortcode_handler_function(array $attributes, string $content = '', string $tag = ''): string;
```
(2) ショートコード名とハンドラ関数のマッピングを定義する。1つのハンドラ関数に複数のショートコード名を対応付けできる。add_shortcode() を呼び出すタイミングはいくつかある。function.php の末尾やハンドラ関数の直後に記述してもよい。add_shortcode() 関数は、wp-includes/shortcodes.php 内に定義されていて、`$shortcode_tags`という連想配列にハンドラ関数をセットしている。shortcode_handler_functionをnamespace で定義している場合は、namespace を添えて指定すること。
```php
add_shortcode('shortcode_name',  'shortcode_handler_function');
add_shortcode('shortcode_name2', 'shortcode_handler_function');
add_shortcode('shortcode_name3', 'namespace\shortcode_handler_function'); 	// namespaceありのとき
```
(3) ショートコードの呼び出しは、以下の感じになる。ショートコード名(タグ名)、属性(attribute)の並び、テキストはHTMLの要素(element)を表現するような記法で書く。ショートコードは、本来、1つのHTMLの要素(element)を生成するために用意されたものと言える。contentを挟み込むものを「囲みショートコード」と呼ぶ。ショートコードは固定ページ、投稿、ウィジェットを対象に差し込みが行える。
```
[shortcode_name att1='123' att2='abc']content[/shortcode_name]
[shortcode_name att1='123' att2='abc']
[shortcode_name]content[/shortcode_name]
[shortcode_name]
```
[shortcode_name/]や[shortcode_name att1='123' att2='abc'/]のような記法はないみたい。末尾のスラッシュは不要。ショートコード名から(2)で定義されたマッピングを経由して、(1)のハンドラ関数が呼び出される。

- 引数`$attributes`には、上記の例だと、array(`att1 => '123', 'att2' => 'abc') のような連想配列が渡される。HTMLと同様に属性(attribute)はすべて文字列として渡される。'123'も整数ではなく、文字列である。
- 引数`$content`には、contentで指定されるテキストが渡される。
- 引数`$tag`には、shortcode_nameが渡される。1つのハンドラ関数に複数のショートコード名を定義した場合に有効に使える。

(4) 固定ページ、投稿、ウィジェット以外でも、do_shortcode() コールバック関数を使えば、任意の箇所でショートコードを利用できる。このdo_shortcode() 関数は、wp-includes/shortcodes.php 内に定義されている。
```php
<?= do_shortcode("[shortcode_name]"); ?>
<?= do_shortcode("[shortcode_name style='color:red;']注意[/shortcode_name]"); ?>
```

## 例1: ショートコードの練習

自身のサイト用に mysite/shortcodeHandler.php といったファイルを作成し、wp-includes/functions.php から読み込むようにしておく。shortcodes.php が重複して読み込まれないように、require_once に変更しておこう。

```php:wp-includes/functions.php
require_once ABSPATH . '/mysite/shortcodeHandler.php';		// 末尾に追加
```

```php:wp-settings.php
require ABSPATH . WPINC . '/shortcodes.php';				// 228行目あたり
↓
require_once ABSPATH . WPINC . '/shortcodes.php';			// require_onceに変更しておく
```
自サイト用の shortcodeHandler.php にハンドラ関数を書く。また、ハンドラ関数にショートコード名を付けて登録しておく。1つのハンドラ関数に複数のショートコードを定義できる。namespace を指定して、構造化した方がよいと思う。テスト用の`test`というショートコードとphpinfo() のINFO_VARIABLESを表示する`variables`というショートコードを作ってみる。ハンドラ関数の直後に add_shortcode() でショートコードを定義する。
```php:mysite/shortcodeHandler.php
<?php
namespace mysite;
require_once ABSPATH . '/wp-includes/shortcodes.php';

/**
 * テスト用
 * @return string
 */
function test(): string { 
  return '<p>こんにちは、ショートコードのテストです。</p>';
}
add_shortcode('test', 'mysite\test');

/**
 * phpinfoのINFO_VARIABLESを表示する
 * @return string
 */
function dumpVariables(): string {
  ob_start();
  phpinfo(INFO_VARIABLES);
  return ob_get_clean();
}
add_shortcode('variables', 'mysite\dumpVariables');
```
namespace（上記の例ではmysite）を使うときは、ハンドラ関数に添えて定義すること。これらのショートコードを投稿ページから呼び出してみよう。投稿一覧から、適当な投稿を1つ開き、ダイレクトに以下の2行を追記する。ビジュアルエディタでもコードエディタでも構わない。'[ ]'では挟めば、ショートコードの呼び出しと判定してくれる。
```
[test]
[variables]
```
編集した投稿ページを「更新」し、「投稿を表示」する。「こんにちは、ショートコードのテストです」とphpinfo()の一部が表示されるはずである。


## 例2: HTMLの要素(element)を生成する例

HTMLの要素(element)を生成するようなショートコードも書いてみる。例えば、`<a>`タグや`<input>`タグを生成できるようなショートコードを作成してみる。`<select>`タグにも挑戦してみる。
```
[a href='mysite/dummy1.php']登録[/a]
[button onclick="location.href='mysite/dummy2.php'" value='更新']
[select array='test_array2' value='20' onchange="location.href='mysite/dummy3.php'"]
```
自サイト用の shortcodeHandler.php にハンドラ関数を追加する。HTMLのAttributesとElementを処理するメソッドを作成し、createLink()、createInput()、createSelect()を作成しておく。createInput()は、typeを指定して、button,submit,checkbox,radioなどに利用できる。createSelect()では、呼び出す前に事前にoptionの配列を生成して変数に代入しておく必要がある。例では、`$selectMap['test_array2']`に事前にoptionの配列をセットしている。
```php:mysite/shortcodeHandler.php
<?php
namespace mysite;
require_once ABSPATH . '/wp-includes/shortcodes.php';

function createAttributes(array $attributes): string {
  $s = '';
  foreach ($attributes as $k => $v){
    $s .= ' ' . $k . '="' . $v . '"';
  }
  return $s;
}

function createElement(array $attributes, string $content = '', string $tag = 'a'): string {
  return '<' . $tag . createAttributes($attributes) . '>' . $content . '</' . $tag . '>';
}

/**
 * リンク<a>を生成する
 * @param array $attributes 属性
 * @param string $content テキスト
 * @return string
 */
function createLink(array $attributes, string $content = ''): string {
  return createElement($attributes, $content, 'a');
}
add_shortcode('a', 'mysite\createLink');

/**
 * ボタンなどのエレメント<input type='xxx'>を生成する
 * @param array $attributes 属性
 * @param string $content テキスト
 * @param string $type ショートコード名(inputのtype属性)
 * @return string
 */
function createInput(array $attributes, string $content = '', string $type = 'button'): string {
  $attributes['type'] = $type;
  return createElement($attributes, $content, 'input');
}
add_shortcode('checkbox', 'mysite\createInput');
add_shortcode('button', 'mysite\createInput');
add_shortcode('submit', 'mysite\createInput');
add_shortcode('radio', 'mysite\createInput');

/**
 * メニュー<select><option value='10' selected='selected'>AAA</option></select>を生成する
 * @param array $attributes 属性
 * @return string
 */
function createSelect(array $attributes): string {
  global $selectMap;
  $array = $selectMap[$attributes['array']];  // optionリスト
  if (is_array($array)){
    $selectedValue = $attributes['value'];    // 選択されたoptionのvalue
    unset($attributes['array']);
    unset($attributes['value']);
    $content = '';
    foreach ($array as $p){
      if (is_array($p)){
        $value = $p[0];
        $text = $p[1];
      } else {
        $value = $p;
        $text = $p;
      }
      $selected ='';
      if ($value == $selectedValue){
        $selected = ' selected="selected"';
      }
      $content .= '<option' . $selected . ' value="'  .$value  . '">' . $text . '<option>';
    }
    return createElement($attributes, $content, 'select');
  }
  return '';
}
add_shortcode('select', 'mysite\createSelect');

$selectMap = array();
$selectMap['test_array1'] = [ 10, 20, 30 ];
$selectMap['test_array2'] = [ [10, 'AAA'], [20, 'BB'], [30, 'CCCC'] ];
```
投稿ページなどに以下のように記述しておくと、
```
[a href='mysite/dummy1.php']登録[/a]
[button onclick="location.href='mysite/dummy2.php'" value='更新']
[select array='test_array2' value='20' onchange="location.href='mysite/dummy3.php'"]
```
展開されて、以下のようなHTMLが生成される。onclickやonchange属性でlocation.hrefを使うのを避けたい場合には、`<form>`内で formaction属性を使ってもよい。
```html
<a href="mysite/dummy1.php">登録</a>

<input onclick="location.href='mysite/dummy2.php'" value="更新" type="button">
</input>

<select onchange="location.href='mysite/dummy3.php'">
  <option value="10">AAA<option>
  <option selected="selected" value="20">BB<option>
  <option value="30">CCCC<option>
</select>
```

## 例3: 任意の.phpファイルをロードする例

ハンドラ関数は、本来、HTMLの要素(element)を生成するためのシグニチャになっているが、遷移先のHTMLをごっそり生成したり、HTMLのごく一部の部品だけを生成してもよい。また、副作用のみを利用して、stringを返さなくてもよい。

ob_start()とob_get_clean()を利用して、指定の.phpファイルから遷移先のHTMLを生成する例をあげておく。loadFile()という関数にショートコード名を付けて登録しておく。名前を例えば、'load'としておこう。

```php:mysite/shortcodeHandler.php
<?php
namespace mysite;
require_once ABSPATH . '/wp-includes/shortcodes.php';

/**
 * ファイルを読み込む（テキストを返す）
 * @param array $attributes 属性 { path, echo }
 * @return string
 */
function loadFile(array $attributes): string {
  
  if (empty($attributes)){
    return '$attributes is empty at shortcodeHandler::loadFile';
  }
  if (!isset($attributes['path'])){
    return '$path is empty at shortcodeHandler::loadFile';
  }
  // 例えば、path='mysite/test.php'のような相対パスで指定する。
  $path = $attributes['path'];
  
  // 絶対パスを取得する。
  $abspath = __DIR__  . '/../' . $path;
  if (!file_exists($abspath)){
    return 'Not found ' . $abspath . ' at shortcodeHandler::loadFile';
  }
  
  // 指定の.phpを実行し、出力されたテキストを取り込んで返す。
  // .php内でechoやprintで標準出力に吐き出している場合には出力制御関数も使える。
  $echo = false;
  if (isset($attributes['echo'])){
    $s = strtolower($attributes['echo']);
    $echo = ($s == 'true' || $s == 'on' || $s == '1');
  }
  if ($echo) ob_start();
  include($abspath);
  return $echo ? ob_get_clean() : '';
}
add_shortcode('load', 'mysite\loadFile');
```

固定ページや投稿ページから呼び出すときには、以下の感じ。ショートコード名(load)とパラメータ(path)を指定する。pathで指定した.phpファイルがロードされ実行される。更新系の操作などで副作用のみで十分であれば、echoの指定は不要である。.php内でechoやprintを使用して標準出力を利用している場合、echo='true'を添えると出力したテキストが取得できる。
```
[load path='mysite/dummy1.php']
[load path='mysite/dummy2.php' echo='true']
```
このloadショートコードを埋め込んでおくと、HTMLでのアクション（`<a>`タグ、`<input>`タグ、`<select>`タグなど）とは関係なく、任意の箇所から任意の.phpを実行できる。画面遷移してもいいし、HTMLの要素を生成して差し込んでもよい。

|[../](https://qiita.com/taconana/items/3f5424f9abfc755772c2) |
|---|