プログラミング

プログラミング初心者のPHP修羅の道#9〜PHP8上級問題を徹底追求する〜

PHP8上級資格取得へ向けたアウトプット記事になります。

PHP試験運営団体より公開されている模擬試験を1つ1つみていく形で事細かくみていきますが、勉強途中ゆえ間違っている部分に関してはご了承ください。

PHP上級資格のメイン教材はPHPマニュアルと指定されていますので、このマニュアルを主に踏襲した内容となっています。

主な勉強教材は以下になります。

1.独習PHP第4版

2.はじめてのPHP

 

3.PHP公式マニュアル

 

PHP8上級試験模擬問題

試験問題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より前に宣言されたクラスは宣言した名前空間には含まれない(「<?php」以外の全ての文字や改行などは存在不可)

・明示的に名前空間を宣言せずにクラスの宣言を行った場合、グローバル名前空間としてトップ階層の名前空間に属するものとみなされる

・「非修飾名」とは、名前空間を含まない名前のこと相対パスのようなイメージ

修飾名」とは、名前空間の階層区切りを含んだ名前のこと相対パスのようなイメージ

完全修飾名」とは、「\」で始まる修飾名のことで絶対パスのようなイメージ

/*このファイルはaaa/bbb/ccc/Myclass.php*/
namespace aaa\bbb\ccc;

class Myclass{
  public static function showClass():void{
    print __CLASS__;
  }
}
//非修飾名
namespace aaa\bbb\ccc{
    require_once 'MyClass.php';
    print MyClass::showClass().' :非修飾名'."\n";
}
//修飾名
namespace aaa\bbb{
    require_once 'MyClass.php';
    print ccc\MyClass::showClass().' :修飾名'."\n";
}
//完全修飾名
namespace{
    require_once 'MyClass.php';
    print \aaa\bbb\ccc\MyClass::showClass().' :完全修飾名';
}

/*
出力結果
aaa\bbb\ccc\MyClass :非修飾名
aaa\bbb\ccc\MyClass :修飾名
aaa\bbb\ccc\MyClass :完全修飾名
*/

 

 

この選択肢のコードではnamespaceキーワードよりも前にHogeクラスが宣言されているため、誤りとなります。

 

選択肢②

 

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

namespace Name¥SubName;

class Hoge {
}

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

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

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

 

ディレクトリやファイルと同様、PHP の名前空間においても名前空間の階層構造を指定することができるため、この選択肢は合っています。

 

選択肢③

 

名前空間の定義がない場合、すべてのクラスや関数の定義はグローバル空間に配置される。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 となる。

 

ここでは選択肢①で紹介した「完全修飾名」について問われています。

また、Exceptionクラスはすべてのユーザー定義例外のスーパークラス(基底クラス)です。

Exceptionクラスを利用したい場合は、グローバル名前空間に属するExceptionクラスを利用することになるため、「new ¥Exception()」と記述する必要があります。

よって、この選択肢は合っています。

 

選択肢④

 

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キーワードによって型のインポートを行うことで、それ以降は名前空間を省略して本来のクラス名だけで表記できるようになるものです。

useキーワードの基本

use命令はクラス、関数、制御文などのスコープの内部で呼び出せないため、必ずグローバルスコープかnamespaceブロック配下で呼び出す(use命令はコンパイルのタイミングでインポートを実行するため)

・インポートの有効範囲はファイル単位(現在のファイルでのみ認識

//現在のクラスを出力するファイル(MyClass.php)
namespace aaa\bbb\ccc;

class MyClass{
  public static function showClass():void{
    print __CLASS__;
  }
}
//Main.php
require_once 'MyClass.php';

use aaa\bbb\ccc\MyClass;

print MyClass::showClass(); //出力結果:aaa\bbb\ccc\MyClass
//require_once 'Included.php';
//Included.php
print MyClass::showClass();
/*
インクルード先のこのファイルでは型のインポートが認識されないため、
not found となる
*/

・定数(use const)、関数(use function)のインポートも可能

・名前空間をインポートした場合は、名前空間の最後+定数名/関数名でそれぞれ定数/関数を呼び出せる

//MyClass.php
namespace aaa\bbb\ccc{
    
    const AUTHER = 'PHP太郎';
    
    function hoge():string{
        return 'Hello World';
    }
}
//Main.php
require_once 'MyClass.php';

use const aaa\bbb\ccc\AUTHER;
print AUTHER; //出力結果:PHP太郎
print PHP_EOL;

use function aaa\bbb\ccc\hoge;
print hoge(); //出力結果:Hello World
print PHP_EOL;

/*名前空間をインポート*/
use aaa\bbb\ccc;

print ccc\AUTHER; //出力結果:PHP太郎
print PHP_EOL;
print ccc\hoge(); //出力結果:Hello World

as句を付与することでエイリアス(別名)をつけることが可能

//現在のクラスを出力するファイル(MyClass.php)
namespace aaa\bbb\ccc{
    class MyClass{
        public static function showClass():void{
            print __CLASS__;
        }
    }
}
//Main.php
require_once 'MyClass.php';
require_once 'MyClass2.php';

use aaa\bbb\ccc\MyClass;
print MyClass::showClass(); //出力結果:aaa\bbb\ccc\MyClass
print PHP_EOL;

use aaa\bbb\ccc\ddd\MyClass as AnotherClass;
print AnotherClass::showClass(); //出力結果:aaa\bbb\ccc\ddd\AnotherClass

//現在のクラスを出力するファイル(MyClass2.php)
namespace aaa\bbb\ccc\ddd{
    class MyClass{
        public static function showClass():void{
            print __CLASS__;
        }
    }
}

 

・use命令のグループ化も可能

//現在のクラスを出力するファイル(MyClass.php)
namespace aaa\bbb\ccc{
    class MyClass{
        public static function showClass():void{
            print __CLASS__;
        }
    }
    
    class AnotherClass{
        public static function showClass():void{
            print __CLASS__;
        }
    }
}
//Main.php
require_once 'MyClass.php';

use aaa\bbb\ccc\{MyClass, AnotherClass as Ano};

print MyClass::showClass(); //出力結果:aaa\bbb\ccc\MyClass
print PHP_EOL;
print Ano::showClass(); //出力結果:aaa\bbb\ccc\AnotherClass

 

 

 

よって、この選択肢は合っています。

ABOUT ME
ヒロ
社会人4年目/25歳/食品商社で2年間営業した後、IT業界にシステムエンジニアとして転職/Java,PHP言語を扱う開発エンジニア