がるの健忘録

エンジニアでゲーマーで講師で占い師なおいちゃんのブログです。

「useを使ってクラスが使えるようになる」までの道程

ちょいとお仕事で、要約すると
・useすればクラス名の解決が可能になるのではないか?
 → 参照したいクラスがあるファイルのnamespace\クラス名をuseで指定すれば使用はできるはず
という趣旨の質問をいただきまして。

これはとても興味深い内容だなぁ、と思ったので「Blogで解説してよい?」と聞いたら快諾いただいたので、早速、ネタに(笑

今回書くのに近しいお話が
namespaceとuse https://note.com/gallu/n/nd4efd1c17e25
autoloadについて https://note.com/gallu/n/n2d0bb7718ceb
requireとinclude https://note.com/gallu/n/n658fbf6fd7ce
に乗っておりますので、よかったら。


結論から書くと
・ use は「別名(エイリアス)の作成をするだけでそれ以外は何もしてくれない」ので「なにもなくてuseだけだとクラスは(多分)使えない」
・クラスの解決には「クラスのオートローディング」という機能を使う
 → 自力で実装してもいいけど、まぁ「既存の実装」があるので、ルールに則りつつそれを使うと楽

となります。
ちょいとこの辺を、昔々のあたりから紐解いてみませう。

以下のクラスがあるとします。

class Hoge
{
}

で、以下のPHPがあった場合、これはHogeクラスを解決できます。

<?php

class Hoge
{
}

//
$obj = new Hoge();

まぁねぇ。
ただこんな風に「1ファイルに全部書く」とか色々と以下略なので、別ファイルに切り出します。

Libs/Hoge.php

<?php

class Hoge
{
}

t.php

<?php
//
$obj = new Hoge();

これだと解決できません。なんでかってぇと「t.phpを実行しても、Hoge.phpを読み込むような記述がどこにもないから」。
古(いにしえ)の術者は、こんな風に解決をしていました。

t.php

<?php
require_once('./Libs/Hoge.php');

//
$obj = new Hoge();

require_onceでもrequireでもinclude_onceでもincludeでもいいんですが(詳しい解説は上述の notes 参照)*1
こんな風に「ファイルを読み込んでやる」と、使えるようになります。
……えぇまぁなので昔は「先頭に100行とか200行とかrequire_onceが書いてある」なんてブツも拝見した記憶がございます。

まぁ「この辺は出来るだけ簡略化したい」と考えるのが優秀なプログラマというものでございます。
上述のnotesの「autoloadについて」を読んでいただくと手っ取り早いのですが。

https://www.php.net/manual/ja/language.oop5.autoload.php

オブジェクト指向アプリケーションを作成する開発者の多くは、 クラス定義毎に一つのPHPソースファイルを作成します。 最大の問題は、各スクリプトの先頭に、必要な読み込みを行う長いリストを 記述する必要があることです(各クラスについて一つ)。
spl_autoload_register() 関数を使うと、 任意の数のオートローダーを登録でき、 クラスやインターフェイスが定義されていなくても自動的に読み込めるようになります。 オートローダーを登録すれば、PHPがエラーで止まる前にクラスをロードする最後の チャンスが与えられます。

こんな手法がございます。

つまり、オートローディングの設定がないと
Hogeを使おうとする
・見つからない → エラー

な所が、オートローディングを使うと
Hogeを使おうとする
・見つからない → オートローディングをしてみる → それでも見つからないならエラー

となって、1クッション増えるわけなんですね。
クッションが増えるだけなので、「本当にどこにもそのクラスが存在しない」なら最終的にエラーにはなるのですが。

となると次は「オートローディングを、自力じゃなくて他人様が実装しておいてくれないもんかしらん?」となるのですが。
昨今のPHPにおいて、比較的高確率で「composer」を使っているのではないか、と思います。
で、もし「composerを使っている」とすると、composerにはオートローディングの実装があるので、こいつを借用する事ができます。

「public/index.php の最初の三行(vendor/autoload.php の挙動) https://note.com/gallu/n/ne1bcb25c412a 」を見ていただくと色々書いてあるのですが。
雑に書くと
・とりあえず、composerでインストールしたライブラリは、オートローディングが通用するようになる
感じです。

……だけだと「じゃぁ自作のクラスはどうすんだよ?」って話になるのですが。
実はcomposerのautoload.phpは、composer.jsonの['autoload']['psr-4']に「名前空間名 => ディレクトリ名」を書いておくと*2、こちらも自動で解決してくれるようになります。

なので、例えば上述であれば。
仮に名前空間を「\Gallu 」と仮定して

Libs/Hoge.php

<?php
namespace Gallu;

class Hoge
{
}

composer.json

{
(前略)
    "autoload": {
        "psr-4": {
            "Gallu\\" : "Libs/"
        }
    },
(後略)


t.php

<?php
require_once('./vendor/autoload.php');

//
$obj = new \Gallu\Hoge();

という風に書くと
・\Gallu\Hoge は、ない
・autoload.phpで登録されたオートローディングが動き出す
 → Galluって名前空間だからLibsを探してみる
  → HogeクラスだからHoge.phpってファイルを探してみる
  → あるから require する
・使えるようになった!!

という流れで、使えるようになります。
これがオートローディングの世界です。

まぁ「特殊なニーズがある」のであれば「自力実装を追加する」とかってのもあり得るんでしょうが、普通の用途ならまずは「あるモノを使ってみる」からでよいのではないかなぁ、と思います。
学習用途であれば一度は「自力実装」してみていただきたい所ですが。


じゃぁ改めて「useってなによ?」って話なのですが。
序盤にも書いた通り「別名の作成」となります。

かみ砕いて。

$obj = new \Gallu\Hoge();

ここ。
このまんま書くのであれば、useいらんです。
なお、\Gallu\Hogeを「完全修飾名」って言います。

これくらいならまぁまだ、とも思わんでもないのですが。
これが例えば

\Foo\Bar\Baz\Piyo\Fuga\Hoge

とかいうクラス名で、これが何カ所にも出てくる時に毎回毎回

$obj = new \Foo\Bar\Baz\Piyo\Fuga\Hoge() ;

とか書いていくのも、ちょっと「ぞっとしない」お話でございます。

こんな時に便利なのが別名。カタカナ表記だとエイリアス。これがuse。
こんな風に定義ができます。

use \Foo\Bar\Baz\Piyo\Fuga\Hoge as Hogera;

$obj = new Hogera() ;

こんな風に書くと「Hogera、って出てきたら \Foo\Bar\Baz\Piyo\Fuga\Hoge ってことで一つよろノ」って感じになります。
まぁエイリアスの名前通り、まんまの機能ですね。

さて。
わざわざ「クラス名と違う名前を毎回考えるのも面倒」だと思うので、割とこうなります。

use \Foo\Bar\Baz\Piyo\Fuga\Hoge as Hoge;

$obj = new Hoge() ;

これで目出度しとしてもよいのですが、「Hoge as Hoge」がちょいとウザい……で、実はこれが省略可能。

use \Foo\Bar\Baz\Piyo\Fuga\Hoge;

$obj = new Hoge() ;

スッキルする、と共に、多分見慣れた構文。
こうやってuseは「別名」を付ける機能を持ちます、が、一方で「クラス存在の解決」にはなんにも関わっておりませんので、従って「useだけ、だと、クラスは解決できない(かもしれない)」となります。


なので。
もし「ちゃんとuseしているのにクラスが解決できなくてPHPがエラーを吐く」のであれば、基本的には「所定の場所にクラスがかかれたファイルがない」か「そもそもそのクラスが存在していない」ので。
楽ちん順のやり方としては
・とりあえず find コマンドでファイルを探してみる(普通、 クラス名.php ってファイル名のはずなので)
・ちぃと時間がかかるかもですが、grepで「class クラス名」で全ファイルをあさってみる
で、対象のファイルが
・あるんだけど位置が違う
・ない
のあたりをつけておくと、次のアクションに繋がりやすいかなぁ、と思うです。

残りのお話は個別の内容になってしまうので、commonな内容としてはこの辺まで。
質問とかあったら、コメントにお願いいたします ノ

*1:まぁ普通、プログラムならrequire_onceですな

*2:正確には、書いた後 php composer.phar dump-autoload を実行すると