AIを活用して簡単なWordPressプラグインを作る


## この開発をすることになった経緯・課題
WordPress開発において開発環境と本番環境の2環境にて開発環境→本番環境といったフローで管理画面からの変更を行うケースで、管理画面のエディタのソースコードを移動する際、**誤って開発環境の絶対パスを書いたコードを持ってきてしまう**ことが稀にあります。
これが原因で開発環境の画像やファイルを読みにいってしまって、**本番環境でBasic認証が表示されてしまう**といった事態が発生することがあり、これを解決したかったのです。

どうせならプラグインにして他のサイトでも同じように使い回せるようにしたい。
いい機会だからChatGPTに開発をまるっと任せてみようと思ったのがきっかけです。


## 開発方針
- とにかくコードは自分で書かない、極力ChatGPTのプロンプトだけでなるべく短時間で仕上げたい。

## 要件と仕様
- 本番環境でのみ動作させるプラグイン
- 管理画面に設定画面を設け、開発環境のドメイン(同じドメイン配下で/develop/などで運用している場合はドメイン/develop/)を入力して保存する
- 投稿/固定ページ編集画面で公開(更新)ボタンを押した際、設定画面から入力した文字列がエディタ内に存在する場合は公開(更新)を実施せずエラー画面に遷移させる。
- これはおまけだが、どうせ本番のみで使うならWPの表示設定で「検索エンジンがサイトをインデックスしないようにする」にチェックが入ってたら目立つようにアラートが出るようにする機能もつける。

## プラグイン動作確認済み要件
- WordPress 6.5
- PHP 8.1.29
- MariaDB 10.5.21 (mysqlnd 8.1.29)

## 成果物
とりあえず先に成果物
もし使いたい方がいればどうぞ、多分動きますが保証はしません。
適当な名前で保存して/wp-content/plugins/ディレクトリに放り込めば管理画面から有効化できます。

```php
<?php
/**
 * Plugin Name: コンテンツ検証 & Noindexアラート
 * Description: 開発環境のドメインがコンテンツ内に存在しないかを検証してエラーを出力 & Noindexモードの場合にアラートを表示します。
 * Version: 1.0
 * Author: your_name
 */

// 直接アクセスされた場合は終了。
if (!defined('ABSPATH')) {
    exit;
}

// 設定ページを追加。
function ccva_add_settings_page() {
    add_options_page(
        'コンテンツ検証設定',
        'コンテンツ検証',
        'manage_options',
        'ccva-settings',
        'ccva_render_settings_page'
    );
}
add_action('admin_menu', 'ccva_add_settings_page');

// 設定ページをレンダリング。
function ccva_render_settings_page() {
    if (!current_user_can('manage_options')) {
        return;
    }

    if (isset($_POST['ccva_strings'])) {
        check_admin_referer('ccva_save_settings');
        update_option('ccva_strings', sanitize_textarea_field($_POST['ccva_strings']));
        echo '<div class="updated"><p>設定を保存しました。</p></div>';
    }

    $ccva_strings = get_option('ccva_strings', '');
    ?>
    <div class="wrap">
        <h1>コンテンツ検証設定</h1>
        <form method="post">
            <?php wp_nonce_field('ccva_save_settings'); ?>
            <h2>開発環境のドメイン、もしくは投稿や固定ページの更新時にブロックしたい文字列を1行ごとに入力してください。</h2>
            <textarea name="ccva_strings" rows="10" cols="50" class="large-text code"><?php echo esc_textarea($ccva_strings); ?></textarea>
            
            <p><input type="submit" class="button-primary" value="設定を保存"></p>
        </form>
    </div>
    <?php
}

// 保存前にコンテンツを検証。
function ccva_validate_content($data, $postarr) {
    if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
        return $data;
    }

    // 現在のユーザーが編集権限を持っているか確認。
    if (!current_user_can('edit_post', $postarr['ID'])) {
        return $data;
    }

    $ccva_strings = get_option('ccva_strings', '');
    if (!$ccva_strings) {
        return $data;
    }

    // post_contentが存在し、文字列であることを確認
    if (!isset($data['post_content']) || !is_string($data['post_content'])) {
        return $data;
    }

    $strings = array_map('trim', explode("\n", $ccva_strings));

    foreach ($strings as $string) {
        if ($string && strpos($data['post_content'], $string) !== false) {
            ob_start();
            wp_die(
                sprintf(
                    'エラー: コンテンツにブロックされた文字列「%s」が含まれています。削除してから再度試してください。なお、入力状態を復元する場合は「バックアップから復元」をお試しください。',
                    esc_html($string)
                ),
                'コンテンツ検証エラー',
                ['back_link' => true]
            );
            ob_end_flush();
        }
    }

    return $data;
}
add_filter('wp_insert_post_data', 'ccva_validate_content', 10, 2);

// サイトがNoindexモードの場合に管理画面とサイトのwpadminbarにアラートを表示。
function ccva_noindex_alert() {
  if (get_option('blog_public') == '0') {
      
      // 管理画面の場合に通知を表示
      if (is_admin()) {
          add_action('admin_notices', function () {
              echo '<div class="notice notice-warning"><p style="font-weight:bold;color:red;"><strong>警告:</strong> サイトは現在Noindexモードになっています。検索エンジンがこのサイトをインデックスしません。</p></div>';
          });
      }

      // wpadminbarにアラートを追加
      add_action('admin_bar_menu', function ($wp_admin_bar) {
          $args = [
              'id'    => 'noindex_alert',
              'title' => '<span style="color:#ff0000; font-weight:bold;">⚠️ Noindexモード有効</span>',
              'href'  => admin_url('options-reading.php'), // 表示設定ページへのリンク
              'meta'  => [
                  'title' => '検索エンジンがインデックスしない設定になっています',
              ],
          ];
          $wp_admin_bar->add_node($args);
      }, 100);
  }
}

// サイト側で通知を表示するために 'wp_loaded' フックを使用
add_action('wp_loaded', 'ccva_noindex_alert');



```
## 実際の手順(ざっくり)
### 以下のプロンプトを入力
```
wordpressで下記の2つの機能を持ったプラグインを開発したいと思います。

【特定の文字列がエディタに含まれていたらエラーを出す機能】
1. 管理画面の専用ページから任意の文字列を入力する
2. 投稿、固定ページ、カスタム投稿それぞれの編集画面で公開ボタンを押したときに、エディタのコード内に 1. で入力されている文字列が含まれているか判定し、
含まれていたらエラーを出して更新させないようにする、
3. 2.の判定で1.の文字列が含まれていなかったら更新を実施

【noindex状態の場合に管理画面内すべてにアラート文を出力する】
管理画面 > 設定 > 表示設定 > 検索エンジンでの表示
の「検索エンジンがサイトをインデックスしないようにする」にチェックが入っている場合はアラート文を出力する
```
こんな適当な文章でも意外としっかりしたコードが出力されます。
でもコメントやボタン、出力されるメッセージが全部英語でした。
まずはこいつを翻訳してもらいましょう
```
ソースコード内のメッセージやボタンのテキスト、コメントは日本語でお願いします。
```
無事対象箇所が日本語で修正されたコードが出てきました。
### 吐き出されたコードをとりあえずWPに乗せて動かす。
そのままでは動作せずエラーになりました。
発生したエラー・挙動のおかしかった点を入力して修正してもらいましょう
```
早速有効化して、固定ページで更新ボタンを押したところfatal errorが出ました。
これを解決してもらえますか？
[エラー文をまるっと貼る]
```
対象としてたエラーは解消、だがまた別のエラーが発生。
### 吐き出されたコードをとりあえずWPに乗せて動かす。(2)
省略
### 吐き出されたコードをとりあえずWPに乗せて動かす。(3)
。。。

全部書くと長いので割愛しますが、ほぼこれで完成しました。
自分で書いたのはプラグイン名をちょろっと修正して、管理画面に表示されるエラーメッセージのスタイルを修正した程度。
方針通りに作れました。

## 完成までの所要時間
検証含めて1時間前後

## 感想
- 割としっかりセキュリティ面も担保されてるものが出てくるが、よく見るとエスケープされていないテキストをそのまま使う仕様になってたりする、ここは要注意。
- ちゃんと処理ごとにコメント書いてくれるのはうれしい。
- 以前にも何度か プラグイン作成は試しているが、一発で動くことはまずない。
- 多機能なものを一気に作るのは無理、そんな甘くない。
- 実現したいことをコンポーネント単位や機能単位でプロンプトを書いて、ソースのベースを吐き出してもらうみたいな使い方が最も良いと感じた。