この記事では、プログラムが多くの止まる Fatal Error と、1つのエラーが別のエラーを引き起こす連鎖構造 について解説します。
最後に、テーマを複製して作った場合に起きやすい「更新漏れ」のチェックリストもまとめます。

PHP8移行でWordPressテーマに出るWarning 5パターンと修正コード【実例付き】 PHP7→PHP8への移行後にWordPressテーマで実際に発生したPHP Warning 5種類を、before/afterのコード付きで解説。$postのnullチェック・get_post_meta()のfalse対応・foreachのnull引数・$_...  続きを読む

    Fatal Error:get_page_by_path() に配列を渡してしまう問題

    エラーメッセージ

    PHP Fatal error: Uncaught TypeError: urldecode(): Argument #1 ($string) must be of type string, array given in functions.php on line 361
    

    状況

    本番サーバーにデプロイしたあと、サイトが真っ白になって発覚するケースです。

    analytics.php などの共通テンプレートから、次のように関数を呼んでいる場合があります。

    // analytics.php(共通ファイル)
    if (page_is_ancestor_of(array('request', 'consult'))) {
        // Googleアナリティクスのイベント計測コードなど
    }
    

    request または consult のどちらかがURLの祖先なら」という意図で、配列で渡しています。

    なぜPHP8でFatal Errorになるか

    page_is_ancestor_of() はfunctions.phpで定義したカスタム関数で、内部で get_page_by_path() を使っています。

    function page_is_ancestor_of($slug) {
        $page = get_page_by_path($slug);  // ここに配列が渡る
        // ...
    }
    

    get_page_by_path()文字列のスラッグ を受け取る関数です。PHP7.xでは配列を渡しても内部で暗黙的に文字列変換されて動作していましたが、PHP8.xでは urldecode() が厳密な型チェックを行うようになり、配列を渡すとFatal Error になります。

    修正方法

    呼び出し側(analytics.php)は共通ファイルなので変更したくないというケースが多いです。そのため、関数側(functions.php)を配列にも対応できるよう拡張します。

    // functions.php(Before)
    function page_is_ancestor_of($slug) {
        $queried = get_queried_object();
        $page = get_page_by_path($slug);
        if (!$page) return false;
        return ($queried instanceof WP_Post && $queried->post_parent == $page->ID);
    }
    
    // functions.php(After)
    function page_is_ancestor_of($slug) {
        // 配列が渡された場合、各要素に対して再帰的にチェック
        if (is_array($slug)) {
            foreach ($slug as $s) {
                if (page_is_ancestor_of($s)) {
                    return true;
                }
            }
            return false;
        }
    
        // 文字列の場合は従来の処理
        $queried = get_queried_object();
        $page = get_page_by_path($slug);
        if (!$page) return false;
        return ($queried instanceof WP_Post && $queried->post_parent == $page->ID);
    }
    

    ポイント

    • 関数の先頭で is_array() チェックを追加する
    • 配列の場合は各要素に対して自分自身を再帰呼び出しする
    • こうすることで、文字列での呼び出しとも後方互換性を保てる

    この修正方法のメリットは、呼び出し側のコードを一切変更せずに済む点です。共通ファイルを変更すると、複数テーマ・複数ページに影響が広がるリスクがあります。


    エラーの連鎖構造:includeの失敗がsession_startを止める

    PHP8移行で発生するエラーの中には、1つの根本原因が複数のエラーを引き起こす連鎖構造 があります。これを理解していないと、見えているエラーをすべて個別に修正しようとして無駄な作業が増えます。

    実際に起きた連鎖の例

    error.logに次のようなエラーが複数出ていました。

    PHP Warning: include(css/_layouts/ly-header.css.php): Failed to open stream: No such file or directory in functions.php on line 16
    PHP Warning: session_start(): Session cannot be started after headers have been sent in functions.php on line 57
    

    2種類のWarningが出ていますが、根本原因は1つ です。

    連鎖の仕組み

    [1] functions.php: includeしようとしたCSSファイルが存在しない
            ↓
    [2] PHPが「ファイルが見つからない」という警告メッセージを出力する
            ↓
    [3] その警告テキストがHTMLより先に出力されてしまう(ヘッダー送信済み状態に)
            ↓
    [4] session_start() は「ヘッダー送信前」に呼ばれないといけないため、
        「もうヘッダーが送られてるから開始できない」というWarningが出る
    

    直すべきはCSSファイル名の参照ミス([1])だけ です。[4]のsession_startのWarningは、[1]を直せば自動的に消えます。

    原因:テーマ複製時のファイル名更新漏れ

    今回の場合、テーマを複製して新テーマを作った際に、CSSファイル名が変わっているのにコード内の参照が古いままだったのが原因でした。

    // functions.php(Before):古いテーマのファイル名が残っている
    include get_template_directory() . '/css/_layouts/ly-header.css.php';
    
    // functions.php(After):正しいファイル名に修正 + 存在チェックも追加
    $css_file = get_template_directory() . '/css/_layouts/ly-header-form.css.php';
    if (file_exists($css_file)) {
        include $css_file;
    }
    

    file_exists() チェックを追加しておくと、将来またファイル名が変わっても警告が出なくなります(includeのみが無視されます)。

    session_start() の追加対策

    根本原因(ファイル名)を修正するのが最優先ですが、念のため session_start() にも防御的なチェックを加えておくと安心です。

    // Before
    add_action('init', 'my_session_start');
    function my_session_start() {
        session_start();
    }
    
    // After
    add_action('init', 'my_session_start', 1);  // priority を1にして早めに実行
    function my_session_start() {
        if (!headers_sent() && session_status() === PHP_SESSION_NONE) {
            session_start();
        }
    }
    
    • headers_sent() でヘッダーがまだ送信されていないか確認
    • session_status() でセッションが未開始かどうか確認
    • priority1 にすることで他のフックより先に実行

    テーマ複製時の4大チェックリスト

    既存テーマをコピーして新しいテーマを作った場合、次の4点が更新漏れになりやすいです。

    チェック1:グローバル変数の定義

    // functions.php でこのような変数が定義されているか確認
    $global_session_params = array(
        'site_id' => 'my-site',
        // ...
    );
    

    複製元テーマの functions.php にあったグローバル変数定義が、新テーマに引き継がれているかを確認します。

    head.php などの共通テンプレートが global $xxxx で参照している変数は、functions.php に定義がないとWarningの原因になります。

    チェック2:CSSファイル名の参照

    テーマのCSSファイル(css/_layouts/ 内など)のファイル名を変えた場合、コード内の参照箇所をすべて更新する必要があります。

    検索すべき箇所:

    • functions.phpwp_enqueue_style()include
    • head.php
    • footer-*.php
    • below-fold.php などJSローダーファイル

    チェック3:テンプレートのファイルパス参照

    // functions.php
    add_feed('feed-rss2', function() {
        load_template(get_template_directory() . '/commons/feed-rss2.php');  // 正しいパス
        // load_template(get_template_directory() . '/parts/feed-rss2.php');  // 古いパス
    });
    

    parts/commons/ など、テーマのディレクトリ構造が複製元と異なる場合、ファイルパスの参照も更新が必要です。

    チェック4:関数の引数型

    page_is_ancestor_of() の例のように、共通テンプレートから関数を呼ぶ側が配列などを渡している場合、関数が配列に対応しているかを確認します。

    PHP7.xでは動いていても、PHP8.xで型が厳格になってFatal Errorになることがあります。


    エラー対処の順番まとめ

    1. Fatal error を最優先で修正(サイトが止まる)
    2. Warning を種類ごとに修正
    3. 連鎖しているWarningは根本原因から直す(downstream のエラーは自動的に消える)
    エラー 原因 対処
    get_page_by_path() Fatal Error PHP8の型厳格化で配列を渡すとFatal 関数側にis_array() + 再帰対応を追加
    session_start() Warning include失敗の出力がヘッダー送信を引き起こす連鎖 根本原因(ファイル名)を直す
    テーマ複製後の各種エラー ファイル名・パス・変数定義の更新漏れ 4大チェックリストで確認

    PHP7.xから8.xへの移行は、「今まで見逃されてきた問題が表面化する」プロセスです。
    エラーログを根気よく読んで、1種類ずつ修正していけば必ず解消できます。


    関連記事

    PHP8移行でWordPressテーマに出るWarning 5パターンと修正コード【実例付き】 PHP7→PHP8への移行後にWordPressテーマで実際に発生したPHP Warning 5種類を、before/afterのコード付きで解説。$postのnullチェック・get_post_meta()のfalse対応・foreachのnull引数・$_...  続きを読む PHP7→PHP8に上げたらerror.logが増えた。まず何をすべきか【WordPress】 PHP7.4からPHP8へのバージョンアップ後にerror.logが急増する原因と、最初にやるべき対処の流れを解説。sort -uでログをユニーク化する方法、FatalとWarningの優先順位の判断など、WordPressテーマをカスタマイズしているレベルの...  続きを読む WordPress $post変数のnullエラーを完全解決!error_logを綺麗にする実践的修正ガイド WordPressのerror_logに頻出する「Attempt to read property on null」「Undefined array key」エラー。$post変数や$_GET配列が原因です。本記事では典型的なパターンと修正方法、グローバル変数...  続きを読む