WordPress：固定ページ内の<script>タグでもショートコードを使えるようにする方法

WordPressの固定ページ内で`<script>`タグを使用する際、ショートコードが正しく展開されないという問題に遭遇することがあります。この記事では、この問題の解決方法とレンタルサーバーに依存しない安全な実装方法を解説します。

## 問題の例

例えば、以下のようなショートコードを設定していたとします：

```php
//shortcodes.php または functions.php
$urls = array(
  'topthemeurl' => 'https://example.com/wp-content/themes/your-theme',
  'commonsurl' => 'https://example.com/wp-content/themes/your-theme/commons'
);

function register_shortcodes() {
  global $urls;
  
  foreach ($urls as $key => $value) {
    add_shortcode($key, function () use ($value) {
      return $value;
    });
  }
}
```

固定ページ内で以下のように記述した場合：

```html
<link rel="preload" href="[topthemeurl]/js/script.js" as="script">
<script src="[topthemeurl]/js/script.js"></script>
```

`<link>`タグではショートコードが正しく展開されますが、`<script>`タグでは展開されないという問題が発生します：

```html
<!-- 結果（view-source） -->
<link rel="preload" href="https://example.com/wp-content/themes/your-theme/js/script.js" as="script">
<script src="[topthemeurl]/js/script.js"></script>
```

## 解決方法1: DOMDocument を使用する方法

この問題を解決するには、`functions.php`に以下のコードを追加します：

```php
//scriptタグ内のショートコードを処理する
add_filter('the_content', 'process_shortcodes_in_script', 99);

function process_shortcodes_in_script($content) {
    if (false === strpos($content, '<script')) {
        return $content;
    }

    //DOMDocumentを使用してHTMLを解析
    $dom = new DOMDocument();
    //HTML5のタグを適切に処理するための設定
    libxml_use_internal_errors(true);
    $dom->loadHTML(mb_convert_encoding($content, 'HTML-ENTITIES', 'UTF-8'), LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
    libxml_clear_errors();

    //scriptタグを検索
    $scripts = $dom->getElementsByTagName('script');
    
    //各scriptタグに対して処理
    foreach ($scripts as $script) {
        //src属性を取得
        $src = $script->getAttribute('src');
        if ($src) {
            //src属性内のショートコードを処理
            $new_src = do_shortcode($src);
            $script->setAttribute('src', $new_src);
        }
    }

    //変更を適用したHTMLを返す
    $new_content = $dom->saveHTML();
    
    //余分なタグを削除
    $new_content = preg_replace('/<(!DOCTYPE|html|body).*?>|<\/(html|body)>/', '', $new_content);
    
    return $new_content;
}
```

**注意点**: この方法は一部のレンタルサーバー（カラフルボックスなど）では動作しますが、エックスサーバーやヘテムルなどの一部のサーバーでは、エラー処理が厳格なため動作しない場合があります。




## 解決方法2: 正規表現を使用する方法（サーバー互換性向上版）

より多くのレンタルサーバーで安定して動作する改良版コードを紹介します。この方法では、DOMDocumentではなく正規表現を使用しています：

```php
/* ページ内の<script>タグでもショートコードを処理できるようにする */
function process_shortcodes_in_script($content) {
  //高速チェック - scriptタグが無ければ処理しない
  if (false === strpos($content, '<script')) {
    return $content;
  }

  //正規表現を使用してscriptタグのsrc属性を見つけてショートコードを処理
  $pattern = '/<script\s+[^>]*?src=(["\'])([^"\']*)\1[^>]*>/i';

  return preg_replace_callback($pattern, function($matches) {
    $src = $matches[2];
    //ショートコードがある場合のみ処理
    if (strpos($src, '[') !== false && strpos($src, ']') !== false) {
      $processed_src = do_shortcode($src);
      //属性値を更新
      return str_replace($src, $processed_src, $matches[0]);
    }
    //ショートコードが無ければそのまま返す
    return $matches[0];
  }, $content);
}
add_filter('the_content', 'process_shortcodes_in_script', 99);
```

## 拡張: 複数の属性に対応した統合版

より幅広い用途に対応するため、`script`の`src`属性だけでなく、他の属性（`href`、`onclick`など）でもショートコードを処理できる統合版コードを紹介します：

```php
/* HTML要素の属性内のショートコードを処理する統合関数 */
function process_shortcodes_in_html_attributes($content) {
    //高速チェック - ショートコードがなければ処理しない
    if (false === strpos($content, '[')) {
        return $content;
    }
    
    //処理する属性のリスト
    $attributes = array('src', 'href', 'onclick', 'data-src');
    
    //各属性に対して処理を行う
    foreach ($attributes as $attr) {
        //属性を含むタグを検索する正規表現
        $pattern = '/(' . $attr . ')=(["\'])([^"\']*?)\2/i';
        
        //コールバック関数で属性値内のショートコードを処理
        $content = preg_replace_callback($pattern, function($matches) {
            $attr_name = $matches[1];  //属性名
            $quote = $matches[2];      //引用符 (' または ")
            $attr_value = $matches[3]; //属性値
            
            //ショートコードが含まれる場合のみ処理
            if (strpos($attr_value, '[') !== false && strpos($attr_value, ']') !== false) {
                $processed_value = do_shortcode($attr_value);
                return $attr_name . '=' . $quote . $processed_value . $quote;
            }
            
            //ショートコードがなければそのまま返す
            return $matches[0];
        }, $content);
    }
    
    return $content;
}

//フィルターを追加
add_filter('the_content', 'process_shortcodes_in_html_attributes', 99);
```

`$attributes` 配列に処理したい属性名を追加するだけで、簡単に対象を拡張できます。例えば、`data-*`属性、`srcset`、`alt`などを追加することも可能です。

## 使用例

このコードを`functions.php`に追加後は、固定ページ内で以下のように記述できます：

```html
<link rel="preload" href="[topthemeurl]/js/script.js" as="script">
<script src="[topthemeurl]/js/script.js"></script>
<a href="[topthemeurl]/page.html">リンク</a>
<div data-src="[commonsurl]/images/photo.jpg"></div>
```

全ての属性でショートコードが正しく展開されます。

## レンタルサーバー別の動作状況

| サーバー | 方法1 (DOMDocument) | 方法2 (正規表現) | 統合版 |
|---------|---------------------|------------------|--------|
| カラフルボックス | ✅ | ✅ | ✅ |
| エックスサーバー | ❌ | ✅ | ✅ |
| ヘテムル | ❌ | ✅ | ✅ |
| ロリポップ | △ | ✅ | ✅ |
| さくらインターネット | △ | ✅ | ✅ |

※ △は設定によって動作が異なる場合があることを示します

## まとめ

正規表現を使用した方法2または統合版を使用することで、ほとんどのレンタルサーバーでWordPressの固定ページ内のHTML属性内でショートコードを使用できるようになります。これは特に以下のような場合に便利です：

- テーマのディレクトリパスを動的に管理したい場合
- マルチサイト環境でスクリプトのパスを柔軟に制御したい場合
- 下層に別のWordPressをインストールしている場合のパス管理
- JavaScriptファイルやリソースの参照を一元管理したい場合

安定性を重視する場合は、必ず方法2または統合版を使用することをお勧めします。