• PHP
【php8上級/準上級試験】模擬問題解説 問題7. 名前空間とグローバル空間 use エイリアス(as)

「第1回 PHP 8 上級 模擬試験」を解説していきます

本記事ではひたすら下記記事の模擬試験の解説をしていきたいと思います!

第1回 PHP 8 上級 模擬試験
https://study.prime-strategy.co.jp/study/ph8ex1/

解説記事一覧

模擬問題 7

名前空間に関する説明の中で、誤っているものを1つ選びなさい。
なお「\」はバックスラッシュに読み替えること。
また、すべてのコードには下記のコードが適切な箇所に書かれているものとする。

declare(strict_types=1);
error_reporting(-1);

名前空間は、namespace キーワードを使って宣言する。
名前空間の宣言は、通常他のコードより前にファイルの先頭で宣言をする必要がある。名前空間の宣言前に書かれたクラスなどは、宣言された名前空間には含まれない。
そのため、以下のコード

class Hoge {
}

namespace Name;

class Foo {
}

$obj = new Hoge();
var_dump($obj);

を実行すると、結果は次のとおりとなる。

Fatal error: Uncaught Error: Class ‘Name\Hoge’ not found in …
これはHogeがグローバル空間で宣言をされているためなので、以下のコード

class Hoge {
}

namespace Name;

class Foo {
}

$obj = new \Hoge();
var_dump($obj);

は正しく実行でき、結果は次のとおりとなる。

object(Hoge)#1 (0) {
}

名前空間とグローバル空間

namespaceキーワードより前にクラスを宣言すると、そのクラスはグローバル空間に含まれます

⬇️

そのグローバル空間に含まれるHogeクラスを現在の名前空間(この場合は Name\Hoge)からクラスを探そうとするため

⬇️

下記のエラーが発生

Fatal error: Uncaught Error: Class 'Name\Hoge' not found in …
これはHogeがグローバル空間で宣言をされているため

つまり選択肢の文章にあるエラーの内容は正しい内容です

完全修飾名

グローバル空間のクラスを使うには \Hoge のように先頭にバックスラッシュをつけて完全修飾名を指定します

⬇️

よって下記の処理も正しい説明です

$obj = new \Hoge();
var_dump($obj);

// 出力結果
object(Hoge)#1 (0) {
}

名前空間のベストプラクティス

PHP 7以降では、名前空間の宣言は必ずしもファイルの先頭である必要はありません。ただし、ベストプラクティスとしては先頭に書くことが推奨されています

【推奨】 ファイルの先頭 <?php namespace MyProject; class Foo { // … } 【非推奨】 ファイルの先頭 <?php echo “Hello”; namespace MyProject; class Foo { // … }

上記より説明内容は基本的に正確で、PHPの名前空間の動作を適切に説明していると言えますが、一般的なスタイルではないです →❌

名前空間は、namespace キーワードを使って宣言する。
また、名前空間はディレクトリやファイルと同様に、名前空間の階層構造を指定することができる。
そのため、以下のコード

namespace Name\SubName;

class Hoge {
}

$obj = new Hoge();
var_dump($obj);

を実行すると、結果は次のとおりとなる。

object(Name\SubName\Hoge)#1 (0) {
}

名前空間の階層構造

  1. namespace Name\SubName; という記述によって、Name という名前空間の下にある SubName という階層の名前空間を宣言
  2. オブジェクトを var_dump すると、PHP は 「オブジェクトの完全修飾名」 を表示します。
  3. この例では、Name\SubName\Hoge がクラス名として表示されます。

よって問題の選択肢は⭕正解です

名前空間の定義がない場合、すべてのクラスや関数の定義はグローバル空間に配置される。PHP で定義済みのクラスも、グローバル空間に配置される。
先頭に \ (バックスラッシュ) を付けると、名前空間の内部からであってもグローバル空間の名前を指定することができる。
そのため、以下のコード

namespace Name;

try {
    throw new Exception('error');
} catch (Exception $e) {
    echo 'in to catch';
}

を実行すると、結果は次のとおりとなる。

Fatal error: Uncaught Error: Class ‘Name\Exception’ not found in …
一方で以下のコード

namespace Name;

try {
    throw new \Exception('error');
} catch (\Exception $e) {
    echo 'in to catch';
}

を実行すると in to catch となる。

グローバルの名前空間の参照方法

PHP がもともと提供しているビルトインクラス(Exception や PDO など)も同様に、グローバル空間に存在します。

ところが、名前空間を定義しているファイル の中で、クラスや関数を 「そのままの名前」(先頭に \ がない状態)で呼び出すと、現在の名前空間内部にあるクラスや関数を探しに行き未定義だよ!とエラーになります

よって問題の選択肢は⭕正解です

PHP でのエイリアス作成には use 演算子を使用する。
use で指定する名前空間付きの名前の、先頭の \ (バックスラッシュ) は不要で、推奨されない。
そのため、以下のコード

namespace Name;

class Hoge {
}

class Foo {
}

namespace Name\Sub;

// よく使われているuse
use Name\Hoge;

$obj = new Hoge();
var_dump(__NAMESPACE__, $obj);

namespace Name\Sub2;

// エイリアスを切ったuse
use \name\Hoge as Bar; // 先頭の \ は推奨されていない

echo PHP_EOL;
$obj = new Bar();
var_dump(__NAMESPACE__, $obj);

namespace Name\Sub3;

// useのグループ化
use Name\{Hoge, Foo};

echo PHP_EOL;
$hoge_obj = new Hoge();
$foo_obj = new Foo();
var_dump(__NAMESPACE__, $hoge_obj, $foo_obj);

は正しく実行でき、結果は次のとおりとなる。

string(8) "Name\Sub"
object(Name\Hoge)#1 (0) {
}

string(9) "Name\Sub2"
object(Name\Hoge)#2 (0) {
}

string(9) "Name\Sub3"
object(Name\Hoge)#1 (0) {
}
object(Name\Foo)#2 (0) {
}

基本的な use の使い方

use 名前空間\クラス名; と記述することで、現在の名前空間内でクラス名を省略して利用できるようになる。

エイリアス(as)で別名をつける

use 名前空間\クラス名 as 別名;

useでグループ化された

use 親名前空間{クラス1, クラス2};

例: use Name{Hoge, Foo}; → 一括で Name\Hoge と Name\Foo をインポート。

よって選択肢は⭕正解です