Claude Media
Claude CodeでWordPressのPHP 8互換性を洗う手順 — PHPCompatibilityWP

Claude CodeでWordPressのPHP 8互換性を洗う手順 — PHPCompatibilityWP

PHPCompatibilityWPのtestVersion指定でWordPress一式を検査し、結果をプラグイン別に仕分けます。自作コードはClaude Codeで直し、配布物は更新待ちの台帳に回す流れです。

WordPressのPHPを8.xへ上げる前の下調べは、PHPCompatibilityWPで全体を検査し、結果をプラグイン単位に仕分けるところまでやっておくと、その後の判断が速くなります。自作のプラグインやテーマは直せますが、配布されているプラグインは作者の更新を待つしかないからです。この記事では、検査の実行、JSONレポートの集計、Claude Codeによる分類と修正の分担までを順に組みます。

規約(WPCS)の違反をhookで直させる話はClaude CodeのWordPress規約をphpcsのhookで直させる設定にあります。ここで扱うのはPHPのバージョン互換で、同じphpcsでも使うルールセットと見る単位が違います。

検査に使うのはPHPCompatibilityWP

PHPCompatibilityは、PHP_CodeSniffer(phpcs)用の「PHPバージョン間の互換性を調べるルール集」です。WordPress向けには派生のPHPCompatibilityWPがあります。WordPressが自前で提供するバックフィル(古いPHPに不足する関数を補う実装)やポリフィルを除外し、誤検知を減らすルールセットです。

素のPHPCompatibilityを使うと、WordPress本体が補っている関数まで「このPHPには無い」と報告される可能性があります。WordPressのコードを検査するなら、--standard=PHPCompatibilityWP を選ぶのが筋です。

導入はComposerのみがサポートされています。

composer config allow-plugins.dealerdirect/phpcodesniffer-composer-installer true
composer require --dev phpcompatibility/phpcompatibility-wp:"^3.0@dev"
vendor/bin/phpcs -i

phpcs -i の出力に PHPCompatibility と PHPCompatibilityWP が並べば、登録は成功です。PHPCompatibilityWPの要件にはPHP_CodeSniffer 3.13.3以上とPHPCompatibility 10.0.0以上が含まれます。検査用のツールは、サイト本体とは別のディレクトリにComposerプロジェクトとして置くと、本番のvendorを汚しません。

testVersionを指定しないと見落とす

testVersion なしで走らせると、報告されるのは「非推奨または削除されたPHP機能」だけです。新しいPHPにしかない機能を使ったコードの検出は、testVersion を指定して初めて有効になります。

指定の形は3通りです。

指定意味
8.3意味8.3だけを対象にする
7.4-8.3意味範囲内のどのバージョンで問題が出ても報告する
7.4-意味7.4以上すべてを対象にする

WordPressの最低PHPは、PHPCompatibilityWPのREADMEによるとWP 6.6で7.2.24、WP 7.0で7.4.0です。上げる先が8.3で、移行期間中は7.4でも動かしたいなら、7.4-8.3 のように範囲で指定します。片側しか見ないと、「8.3では動くが7.4では構文エラー」というコードを、移行の途中で踏む可能性があります。

コマンド行の testVersion は、ruleset側の指定より優先されます(PHP_CodeSniffer 3.3.0以降)。rulesetに既定値を書きつつ、特定のファイルだけ別の版で検査したいときに使えます。

レポートはJSONで受け取る

画面に出る既定の表示は、人が読むには良くても集計には向きません。--report=json なら、ファイルごとのメッセージ配列が得られます。PHP_CodeSniffer公式のReportingページが示す形は、次のとおりです。

  • files の下にファイルの絶対パスをキーとしたオブジェクトが並ぶ
  • 各ファイルに messages 配列があり、message source type(ERROR / WARNING)line column が入る
  • source はスニッフのコード(例として Generic.PHP.LowerCaseType.ParamTypeFound の形)

ファイルへ書き出すには --report-file を使います。--ignore で、検査対象外のディレクトリを除外できます。WordPress一式を丸ごと走らせるなら、依存ライブラリや生成物は外しておきます。

vendor/bin/phpcs -p --standard=PHPCompatibilityWP \
  --extensions=php \
  --runtime-set testVersion 7.4-8.3 \
  --ignore='*/node_modules/*,*/vendor/*' \
  --report=json --report-file=php8-report.json \
  /path/to/wp-content

--extensions=php は、PHPCompatibilityのスニッフがPHPコードだけを対象にするため、検査を少し速くする指定です(PHP_CodeSniffer 4.0未満ではJSとCSSも既定の対象になります)。vendor を外している点には注意が要ります。プラグイン内にバンドルされた依存ライブラリが原因の非互換は、このコマンドでは見えません。バンドル先も調べたい場合は、--ignore から */vendor/* を外して別に走らせます。

プラグイン単位に集計する

次は、絶対パスから wp-content/plugins/<名前> を取り出して数えます。jqで足りる処理なので、集計にモデルは使いません。

jq -r '
  .files | to_entries[]
  | .key as $f
  | ($f | capture("/wp-content/(?<kind>plugins|themes|mu-plugins)/(?<name>[^/]+)")? // {kind:"other", name:"-"}) as $p
  | .value.messages[]
  | [$p.kind, $p.name, .type, .source] | @tsv
' php8-report.json | sort | uniq -c | sort -rn | head -30

出力は「件数、種別(plugins / themes / mu-plugins)、名前、ERRORかWARNING、スニッフのコード」の並びになります。ここで見るのは次の2点です。

  1. ERRORは、そのPHPバージョンで実際に動かない可能性が高いもの
  2. 同じスニッフのコードが1つのプラグインに大量に出ていないか

後者は、1つの古い書き方がプラグイン全体に散っているサインです。直す場合は機械的な置換で済むことが多く、Claude Codeに任せやすい形です。

自作と配布物に仕分ける

集計表ができたら、プラグインを2つに分けます。判断の軸は「そのコードを自分たちが保守しているか」だけです。

区分見分け方非互換が出たとき
自作見分け方自社のリポジトリで管理しているプラグイン・テーマ・mu-plugins非互換が出たとき自分たちで直す
配布物見分け方WordPress.orgや販売元から入れたもの非互換が出たとき作者の更新を待ち、台帳に記録する

配布物のファイルを直接書き換える対応は、更新のたびに上書きされて消えるため、台帳の「待ち」に回すのが安全です。待っても直らない場合の選択肢は、代替プラグインへの移行か、そのプラグインが動かない前提での機能の作り直しになります。

この区分表は、Claude Codeに毎回説明するより、プロジェクトのCLAUDE.mdに書いておくと手戻りがありません。

## PHP 8 移行の区分
- 自作(直してよい): wp-content/plugins/acme-*, wp-content/themes/acme-theme, wp-content/mu-plugins
- 配布物(編集禁止): 上記以外の wp-content/plugins 配下すべて
- 配布物の非互換は修正せず、docs/php8-waiting.md の表に追記する

Claude Codeには集計表を渡して分類させる

集計の結果を渡して、台帳づくりまでをClaude Codeにやらせます。claude -p はインタラクティブ画面を開かずに1回で答えを返す実行方法で、標準入力に渡した内容を材料にできます。公式ドキュメントでは、diffをパイプで渡せばClaudeにBashの許可が要らなくなる例が示されています。集計表も同じ要領で渡せます。

jq -r '...(上の集計)...' php8-report.json | sort | uniq -c | sort -rn | \
claude --bare -p "次はPHP 8互換性検査の集計です。plugins配下を、
自作(acme-で始まるもの)と配布物に分け、配布物ごとに
ERRORの件数とスニッフのコードを表にしてください。" \
  --output-format json | jq -r '.result'

--bare は、フックやCLAUDE.md、自動メモリー、MCPサーバーの自動読み込みを省く起動モードで、スクリプトからの呼び出しで推奨されています。そのぶんCLAUDE.mdは読まれないので、区分のルールはプロンプト側に書きます。上の例でプロンプトに acme- と明記しているのは、そのためです。

機械的な後工程に結果を渡したいなら、--json-schema でスキーマを指定すると、structured_output フィールドに構造化された結果が入ります。ただし、Claudeが依頼を断った場合など、有効な出力が得られなかった実行ではこのフィールドが付きません。スクリプトで使う前に、フィールドの有無を確かめてください。

自作コードの修正は、許可の範囲を狭めて任せる

自作側は、スニッフのコードごとに直し方がほぼ決まります。許可の設定で触れる範囲を限定すると、配布物を書き換える事故を防げます。

{
  "permissions": {
    "allow": [
      "Bash(vendor/bin/phpcs *)",
      "Edit(/wp-content/plugins/acme-*/**)",
      "Edit(/wp-content/themes/acme-theme/**)"
    ]
  }
}

これは考え方を示した設定例です。許可に書いていないパスの編集は確認画面で止まるので、配布物のプラグインは書き換えられる前に気づけます。それでも止めたいときは、配布物の個別ディレクトリ名で deny を足します。許可規則の書き方は公式の許可設定ページが正で、Edit(path) の形のほか、パスの先頭が / のときは設定ファイルの置き場所を基準にした相対パスになる点が要所です。

依頼は、次のように範囲と終了条件を決めて出します。

wp-content/plugins/acme-booking を修正対象にしてください。
php8-report.json のこのプラグインのERRORだけを直し、
直したら testVersion 7.4-8.3 で phpcs を再実行してERRORが0件になることを確認してください。
配布物のプラグインは触らないでください。

再実行の結果が0件になった時点が完了の目安です。WARNINGは非推奨の警告が中心で、直すかどうかは別に判断します。

検査が拾えないものは実行時のログで補う

PHPCompatibilityの対応範囲は、公式READMEによると「PHP 5.0から最新まで」を目指す途中の状態で、網羅は100%ではないと明記されています。静的検査は、あくまで一次選別です。

検査を通した後は、PHP 8.xを入れたステージング環境で WP_DEBUG と WP_DEBUG_LOG を有効にして主要な画面を操作し、wp-content/debug.log に出る警告を確かめます。公式のデバッグ解説によれば、WP_DEBUG を有効にすると、非推奨の関数や引数に関する通知も記録されます。画面への表示は WP_DEBUG_DISPLAY を false にして止められるため、ステージングでも閲覧者に警告を見せずに済みます。

誤検知を減らす設定

使っていると、検査結果に自社の関数が拡張機能の関数として報告されることがあります。PHPCompatibility.Extensions.RemovedExtensions というスニッフは、削除された拡張機能を関数名の接頭辞で判定するため、同じ接頭辞を持つ自作関数と衝突するからです。

ruleset(phpcs.xml.dist)に、除外する関数名を書いておけます。

<?xml version="1.0"?>
<ruleset name="php8-compat">
    <rule ref="PHPCompatibilityWP"/>
    <config name="testVersion" value="7.4-8.3"/>
    <rule ref="PHPCompatibility.Extensions.RemovedExtensions">
        <properties>
            <property name="functionWhitelist" type="array">
                <element value="mysql_to_rfc3339"/>
            </property>
        </properties>
    </rule>
</ruleset>

mysql_to_rfc3339 は公式READMEに載っている例で、自社の関数名に置き換えて使います。要素を足すときは、本当に自作の関数かを確かめてからにします。誤って本物の削除済み関数を除外すると、実機で初めて落ちます。

つまずきやすい点

  • vendorを外したまま「問題なし」と判断する: バンドルされた依存の非互換は見えていません。
  • testVersionを書き忘れる: 削除・非推奨だけの報告になり、新しいPHPの構文を使うコードの検出が抜けます。
  • 集計表の分類をClaudeの判断だけに任せる: 自作かどうかは、モデルの推測ではなく、プロンプトやCLAUDE.mdに書いた区分で決めます。
  • 配布物を手で書き換える: プラグインの更新で消えます。台帳に記録して作者の更新を待ちます。

まとめ

PHP 8への移行で最初に決めるのは、非互換の数ではなく「どこを自分たちが直せるか」です。PHPCompatibilityWPのJSONレポートをプラグイン別に切り、自作は許可の範囲を絞ってClaude Codeに直させ、配布物は台帳に残して更新を待つ。この区分が先にあれば、移行の見通しは検査結果の件数に左右されません。静的検査の後はステージングのログで確かめ、規約の違反を直す話はphpcsのhookに分けておくと、検査の目的がぶれません。PHP 7から8へのコード変換そのものはRectorを使う手順が扱っています。

参照した一次ソース

この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。

  1. code.claude.com/docs/en/headless
  2. code.claude.com/docs/en/permissions
  3. github.com/PHPCompatibility/PHPCompatibility
  4. github.com/PHPCompatibility/PHPCompatibilityWP
  5. github.com/PHPCSStandards/PHP_CodeSniffer/wiki/Reporting
  6. developer.wordpress.org/advanced-administration/debug/debug-wordpress
この記事を共有:XはてブLinkedIn