## FAQ: 名前空間について知っておくべきこと

(PHP 5 >= 5.3.0, PHP 7, PHP 8)

この FAQ は 2 つに分かれています。一般的な質問と、 深く理解するために有用な実装に関する質問です。

まずは一般的な質問。

1. 名前空間を使わない場合、 何か注意すべきことはありますか?
2. 名前空間内での内部クラスあるいはグローバルクラスの使用法は?
3. 同じ名前空間にあるクラス、関数あるいは定数を使用する方法は?
4. `\my\name` や `\name` のような名前はどのように解決される?
5. `my\name` のような名前はどのように解決される?
6. 修飾されていない `name` のようなクラス名はどのように解決される?
7. 修飾されていない `name` のような関数名/定数名はどのように解決される?

また、名前空間の実装を理解するために有用な実装の詳細は次のとおりです。

1. インポートした名前が同一ファイルで定義されているクラスと衝突してはいけない
2. 名前空間のネストはできない
3. 動的な名前空間名 (クォートした名前) ではバックスラッシュのエスケープが必要
4. バックスラッシュを含む未定義な定数を参照すると、致命的なエラーが発生する
5. 特別な定数 `null`, `true`, `false` は上書きできない

### 名前空間を使わない場合、何か注意すべきことはありますか?

いいえ。これまで書いてきたコード、今後書く名前空間を含まないコードのいずれについても、 名前空間が何らかの影響を及ぼすことはありません。 お望みなら名前空間を使わないコードを書くこともできます。

例1 名前空間の外部にあるグローバルクラスへのアクセス

    <?php
    $a = new \stdClass;
    print $a::class;

これは、機能的に次と同等です。

例2 名前空間の外部にあるグローバルクラスへのアクセス

    <?php
    $a = new stdClass;
    print $a::class;

### 名前空間内での内部クラスあるいはグローバルクラスの使用法は?

例3 名前空間内からの内部クラスへのアクセス

    <?php
    namespace foo;
    $a = new \stdClass;
    print $a::class . "\n";

    function test(?\ArrayObject $parameter_type_example = null) {}

    $a = \FilesystemIterator::CURRENT_AS_FILEINFO;
    print $a . "\n";

    // 内部クラス/グローバルクラスの継承
    class MyException extends \Exception {}
    print MyException::class . "\n";

### 同じ名前空間にあるクラス、関数あるいは定数を使用する方法は?

例4 名前空間内のクラス、関数あるいは定数へのアクセス

    <?php
    namespace foo;

    class MyClass {}

    // 現在の名前空間のクラスをパラメータの型として使う
    function test(?MyClass $parameter_type_example = null) {}
    // 現在の名前空間のクラスをパラメータの型として使うもうひとつの方法
    function test2(?\foo\MyClass $parameter_type_example = null) {}

    // 現在の名前空間のクラスの継承
    class Extended extends MyClass {}
    print Extended::class . "\n";

    // グローバル関数へのアクセス
    $a = \strlen("test");
    print $a . "\n";

    // グローバル定数へのアクセス
    $b = \INI_ALL;
    print $b . "\n";

### `\my\name` や `\name` のような名前はどのように解決される?

`\` から始まる名前は常に見た目のままに解釈されます。 つまり `\my\name` は `my\name` であり、 `\Exception` は `Exception` となります。

例5 完全修飾名

    <?php
    namespace foo;
    $a = new \my\name(); // "my\name" クラスのインスタンスを作成します
    echo \strlen('hi'); // "strlen" 関数をコールします
    $a = \INI_ALL; // $a に定数 "INI_ALL" の値を設定します

### `my\name` のような名前はどのように解決される?

名前にバックスラッシュを含むが先頭はバックスラッシュでない名前、たとえば `my\name` のような名前は 2 通りの方法で解釈されます。

別の名前に `my` というエイリアスを指定する import 文がある場合は、そのエイリアスが `my\name` の `my` 部分に適用されます。

それ以外の場合は、現在の名前空間が `my\name` の先頭に付け加えられます。

例6 修飾名

    <?php
    namespace foo;
    use blah\blah as foo;

    $a = new my\name(); // "foo\my\name" クラスのインスタンスを作成します
    foo\bar::name(); // "blah\blah\bar" のstaticメソッド "name" をコールします
    my\bar(); // "foo\my\bar" 関数をコールします
    $a = my\BAR; // $a に定数 "foo\my\BAR" の値を設定します

### 修飾されていない `name` のようなクラス名はどのように解決される?

バックスラッシュを含まない `name` のようなクラス名は 2 通りの方法で解釈されます。

別の名前に `name` というエイリアスを指定する import 文がある場合は、そのエイリアスが適用されます。

それ以外の場合は、現在の名前空間が `name` の先頭に付け加えられます。

例7 非修飾クラス名

    <?php
    namespace foo;
    use blah\blah as foo;

    $a = new name(); // "foo\name" クラスのインスタンスを作成します
    foo::name(); // "blah\blah" クラスのstaticメソッド "name" をコールします

### 修飾されていない `name` のような関数名/定数名はどのように解決される?

バックスラッシュを含まない `name` のような関数名/定数名は 2 通りの方法で解釈されます。

まず、現在の名前空間が `name` の先頭に付け加えられます。

現在の名前空間に `name` という関数あるいは定数がない場合は、 グローバル関数あるいは定数に `name` があればそれを使用します。

例8 非修飾関数/定数名

    <?php
    namespace foo;
    use blah\blah as foo;

    const FOO = 1;

    function my() {
        print __FUNCTION__ . "\n";
    }
    function foo() {
        print __FUNCTION__ . "\n";
    }
    function sort(&$a)
    {
        \sort($a); // グローバル関数 "sort" をコールします
        $a = array_flip($a);
        return $a;
    }

    my(); // "foo\my" をコールします
    print strlen('hi') . "\n"; // "foo\strlen" が存在しないので、グローバル関数 "strlen" をコールします
    $arr = array(1,3,2);
    $b = sort($arr); // "foo\sort" 関数をコールします
    var_dump($b);
    foo(); // calls function "foo\foo" - import is not applied

    $a = FOO; // sets $a to value of constant "foo\FOO" - import is not applied
    print $a;
    $b = INI_ALL; // sets $b to value of global constant "INI_ALL"
    print $b;

### インポートした名前が同一ファイルで定義されているクラスと衝突してはいけない

次のようなスクリプトの組み合わせは、正当なものです。

file1.php

    <?php
    namespace my\stuff;
    class MyClass {}

another.php

    <?php
    namespace another;
    class thing {}

file2.php

    <?php
    namespace my\stuff;
    include 'file1.php';
    include 'another.php';

    use another\thing as MyClass;
    $a = new MyClass; // 名前空間 another のクラス "thing" のインスタンスを作成します。

`MyClass` クラスが名前空間 `my\stuff` にあるとはいえ、名前の衝突はありません。 MyClass の定義は別のファイルにあるからです。 しかし、次の例は名前の衝突による致命的なエラーとなります。 MyClass の定義が同じファイル上の use 文で行われているからです。

    <?php
    namespace my\stuff;
    use another\thing as MyClass;
    class MyClass {} // fatal error: MyClass conflicts with import statement
    $a = new MyClass;

### 名前空間のネストはできない

PHP では名前空間のネストはできません。

    <?php
    namespace my\stuff {
        namespace nested {
            class foo {}
        }
    }

しかし、それっぽいことをするのは簡単です。次のようにすればいいのです。

    <?php
    namespace my\stuff\nested {
        class foo {}
    }

### 動的な名前空間名 (クォートした名前) ではバックスラッシュのエスケープが必要

バックスラッシュは文字列のエスケープ文字として使われることに注意しましょう。 文字列の中で使う際にはバックスラッシュを二重に書く必要があります。 そうしないと、予期せぬ結果を引き起こしてしまいます。

例9 ダブルクォートで囲んだ文字列内で名前空間名を扱う際の危険

    <?php
    $a = "dangerous\name"; // ダブルクォートの中では \n が改行文字になってしまいます!
    $obj = new $a;

    $a = 'not\at\all\dangerous'; // これなら大丈夫です
    $obj = new $a;

シングルクォートで囲んだ文字列内では、 バックスラッシュによるエスケープシーケンスをより安全に使うことができます。 しかし、文字列内では常にバックスラッシュをエスケープする習慣をつけておくことをお勧めします。

### バックスラッシュを含む未定義な定数を参照すると、致命的なエラーが発生する

未定義の定数のうち `FOO` のような修飾されていないものは、 PHP が `FOO` を定数の値と解釈したという notice が発生します。 修飾あるいは完全修飾形式の定数、つまりバックスラッシュを含む定数の場合、 それが未定義なら致命的なエラーが発生します。

例10 未定義の定数

    <?php
    namespace bar;
    $a = FOO; // notice - undefined constants "FOO" assumed "FOO";
    $a = \FOO; // fatal error, undefined namespace constant FOO
    $a = Bar\FOO; // fatal error, undefined namespace constant bar\Bar\FOO
    $a = \Bar\FOO; // fatal error, undefined namespace constant Bar\FOO

### 特別な定数 `null`, `true`, `false` は上書きできない

名前空間内で特別な組み込み定数を定義しようとすると、致命的なエラーが発生します。

例11 未定義の定数

    <?php
    namespace bar;
    const NULL = 0; // 致命的なエラー
    const true = 'stupid'; // これも、致命的なエラー
    // etc.

`↑` and `↓` to navigate • `Enter` to select • `Esc` to close • `/` to open