問題:なぜ PHP の実行が停止するのか
PHPは命名に関して厳格です。エンジンが同じスコープ内に全く同じ名前の関数を2つ見つけると、パニックを起こします。Fatal Error(致命的なエラー)をスローし、スクリプトを即座に終了させます。これは通常、誤って同じファイルを2回インクルードした場合や、2つの異なるライブラリが init() や send_email() のような汎用的な名前を使用している場合に発生します。
最近、5年前のレガシーコードベースをモダンなフレームワークに移行している際に、この問題に遭遇しました。私の error.log に表示された正確なエラーは以下の通りです。
PHP Fatal error: Cannot redeclare send_email() (previously declared in /var/www/html/functions.php:10) in /var/www/html/helper.php on line 5
デバッグプロセス
エラーメッセージを無視しないでください。競合がどこにあるのかを正確に示しています。2つの重要な手がかりが得られます:
- 元のソース:
/var/www/html/functions.php:10(関数が最初に定義された場所)。 - 競合ソース:
/var/www/html/helper.php on line 5(PHPが再定義を試みた場所)。
90%のケースでは、次の3つの理由のいずれかが原因です。ループ内で include を使用している、異なる2つのファイルで同一の名前を使用している、あるいは composer.json のオートローダーの設定ミスにより同じファイルが2回読み込まれている可能性があります。
解決策1:require_once に切り替える
これが第一の防御策です。ヘッダーとフッターの両方が config.php を呼び出しているなど、同じファイルを複数回インクルードすることが原因でエラーが発生している場合は、require_once または include_once に切り替えてください。
間違った方法:
// index.php 内
require 'functions.php';
require 'helper.php'; // helper.php 内でも require 'functions.php' を呼び出している場合、スクリプトはクラッシュします。
正しい方法:
// index.php 内
require_once 'functions.php';
require_once 'helper.php';
_once サフィックスは、読み込まれたファイルのチェックリストを保持するよう PHP に指示します。ファイルがすでにリストにある場合、PHP は2回目の要求を無視します。この単純な変更で、再定義の問題の大部分が解決します。
解決策2:function_exists で関数を囲む
プラグインや共有ライブラリを作成している場合、他の開発者がどのようにファイルをインクルードするかを制御することはできません。クラッシュを防ぐために、宣言を条件付きチェックで囲みます。これは WordPress 開発において必須の標準となっています。
if (!function_exists('send_email')) {
function send_email($to, $subject, $message) {
return mail($to, $subject, $message);
}
}
このチェックを使用することで、PHP はその名前がまだ使用可能である場合にのみ関数を定義します。別のスクリプトがすでにその名前を使用している場合、PHP は単にそのブロックをスキップして次に進みます。
解決策3:名前空間(プロフェッショナルの標準)
名前は同じだが異なるタスクを実行する2つの関数がある場合はどうすればよいでしょうか? send_email_v2_final() のような煩雑な名前に変更する必要はありません。代わりに、名前空間(namespaces)を使用して、それらを別々の「フォルダ」に保管します。
ファイル: functions.php
namespace App\Core;
function send_email() {
echo "Core Mailer 経由で送信中...";
}
ファイル: helper.php
namespace App\Utilities;
function send_email() {
echo "Utility Helper 経由で送信中...";
}
使用例:
use App\Core as CoreMail;
use App\Utilities as UtilMail;
CoreMail\send_email();
UtilMail\send_email();
名前空間は、モダンな PHP を整理するための最もクリーンな方法です。これにより、App\Core\send_email と App\Utilities\send_email が摩擦なく共存できるようになります。
修正の確認方法
解決策を適用したら、次の手順に従ってエラーが完全に解消されたことを確認してください:
- ログをクリアする:
error_logを消去し、ページをリフレッシュして、ファイルが空のままであるか確認します。 - CLI で確認する: ターミナルで
php index.phpを実行します。スタックトレースなしで実行されれば成功です。 - ユニットテストを実行する: PHPUnit を使用している場合は、関数の名前変更やラッピングの後もロジックが正常に動作することを確認するためにテストスイートを実行します。
クリーンなコードのためのプロのヒント
- 設定やデータベース接続などのコアな構造ファイルには、常に
require_onceを使用してください。 - 汎用的な名前は避けてください。
init()の代わりに、stripe_payment_init()のようなユニークな名前を使用します。 - クラスベースの構造を採用してください。クラス内のメソッドは、グローバルな名前の競合の影響を受けません。
- 面倒な作業は Composer に任せましょう。PSR-4 オートローダーを使用すれば、手動で
requireステートメントを書く必要が完全になくなります。

