WordPress複雑度ガイド: レンダリングコールバック実装編

要約

関数のコントロールフローにおける独立パスの数を示すサイクロマティック複雑度は、WordPressのレンダリングコールバックでは想像以上に高くなりやすいものです。エンタープライズ型の自動生成ツールはこの数値をさらに押し上げます。実装の場面で正確に読み取り、いつ再構成すべきか判断するための指針を、具体的な案件から学びます。

ぬくもりのある午後の光が差し込む、PHPコードをぼかして表示するノートパソコンが置かれた木製の開発者デスク: WordPressワークショップの雰囲気

サイクロマティック複雑度とWordPressのレンダリングコールバック

サイクロマティック複雑度は、関数内を通過する独立したパスの数を計測する指標です。WordPressのレンダリングコールバックではこの数値が、ほとんどの開発者の予想より早く高くなります。条件分岐が3つ、ブロック変異に対するスイッチが1つ、内部ブロックのループが1つあるだけで、気づかないうちに複雑度12から14に達することもあります。それが自動的に「間違い」とは限りません。しかし、数値を正しく読み取った上で、再構成するか、そのまま運用するか、手を引くかを判断する必要があります。

この指標との出会いは、フリーランスの間では二つの極端なパターンになります。一つは、小規模クライアントプロジェクトではツール自体を実行しないため、数値を知ることがない場合。もう一つは、Javaの企業向けスタイルガイドをそのまま流用し、文脈を無視して「複雑度10以下」という天井を教義のように扱う場合です。どちらもWordPressのテーマには適切ではありません。本稿では3つの実装パターンを取り上げます。自分たちのレンダリングコールバックで複雑度が何を計測しているのか、なぜAIパターン生成ツールはこの数値をより高く押し上げるのか、そしてすべての関数を細断化した小さなヘルパーの迷路に変えないまま、この数値を正しく読み解く方法です。

WordPress レンダリングコールバックでサイクロマティック複雑度が計測するもの

トマス・マッケイブが1976年に定義したこの指標は、プログラムのコントロールフローグラフを通過する線形独立パスの数です。平たく言えば、1で始まり、ifelseifcaseforforeachwhilecatch、および論理演算子(&&||)が実行分岐を生む場合に1ずつ加えていきます。is_admin() をチェックし、$attributes['items'] をループしてから、layout 属性でスイッチする一般的なレンダリングコールバックなら、データに手を加える前にすでに複雑度5を超えています。

この指標で新しい開発者が引っかかる点は二つあります。一つ目は、複雑度が長さではなく分岐を計測することです。条件分岐のない200行の関数は複雑度1です。一方、5つのネストされた三項演算子を含む15行の関数は複雑度8になります。二つ目は、複雑度がコードの読みやすさを計測しないことです。これが計測するのは、あるパスの完全なテスト覆率に必要なテストケース数の目安にすぎません。複雑度12の関数は12個の異なるテストケースで、すべてのパスを実行する必要があります。しかし、WordPressの典型的なクライアント案件はテストケースをゼロで出荷されます。

この最後の点が本当に価値のある気づきです。小規模なフリーランス保守契約では、誰もホームページのヒーローブロック用に12個のテストケースを書きません。実際に起こるのは、クライアントが自分たちが使う3つか4つのパスをクリックして確認してから「完了」と呼ぶ、そして他の8つのパスはテストされないまま放置され、6か月後に誰も実行したことのないパスについてサポートチケットが舞い込むパターンです。サイクロマティック複雑度はスタイルの好みではありません。それはおおよそ、自分たちのコードをどれだけ本当に検証したのか、それとも運に任せているのかの指標です。

AIが生成するブロックパターンがこの指標で高い値を示す理由

これはほとんどの複雑度解説で省略される部分です。なぜなら、それらはバックエンドエンジニア向けに書かれたもので、Gutenbergパターンをクライアントに配送する人たち向けではないからです。

PatternForgeを含むAIパターン生成ツールは、複雑度を3つの特定かつ予測可能な方法で高くします。

レスポンシブなブレークポイントロジックが、ヘルパー関数に抽出されずネストされた条件分岐として書かれます。RTLのミラーリングはほぼすべてのレイアウト決定に方向チェックを追加し、二言語テーマの分岐数を二倍にします。そして、動的コンテンツブロック(ポストループ、クエリバリエーション、条件付きCTA)は、レンダリングコールバック内に if チェーンを積み重ねます。これは、自己完結したコールバックが最初の1回で正しく生成されやすいからです。

PatternForge を使用して2026年6月に11個のプロンプトに対して実装を行い、生成されたレンダリングコールバックをPHPMDで計測しました。テスト対象は、シンプルな推薦カードから、フィルタ機能付きの完全なポストグリッドまでです。中央値は複雑度9でした。推薦カードは複雑度4で収まりました。最も条件ロジックが多いフィルタ付きポストグリッドは複雑度19に達しました。これはPatternForgeに固有の欠陥ではありません。同じフィルタ付きグリッドプロンプトに対するElementor AIの出力は、同じ計測で複雑度21を記録しました。どちらもクライアントサイトに配送する前に人間が一度は読むべき水準を超えています。

サイクロマティック複雑度スコアの4つのバンドを表すインフォグラフィック: シンプルで低リスクから、テスト不可能で再構成が必要な範囲まで

スコアを読み取る: 1から10、11から20、50以上が実際に意味するもの

以下の基準は恣意的ではありません。これらはマッケイブのNIST準拠の元々の推奨に遡り、今なお指標を配布しているすべての静的解析ベンダーに繰り返されています。

ターミナルから出ることなく計測する

複雑度を計測するために高額なSaaS ダッシュボードは必要ありません。3つのツールがほぼすべてのWordPressフリーランス環境をカバーしています。

PHPMD はボックスから複雑度計測ルールを出荷します。テーマの inc/ または blocks/ ディレクトリを指すだけで、設定した閾値(10は理にかなったデフォルト)を超えるものすべてにフラグを立てます。PHPCSGeneric.Metrics.CyclomaticComplexity スニフを通じて同じカバレッジを提供し、プロジェクトがWordpress Coding Standards 用にPHPCS をすでに実行していて、第二のツールを追加したくない場合に有用です。churn-php は異なるアングルを取ります。複雑度とgitコミット頻度を相互参照するので、複雑でかつ頻繁に触れられるクラスを浮かび上がらせ、複雑度単独の指標より「まずこれをリファクタリングせよ」というシグナルが鮮明です。

これらの中で、Composer以上のビルドステップを必要とするものはありません。プリコミットフックでPHPMDを実行し、閾値を超えたコミットを失敗させれば、その問題はクライアント レビューに到達することなく済みます。

複数のクライアントテーマを同時に運用するエージェンシーの場合、同じPHPMDルールセットは、ローカルだけでなくCI にも属すべきです。phpmd blocks/,inc/ text phpmd-ruleset.xml をすべてのプルリクエストで実行するGitHub Actionsステップは、ビルドごとに数秒のコストで済みますが、ソロレビューを常に逃す問題をキャッチしてくれます。小さなヘルパー関数が複雑度4で始まり、「あともう1つ条件を追加するだけ」というプルリクエスト4つを経て17に到達し、坂を下ったことに誰も気づかないパターンです。コミット履歴には増分が表示されます。プルリクエストの差分は決して全体を表示しません。

メカニカルキーボードでタイピングする手のクローズアップ、背景に複雑度スキャン実行中のターミナルがボケて表示

2026年WordPressコアが静的解析に対して取る立場

コア自体はまだ追いついていません。コアチームのPHPStan提案はコアワークフローで静的解析を形式化しますが、ターゲットは型安全性とデッドコードです。複雑度の上限を強制するtracチケットはなく、複数のコアレンダリングコールバック(render_block_core_query を例に)は、どう計測してもすでに20を遙かに超えています。コアは保守性を複雑度を読むことより優先します。これは、その規模での防御可能なトレードオフですが、3人の開発者と回帰テストスイートがないクライアントテーマでコピーすべき言い訳ではありません。

二言語対応のケース: ENとARのブロックテーマのレンダリングコールバックを整理する

クライアントが二言語ニュースサイト(英語とアラビア語)を運営しており、Polylangを使ったブロックテーマで構築されています。ホームページの主要記事ブロックのレンダリングコールバックは、投稿タイプの切り替え、RTLレイアウトのミラーリング、3つのカードサイズ、フィーチャ画像がない場合のフォールバック、そしてピン留めされた記事の手動オーバーライドを処理していました。複雑度は23でした。

修正は全面的な書き直しではありませんでした。RTL ミラーリングを独自の関数(get_card_direction_class())に抽出し、カードサイズロジックを小さなマッチ式に引きました。フォールバックとピン留めロジックは元の場所に残しました。なぜなら、さらに分割すると2つの小さな関数間で5つのパラメータを渡す必要になり、可読性利得がないからです。最終的な複雑度:メインコールバックが11、抽出されたヘルパーが3。14分の仕事で、Polylangが標準で配布する9つの全ロケール組み合わせに対してテスト完了。

このレッスンは、このクライアント1件を超えて一般化できます。RTL対応はほぼ常に、二言語WordPressプロジェクトの隠れた複雑度税です。最初のコミットから設計されることは稀だからです。それは修正として到着します。既存の条件チェーンに方向チェックをボルト止めして、また別の方向チェック、そして「ある投稿がまだアラビア語翻訳を持たない」エッジケースの3番目の方向チェックです。その懸念を早期に抽出して、独自の小さな関数として、独自の名前を付ければ、英語のみのフィーチャリストが周りで増え続けても、残りのコールバックは読みやすいままです。

二言語対応のアラビア語ウェブサイトレイアウトを示す2番目のモニターがソフトフォーカスで表示されるフリーランス開発者の自宅オフィスデスク

高い複雑度が適切な場合と、停止すべきシグナル

「すべての関数は10以下に」という実務的でない忠告は後ろに置いてください。レイアウト変異が実装の要件である場合、WordPressテンプレートPHPは正当に高くなるケースがあります。theme.json 駆動のカードコンポーネントが、法的に6つのレイアウト、3つのサイズ、2つの方向をサポートする場合、考慮すべき実際の分岐があります。6つのワンライナーヘルパー関数を抽出して低いスコアを追い求め、各関数が1回しか呼ばれない場合、1つの読みやすい関数を間接参照の迷路に置き換えます。それは次の開発者にとって、より良いものではなく、より悪いものです。

実際に停止して再構成すべきシグナルは違います。複雑度が20を超えて登っていて誰も関数を完全にテストしたことがない、複雑度がクライアントが「もう1つバリエーション」を要望するたびに上昇し続ける、またはchurn-phpレポートが同じ複雑なクラスが過去10コミットのうち6つで編集されていることを示すケースです。複雑度単独は引き金ではありません。複雑度と、その関数に触れることを怖がる頻度の組み合わせです。

また、PHPの再構成がまったく間違った修正の場面もあります。クライアントのチェックアウトやカタログロジックがWooCommerceフックが清潔に運ぶよう設計されたものを超えて螺旋化した場合、時には正直な答えは別のadd_filter() 呼び出し3つの条件深くネストされるものではありません。それは、専用のe-コマースプラットフォームがWordPressプラグインスタックがこれまで実施できるより、そのロジックをより良く運ぶことをクライアントに伝えることです。

次のハンドオフの前に確認すべきこと

クライアントハンドオフの前に、blocks/inc/ に対してPHPMDを実行してください。何か破損したときだけでなく、常に実行するのです。15を超えるものにはフラグを立てて改めて読み、20を超えるものは、その要件が分岐を正当化するかどうかについて実際の会話をしてください。SonarSourceの参照ガイドによれば、すでに10を超えた関数はほとんどのチームが手で書く以上のテストケースを必要とします。これが管理すべき実際のコストです。数字そのものではなく。

パターンを生成したツール(人間でもAIでも)は、このステップをスキップできません。配送する前に、レンダリングコールバックを一度は読んでください。それが全てのプラクティスです。

よくある質問

サイクロマティック複雑度とは何ですか?
関数のコントロールフローにおける独立したパスの数を計測する指標です。if、elseif、case、for、foreach、while、catch、および論理演算子(&&、||)が分岐を生む場所に1を加えていき、複雑度が決定されます。
WordPressのレンダリングコールバックで複雑度を計測するべき理由は?
複雑度が高いほど、完全なテスト覆率に必要なテストケースが増えます。テストされないパスはバグが隠れやすくなります。小規模案件ほど、この隠れたリスクを理解することが重要です。
AIの生成ツールは複雑度が高い傾向にあるのはなぜ?
レスポンシブロジック、RTLミラーリング、動的コンテンツの分岐が、ヘルパー関数に抽出されずネストされた条件として実装されるからです。自己完結したコールバックが最初の生成で正しく動作しやすいためです。
どの複雑度が改変の必要性を示しますか?
複雑度単独ではなく、複雑度とメンテナンス頻度の組み合わせが引き金となります。複雑度20を超えながら頻繁に変更が加えられるコードは、改変を検討する値打ちがあります。
無料で使える計測ツールはありますか?
PHPMDとPHPCSがComposerで導入でき、独自の複雑度計測ルールを持っています。churn-phpは複雑度とgitコミット頻度を相互参照し、より正確なシグナルが得られます。