どもです。
自分はPHPというものに関わってきて既にウン年…ちょっと数えるのも躊躇う年月が経っていますが、やってきた仕事の都合上、名前空間というものを自分で使うことなく、なんとなくの理解でやり過ごしてきました。
が、どうやら直視せねばならない時がやってきたようです。
しかし自分のような「PHPはまあ充分理解しているといっても過言じゃないが、名前空間のことはほぼさっぱり分からん」という輩向けの解説というものは存在しないものです。
今回はそういう話です。
名前空間 とは、自動でクラスをインクルードしてくれる機能である??
取り敢えずGeminiに「PHP分かる人に対して名前空間を説明してみせい」と言ってみました。
//自分は「doshirouto」フォルダにいるという宣言
namespace doshirouto;
//「core\Logger」のショートカットを作る
use core\Logger;
class Main{
public function create() {
$logger = new Logger();
}
}はい。もう匙を投げました。
意味が分かりません。ルートはどこなんですか。Loggerはフォルダ名ですか、ファイル名ですか。
名前空間のルートやルールは、vendor/autoload.phpが決める???
どうやら、名前空間のことになると、なぜかComposer(PHPのパッケージ管理システム)ありきの話になりがちなようです。
プロジェクト/
├── vendor/ Composerが管理する外部ライブラリ
├── composer.json 名前空間とフォルダの紐付けを設定するファイル
└── src/ 自分で書くのはこの中身
├── doshirouto/
│ └── Main.php
└── core/
└── Logger.phpComposerを使うと、プロジェクトのフォルダ構成はこうなります。
composer dump-autoload コマンドを実行することで、vendorにautoload.phpが生成されます。
このファイルの中身は、composer.jsonに従って生成されたリンク群であり、使おうとしたクラスが見つからない場合に autoload.php の中身が実行され、autoload.php にてインクルードが行われます。
このとき、autoload.php のルールで、namespace のルートは src ディレクトリにされます。
つまり、名前空間を使えば一定ルールでファイルが自動インクルードされる、というのは、
×名前空間の機能
○Composerが名前空間を利用して勝手に実装した機能
だということ。
どうやら、自分は根本的な思い違いをしていたようです。
本当の namespace
さて、自分はnamespaceを勘違い、というか過大評価していたようです。
本当のnamespaceを理解する必要があります。
namespace doshirouto; //このファイルは「doshirouto」という名前空間下にいるという宣言
function exec(){}というファイルがあった場合に、
require_once 'さっきのやつ.php';
doshirouto\exec(); //「doshirouto」という名前空間下のexec()を実行するとすることで、プロジェクト内にexec()がいくつあっても関数名重複にならない。
これが名前空間の本来の用途のようです。
また、実用では名前空間は多階層化する宿命にあります。
doshirouto\product\item\test\exec(); //「doshirouto」下の「product」下の「item」下の「test」下のexec()を実行するこんな呼び出し方をするのは御免なので、useを使います。
use doshirouto\product\item\test; //「doshirouto」下の「product」下の「item」下の「test」を、このファイルでは「test」として扱う指定
test\exec() //「test」下のexec()を実行するこれがuseの本来の用途。
なんでこれが自動インクルードなんかに飛躍してしまったんだ…?
使おうとしたクラスが見つからない場合のPHP機能 spl_autoload_register
spl_autoload_register('AAA::bbb')と記載しておくと、使おうとしたクラスが見つからない場合に「AAAクラスのbbb関数」を呼び出します。
function bbb($className){}呼び出した関数には、使おうとしたクラス名が第一引数にセットされます。
そして、この引数に入る値には、名前空間が適用されます。useを使っていた場合、ちゃんとnamespaceのパスに置き換えられます。
つまり…
名前空間で自動インクルードする という幻想の正体
spl_autoload_register('AAA::bbb');
class AAA{
function bbb($className){
$className = str_replace('\\', '/', $className); //名前空間のセパレータは「\」なので、インクルード用の「/」に変換する
require_once($className.'.php');
}
}辿り着きました。
これが「名前空間で自動インクルードする」という幻想の正体です。
ここでは超最低限の記載にしていますが、spl_autoload_register で指定した関数の書き方は基本的に自由です。
autoload.php のように膨大なリスト形式にしても良し、自動でアドレスを生成しても良し、勝手にルートを設定しても良しです。
おまけ \Exception の話
最近の解説サイトでは、try-catchを行うときに、だいたいこう書かれています。
catch(\Exception $e){
...
}この\Exceptionの「\」の意味が分かっていなかったのですが、これ調べてる最中のついでに理解しました。
名前空間を使わないプロジェクトでは、あっても無くても同じだったので。
名前空間下にで宣言する場合、PHP標準クラス(ThrowableとかDateTimeとか)は「\」をつけないと、自分の名前空間内の関数を探しに行ってしまうからだそう。
namespace doshirouto;
new Exception();だと、doshirouto\Exceptionを参照しようとしてしまうという。
ほーん。そういう。
参考サイト:
【PHP超入門】名前空間(namespace・use)について
Composerに汚染されていない名前空間の学習がしたいならここ。