PHPにはクラスという仕組みがあります。クラスは関連するデータと操作をまとめることができる便利な仕組みです。適切なクラス分けがなされている場合、関連する機能はまとまった場所に置かれ、関連しない機能は独立します。疎結合と高凝集なコードをつくる助けとなる機能なわけです。
PHPで同名のクラスは存在を許されません。とはいえ昨今のコードでこれが問題になることは珍しいです。これはcomposerのオートロード機能を利用するためにファイルパスと名前空間とクラス名の対応付けが自然となされることが多いからです(歴史の時系列的には、名前の衝突を避けるためのPSR(PHP標準勧告)ができる→PSRに則ってオートロード機能ができる→みんな名前をいい感じにする、の順です)。その様なわけでクラス名の衝突は起きにくいわけですが、それでも様々な事情が絡み合って衝突する時はあります。そういった時に原因となっているコードを探すためのコードを紹介します。
クラス名衝突エラーは次のようなエラーメッセージが表示されます。
Fatal error: Cannot declare class DuplicateClass, because the name is already in use in {ファイルのフルパス} on line {クラスの定義されている行番号}
// 例
Fatal error: Cannot declare class DuplicateClass, because the name is already in use in /home/hoge/tmp.php on line 13
このエラーメッセージの困ったところは衝突したクラスのうち後から定義された片方についてしか定義場所の説明がなされないことです。実際には衝突された前から定義されてある方の場所も知りたいことがままあります。これを解決するためには次のコードが使えます、
<?php
/**
* @param string $className
* @return array{0:string, 1:string} [ファイル名, 開始行番号]
* @throws ReflectionException
*/
function getClassCodePlace(string $className): array
{
// リフレクションクラスのインスタンスを作成
$reflector = new ReflectionClass($className);
// 定義されているファイル名を取得
$fileName = $reflector->getFileName();
// 定義が始まる行番号を取得
$startLine = $reflector->getStartLine();
return [$fileName, $startLine];
}
/***********************************************
* 使用例
**********************************************/
class DuplicateClass
{
public function echoMsg(): void
{
echo '重複されるクラスです';
}
}
var_dump(getClassCodePlace(DuplicateClass::class));
class DuplicateClass
{
public function echoMsg(): void
{
echo 'クラス名を被せに行くクラスです';
}
}
/*
/xxxx/tmp.phpの22行目が一つ目のDuplicateClassの定義場所とわかる
array(2) {
[0]=>
string(62) "/xxxx/tmp.php"
[1]=>
int(22)
}
/xxxx/tmp.phpの32行目が二つ目のDuplicateClassの定義場所とわかる
PHP Fatal error: Cannot declare class DuplicateClass, because the name is already in use in /xxxx/tmp.php on line 32
*/
クラス名からそのクラスが定義されているファイルと行番号を読み取るリフレクションを使ったコードです。あらかじめgetClassCodePlace関数を用意しておき、クラス名衝突のエラーが出たらgetClassCodePlaceをエラーの手前に差し込んで画面なりコンソールなりに結果を表示します。こうと衝突の原因となった二つのクラスの定義場所がわかります。