名前空間をさっくり理解する

名前、つけてますか？

PHPには**namespace**(名前空間)という言語機能があります。

原初のPHPにはなかったのですが、PHP 5.3くらいからあるので、まあ平安時代には成立していたということです。それ以前の時代は `App_Http_Controllers_User` のような `_` 区切りの擬似名前空間が用いられていたことがありました。現在では `App\Http\Controllers\User` のような `\` 区切りの名前空間が利用できます。

## 名前空間付きのコード

名前空間が見慣れないという方のために`namespace`のあるコードとしてLaravelで自動生成したControllerファイルの例を先に出しておきます。

```php
<?php

namespace App\Http\Controllers;

use App\Models\Book;
use App\Http\Requests\StoreBookRequest;
use App\Http\Requests\UpdateBookRequest;

class BookController extends Controller
{
    /**
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        //
    }
}
```

アプリケーションに`App`なんて漠然とした名前をつけるなんて蛮行が許されるのかは議論のあるところですが、アプリケーションはライブラリではなく独立して成立するものなので、まあ許されるのでしょう。

## PHPマニュアル曰く

namespaceが何かということを知りたければPHPマニュアルを読むのがよいでしょう。

https://www.php.net/manual/ja/language.namespaces.rationale.php

> 名前空間とは何でしょう? 広義の「名前空間」とは、項目をカプセル化するもののことです。 これは多くの場面で見られる抽象概念です。 たとえば、たいていの OS はディレクトリでファイルをグループ化します。 この場合、ディレクトリがその中のファイルの名前空間として機能しています。 具体的に言うと、`foo.txt` というファイルは `/home/greg` と `/home/other` の両方に存在することが可能ですが、それらふたつの foo.txt を同じディレクトリに配置することはできません。 さらに、`/home/greg` ディレクトリの外から `foo.txt` にアクセスするには、ディレクトリ名をファイル名の前につけて `/home/greg/foo.txt` としなければなりません。 プログラミングの世界における名前空間も、この延長線上にあります。

まあうん、わかるようなわからないような……。とりあえず、`/home/greg/foo.txt` と `/home/other/foo.txt` はどちらも `foo.txt` というファイル名ですが別々に存在できるということは読み解けます。

namespaceで定義されたものを使うには`use`、つまりインポートという機能を使うのだということもPHPマニュアルに書いてあります。

https://www.php.net/manual/ja/language.namespaces.importing.php

> 外部の完全修飾名をエイリアスで参照したりインポートしたりする機能は、 名前空間において非常に重要なものです。 これは、Unix 系のファイルシステムでファイルやディレクトリへのシンボリックリンクを作成することに似ています。
>
> PHP は定数、関数、クラス、インターフェイス、トレイト、列挙型(Enum)、名前空間のエイリアスやインポートをサポートしています。
>
> エイリアス作成には use 演算子を使用します。 ここに、5 種類すべてのインポート方法の例を示します。

ちょっと待って、インポートって何？ `include`とは違うの？ という疑問は解決できたでしょうか。

このマニュアルを読んで完全に理解できたという方はここでこのページを閉じていただいて、あとは楽しい年末を心待ちにするだけです。

:::note info
***メリークリスマス！*** :tada:
:santa_tone2:「よいお年を！」
:::

## 日本人の名前で学ぶ名前空間

https://dic.pixiv.net/a/%E4%BD%95%E3%81%8C%E3%82%AF%E3%83%AA%E3%82%B9%E3%83%9E%E3%82%B9%E3%81%98%E3%82%83%E3%81%82%E3%81%84%21

*私、クリスマスとかいう海外のお祭りはしっくりこないんです* ということで説明を続けます。

PHPの名前空間は名字のようなものだということで説明ができます。

```php
<?php

namespace 山田;
```

ファイルの冒頭でこのような記述があるファイルは**山田家**だと思ってください。

```script.php
<?php

namespace 山田;

$man = new 一郎();
```

おもむろに一郎を呼び付けます。*「おい一郎！」*

山田家で一郎といえば誰でしょうか。

そうです、われらが山田家の長男「`山田\一郎`さん」のことを指すに決まっています。みんな知ってるね。

https://dic.pixiv.net/a/%E3%81%BF%E3%82%93%E3%81%AA%E7%9F%A5%E3%81%A3%E3%81%A6%E3%82%8B%E3%81%AD

https://dic.pixiv.net/a/%E3%81%9D%E3%82%93%E3%81%AA%E4%BA%8B%E3%80%81%E4%BF%BA%E3%81%8C%E7%9F%A5%E3%82%8B%E3%81%8B%21

*えっ知らない？* 仕方がないので定義してあげましょう。

```src/一郎.php
<?php

namespace 山田;

class 一郎
{
}
```

念のためディレクトリの構造も示しておきます。

```
yamada
├── script.php
└── src
    └── 一郎.php

1 directory, 2 files
```

`script.php`から`山田\一郎`さんのクラスを読み出すにはどうすればいいのでしょうか。

むかしながらのPHPではこのようにしていました。

```diff:script.php
 <?php
 namespace 山田;
 
+require_once __DIR__ . '/src/一郎.php';
 
 $man = new 一郎();
```

この構造の問題はなんでしょうか。

……そうですね、家族が増えていくたびにファイルの`require`を増やしていかなければいけません。

```diff:script.php
 <?php
 namespace 山田;
 
 require_once __DIR__ . '/src/一郎.php';
+require_once __DIR__ . '/src/二郎.php';
+require_once __DIR__ . '/src/三郎.php';

 $man = new 一郎();
+$man2 = new 二郎();
+$man3 = new 三郎();
```

ここは2022年、現代文明ではこのようなコードを見ることは**非常にまれ**になってしまいました。

ではどうするかというと、現代的な環境では[Composer](https://getcomposer.org/)を使うことが一般的です。

https://getcomposer.org/

Composerは「パッケージ」という単位でPHPスクリプトを扱うことができるツールです。一般的には[Packagist](https://packagist.org/)で公開されているフレームワークやライブラリをインストールするために使うのではないかと思いますが、Composerのすぐれている点はパッケージをインストールする対象も同じ「パッケージ」として管理対象にできるということです。

:::note info
Composer用語で「<ruby>Root Package<rt>ルートパッケージ</ruby>」と呼びます。
:::

Composerでインストールしたパッケージは一般的には個別のクラスを`include`/`require`を書かなくても使えます。どうしてそれが実現できるのかというと、Composerがクラスローダーの機能を提供しているので、これを使うことでクラスがオートロードされるのです。

クラスのオートロードについて知りたい人のために「[includeって書きたくない僕たちのためのオートローディングとComposer](https://qiita.com/tadsan/items/a78c9160418200e2d47a)」という記事を書いたのですが、まあこれは後回しでよいでしょう。

この機能を使うためにはプロジェクトのルート直下に`composer.json`を用意して、このように定義してあげます。

```composer.json
{
    "name": "zonuexe/yamada",
    "autoload": {
        "psr-4": {
            "山田\\": "src/"
        }
    }
}
```

このように書いてあげることで、**このプロジェクトは山田さんの敷地**であって、`src/`ディレクトリには「山田一家が整列している」ということを表しています。

ではさっそく、Composerの力を借りて一郎を呼び付けてやりましょう。Composerは `composer install` コマンドを実行することで`vendor/`ディレクトリ以下にファイルが生成されます。これらのファイルについてもご自身で読み解いていただくときっと興味深いことがわかると思うのですが、今回の記事では思考停止して単に使ってみましょう。

```diff:script.php
 <?php
 namespace 山田;
 
-require_once __DIR__ . '/src/一郎.php';
-require_once __DIR__ . '/src/二郎.php';
-require_once __DIR__ . '/src/三郎.php';
+require_once __DIR__ . '/vendor/autoload.php';

 $man = new 一郎();
 $man2 = new 二郎();
 $man3 = new 三郎();
```

:::note warn
**注意**
`/vendor/autoload.php` の読み込みはすべてのファイルに記述するのではなく、
個別に実行するスクリプトやWebの起動ファイル(`public/index.php`)にのみ記述します
:::

## おともだちを家に呼ぼう

せっかくなので田中さんの家を建てましょう。別のリポジトリを作ってもいいのですが、今回は~~めんどくさいので~~山田さんの家は広いので、敷地内に田中さんの家を建築させてもらうことにします。

`other/tanaka`以下は田中さんの家ということで、独立した`composer.json`を用意します。

```other/tanaka/composer.json
{
    "name": "zonuexe/tanaka",
    "autoload": {
        "psr-4": {
            "田中\\": "src/"
        }
    }
}
```

そして、`composer.json`で以下のように書くことでローカルファイルシステムにあるパッケージに依存できます。

```diff:composer.json
 {
     "name": "zonuexe/yamada",
+    "repositories": [
+         {
+             "type": "path",
+             "url": "other/tanaka/"
+         }
+    ],
     "autoload": {
         "psr-4": {
             "山田\\": "src/"
         }
     },
+    "require": {
+        "zonuexe/tanaka": "dev-master"
+    }
 }
```

この状態で `composer require zonuexe/tanaka:dev-master` のように実行すると `zonuexe/tanaka` パッケージがインストールできてしまうはずです。

<details><summary>ディレクトリ配置も見てみしょう (折りたたみ)</summary>

```
yamada/
├── composer.json
├── composer.lock
├── other
│   └── tanaka
│       ├── composer.json
│       ├── composer.lock
│       ├── src
│       │   └── 太郎.php
│       └── vendor
│           ├── autoload.php
│           └── composer
│               └── ...
├── script.php
├── src
│   └── 一郎.php
└── vendor
    ├── autoload.php
    ├── composer
    │   └── ...
    └── zonuexe
        └── tanaka -> ../../other/tanaka/
```

</details>

:::note info
**無駄なことをしていると感じましたか？**
このように同じGitリポジトリ内でComposerパッケージを分割することでモジュラモノリスに活用もできます
:::

さて、`script.php`で`田中\太郎`くんを召喚してみましょう。

方法はふたつあります。

 * `use` でインポートする
 * インポートせずフルネームで呼ぶ

```php:script.php (インポートする場合)
<?php
namespace 山田;

use 田中\太郎;

require_once __DIR__ . '/vendor/autoload.php';

var_dump(new 太郎());
```

```php:script.php (インポートしない場合)
<?php
namespace 山田;

require_once __DIR__ . '/vendor/autoload.php';

var_dump(new \田中\太郎());
```

構成に成功していれば、どちらのコードでも`田中\太郎`くんが呼び出せるはずです。

よく気をつけてほしいのは、`use 田中\太郎;`と書くと最後の`\`と`;`の間、つまり**そのファイル内では`太郎`と呼び捨てにできる**ということです。

以下のコードを考えてみてください。

```php
<?php
namespace 山田;

require_once __DIR__ . '/vendor/autoload.php';

var_dump(new 田中\太郎());
```

このコードは期待通りに動きません。ここは`山田`さんの家だというルールが生きています。ここでインポートされずに呼ばれる名前というのは山田ファミリーの一員なので、つまり **`山田\田中\太郎`として名前解決されます**。

```diff:script.php (名前空間を部分的にインポートする)
 <?php
 namespace 山田;
 
+use 田中;
 
 require_once __DIR__ . '/vendor/autoload.php';
 
 var_dump(new 田中\太郎());
```

`use 田中` を追加してあげることで、このファイル内では山田ファミリーの一員 **ではない** `田中` という野郎も居るのだと認められることになります。

では、田中くんの妹の`田中\アーデルハイト\花子`ちゃんも招待してあげることにしましょう。

```php
<?php
namespace 山田;

use 田中\太郎;
use 田中\アーデルハイト\花子;

require_once __DIR__ . '/vendor/autoload.php';

var_dump(new 花子());
```

花子さんは名前があまり好きではないようで、「ハイジ」と呼んでほしいみたいです。

```diff:php
 <?php
 namespace 山田;
 
 use 田中\太郎;
-use 田中\アーデルハイト\花子;
+use 田中\アーデルハイト\花子 as ハイジ;
 
 require_once __DIR__ . '/vendor/autoload.php';
 
-var_dump(new 花子());
+var_dump(new ハイジ());
```

これで無事にお気に召すように呼んで差し上げられます。

ここで重要なのは、`use 田中\太郎;`と書いた時点では**クラスを読み込んでいない**ということです。ではいつ読み込まれるかというと `new 太郎()` が評価されたときです。これはPHPとComposerが提供するオートロード機能によって読み込まれるのですが、PHPの特徴はそのクラスを使う瞬間までロードを遅らせることができます。すなわち<ruby>Lazy load<rt>遅延ロード</ruby>です。

:::note alert
実際に必要になるまで遅延するということは、存在しないクラスをuseしても実行時にエラーになりません。
PhpStormや静的解析ツールなどできちんとコーディングミスがないようにチェックすることが重要です。
:::

:::note info
PHPの組み込みクラス(`Exception`, `DateTime`など)の多くはサブ名前空間に属していません。
つまり、この記事的にいうと名字がありません。名字なしのことを**トップレベル**といいます。
:::

花子をハイジと呼び替えるのは極端な例ですが、`Foo\Bar\Twitter\Client`というクラスを使うことを考えてみましょう。

```php
use Foo\Bar\Twitter\Client;
use Foo\Bar\Twitter\Exception;

try {
    $twitter_client = new Client();
    $twitter_client->request(...$args);
} catch (Exception $e) {
    log($e);
}
```

このコードは十分に小さいといえますが、複数のClientを同じ文脈で扱うとなると多少混乱を呼ぶかもしれません。このようなコードは `use as` であえて冗長な名前を付けることでファイル内で意味が明確になります

```diff:php
-use Foo\Bar\Twitter\Client;
-use Foo\Bar\Twitter\Exception;
+use Foo\Bar\Twitter\Client as TwitterClient;
+use Foo\Bar\Twitter\Exception as TwitterException;

 try {
-    $twitter_client = new Client();
+    $twitter_client = new TwitterClient();
     $twitter_client->request(...$args);
-} catch (Exception $e) {
+} catch (TwitterException $e) {
     log($e);
 }
```

:::note warn
クラス定義の段階で`Foo\Bar\Twitter\Client`のように重複のない短い名前をつけるべきか、`Foo\Bar\Twitter\TwitterClient` のように重複を許容し`as`が不要な明示的な名前をつけるべきか、私の中でも決着がついていません。皆さんはどう考えますか？
:::

また、これは`田中\太郎`と`山田\太郎`を同じファイルで活躍させるときにも有用です。

```php
use Foo\Bar\Twitter\Client as TwitterClient;
use Hoge\Piyo\Http\Client as HttpClient;
use 田中\太郎 as 田中太郎;
use 山田\太郎 as 山田太郎;
```

この方法はこれから定義するクラスと継承元のクラスが(namespace違いの)同名である場合にも有用です。

```php
namespace App;

use PHPUnit\Framework\TestCase as BaseTestCase;

abstract TestCase extends BaseTestCase
{
}
```

## インポートの対象

### クラス・トレイト・インターフェイス

ここまで使ってきたように `use` はクラスをインポートしますが、[トレイト](https://php.net/trait)・[インターフェイス](https://www.php.net/interface)・[列挙型(enum)](https://www.php.net/enum)も基本的にはクラスと同様の振る舞いをします。さらには`use`は名前空間の一部分もインポートできます。

以下のインポートとインスタンス化はすべて合法です。

```php
<?php
namespace 山田;

use 田中;                               // インポート(1)
use 田中\アーデルハイト;                // インポート(2)
use 田中\アーデルハイト\花子;           // インポート(3)
use 田中\アーデルハイト\花子 as ハイジ; // インポート(4)

require_once __DIR__ . '/vendor/autoload.php';

var_dump(new \田中\アーデルハイト\花子()); // インポートなし
var_dump(new 田中\アーデルハイト\花子());  // インポート(1)
var_dump(new アーデルハイト\花子());       // インポート(2)
var_dump(new 花子());                      // インポート(3)
var_dump(new ハイジ());                    // インポート(4)
```

ところで、PHPではクラス名の文字列からインスタンス化したり、DIコンテナにクラス名の文字列を渡して実体と引き換えるパターンがよくあります。また、PHPUnitの`expectException`メソッドで送出されることが見込まれるクラス名を渡したりすることもあります。

PHPでは`クラス名::class`という記法(マジック定数)でクラス名の文字列に展開されます。

```php
var_dump([
    \田中\アーデルハイト\花子::class,
    田中\アーデルハイト\花子::class,
    アーデルハイト\花子::class,
    花子::class,
    ハイジ::class,
]);
// => すべて "田中\アーデルハイト\花子" に展開される
```

これらの仕様によってインポートですっきりとクラス文字列を生成できます。

### try-catch

以下のようなコードは初心者が犯しがちなミスです。

```php
<?php
namespace 山田;

try {
    query();
} catch (PDOException $e) {
    log($e);
}
```

このファイル内で単に`PDOException`と書くと、それは`山田\PDOException`を意味してしまいます。そんなクラスは普通定義しないので、本物の[`PDOException`](https://www.php.net/PDOException)をキャッチするには以下のどちらかの変更を加える必要があります。

 * `use PDOException;` を追加する
 * `catch (\PDOException $e)` のように先頭に `\` をつける

:::note alert
繰り返しますが、存在しないクラスを`catch`しても実行時にエラーにならず、単に例外を素通しします。
PhpStormや静的解析ツールなどの警告を無視せず、きちんと対処することが重要です。
:::

### 定数・関数

PHPではクラスとは別に`namespace`以下に関数や定数を定義できます。

```php
<?php
namespace Name\Sub;

const 定数 = __NAMESPACE__;

function 関数(): string {
    return __FUNCTION__;
}

class クラス {
    const クラス定数 = __CLASS__;

    public static function メソッド(): string {
        return __METHOD__;
    }
}

var_dump([
    \Name\Sub\定数,
    \Name\Sub\関数(),
    \Name\Sub\クラス::class,
    \Name\Sub\クラス::クラス定数,
    \Name\Sub\クラス::メソッド(),
]);
```

<details><summary>つまり、やろうと思えば以下のようなこともできます (折りたたみ)</summary>

```php
<?php
namespace Name\Sub;

const クラス = クラス::class;

function クラス(): クラス {
    return new クラス();
}

class クラス {
    public static function new(): self {
        return new self();
    }
}

var_dump([
    new (\Name\Sub\クラス),
    \Name\Sub\クラス(),
    \Name\Sub\クラス::new(),
    new \Name\Sub\クラス(),
]);
```

</details>

あまりメリットがないから普通にコンストラクタを呼べばいいと思いますが…

:::note warn
**注意**
クラス類とは異なり、関数や定数はオートロードできません。
`composer.json` から `autoload.files` として読み込むか、直接読み込む必要があります。
:::

namespace内で関数や定数が定義できるということは、もちろんPHP標準のものと同じ名前の関数や定数も定義できてしまいます。

そのため、トップレベル以外のnamespace宣言されたファイル内で関数呼び出しをすると、「現在の名前空間に関数(定数)が定義されているか」「トップレベル名前空間に関数(定数)が定義されているか」と呼び出しの都度2度探索するので若干パフォーマンスが落ちることがあります。

特に標準関数を呼び出すときは `use function` を書くか、関数呼び出し時に `\` を先頭につけることでパフォーマンス的にはやや有利になります。

:::note info
特に一部の関数は、ただの関数呼び出しではなく専用命令に置き換えられることがあります
:::

:::note warn
一方で、ボトルネックになるような箇所でなければ望むようなパフォーマンス改善効果も出ないでしょう
:::

## まとめ

いままで口頭で名前空間を「名字」による説明をしていたので文章にまとめました。非常に雑に思いつくまま書き殴ったので書いておくべきことがいろいろ漏れているような気がするのですが、これを読むだけでPHPのnamespaceを6割くらい完全マスターできるのではないでしょうか。

## 関連記事

https://qiita.com/tadsan/items/a78c9160418200e2d47a

あとなんかむかしいろいろ書いていた気もするので、起きて思い出したらなんか追記していきます。