• PHP
【php8上級/準上級試験】模擬問題解説 問題5. __toString() 「例外を投げる(throw new \Exception)とは?」 __invoke() __get() __debugInfo()

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

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

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

解説記事一覧

模擬問題 5

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

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

__toString() メソッドにより、クラスが文字列に変換される際の動作を決めることができる。
そのため、以下のコード

class Hoge {
    public function __toString() {
        return $this->s;
    }

    private $s = 'string';
}

$obj = new Hoge();
echo 'object: ' . $obj , PHP_EOL;

は正しく実行でき、結果は object: string となる。

また、__toString() メソッドで例外を投げる事は、PHP 7.3 までは出来なかったが、PHP 7.4 からは出来るようになった。
そのため、以下のコード

class Hoge {
    public function __toString() {
        throw new \Exception('*** test string ***');
    }

    private $s = 'string';
}

try {
    $obj = new Hoge();
    echo 'object: ' . $obj, PHP_EOL;
} catch(\Throwable $e) {
    echo $e->getMessage(), PHP_EOL;
}

を実行すると *** test string *** となる。

__toString()について

__toString() メソッドは、「オブジェクトを文字列として扱うときに、どんな文字列にするか」を決めるメソッドです。

例えば、echo したりするときに、このメソッドが自動で呼び出されます。

そのうえで、「PHP 7.4 以降は __toString() でも例外を投げられる」ようになりました。

例外を投げる(throw new \Exception)とは?

プログラムを強制的に中断させます

throw new \Exception('エラーメッセージ');
echo 'not finished';

// 出力結果
PHP Fatal error:  Uncaught Exception: エラーメッセージ

通常は try / catch ブロックでエラーハンドリングをします!

tryの中で例外を投げられるとtryブロックの残りの処理はスキップされcatchブロックにジャンプします

try {
    throw new \Exception('エラーメッセージ');
} catch(\Throwable $e) {
    echo $e->getMessage();
}

echo ' not finished';

//出力結果
エラーメッセージ not finished
try-catch の流れ try { 通常の処理1 例外発生! 処理2(スキップ) 処理3(スキップ) 例外発生後は ここをスキップ catch (Exception $e) { エラー処理

問題文では下記の流れで例外がキャッチされます

  1. $obj を 文字列として扱おうとしている
  2. __toString() が 自動的に呼ばれる
  3. __toString() の中で throw new \Exception(‘*** test string ***’); を実行
*** test string ***

よって選択肢は⭕です

__invoke() メソッドは、 スクリプトがオブジェクトを関数としてコールしようとした際にコールされる。
そのため、以下のコード

class Hoge {
    public function __invoke() {
        var_dump($this);
        return 'string';
    }
}

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

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

object(Hoge)#1 (0) {
}
string(6) "string"

__invoke() メソッド

__invoke() メソッドは、 スクリプトがオブジェクトを関数としてコールしようとした際にコールされます。

なので問題だと$objはオブジェクトですが、$obj();関数として呼び出されているので__invokeが呼び出されます

⬇️

var_dump($this)の処理について

そもそも$thisとは

$thisは現在のオブジェクトインスタンスを指す特別な変数

var_dumpでオブジェクトを出力するときのフォーマット

object(クラス名)#オブジェクトID (プロパティの数) { プロパティの内容 }

class Test {}
$obj = new Test();
var_dump($obj);
// 出力: object(Test)#1 (0) { }

var_dumpで文字列を出力するときのフォーマット

object(クラス名)#オブ$str = "hello";
var_dump($str);
// 出力: string(5) "hello"ジェクトID (プロパティの数) { プロパティの内容 }

となるので問題文の通りの出力結果となり選択肢は⭕です

__get() は、アクセス不能 (protected または private) または存在しないプロパティからデータを読み込む際に使用する。
__getStatic() は存在せず、オブジェクトのコンテキスト、静的コンテキストのどちらでも動く。
そのため、以下のコード

class Hoge {
    public function __get(string $name) {
        return "not exist {$name}";
    }
}

$obj = new Hoge();
echo $obj->test, PHP_EOL;
echo Hoge::$test2, PHP_EOL;

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

not exist test
not exist test2

__getメソッドとは

__get は下記のケースでアクセスしようとした際に呼び出されます(データを読み込もうとしたとき)

__get が呼び出される場合 1. アクセスできないプロパティへのアクセス時 (private, protected) 2. 存在しないプロパティへのアクセス時

問題文で「静的コンテキストのでも動く」とありますが、間違ってます

__getは インスタンス化されたものに対して アクセスされた場合に呼び出される

Hoge::$test2のような静的なアクセスの場合は__getは呼び出されません。
このようなアクセスではエラーが発生します。

よって選択肢は誤り❌です

インスタンス用 __get() __set() __call() __isset() 静的(static)用 __callStatic()

__debugInfo() メソッドが実装されていると、var_dump() でオブジェクトをダンプするときに出力するプロパティの情報を制御できる。
そのため、以下のコード

class Hoge {
    public function __debugInfo() {
        return [
            's' => $this->s,
            'i' => $this->i,
        ];
    }

    private $pass = 'password';
    private $s = 'string';
    private $i = 999;
}

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

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

object(Hoge)#1 (2) {
  ["s"]=>
  string(6) "string"
  ["i"]=>
  int(999)
}

__debugInfo()とは

var_dump()が呼ばれた時のオブジェクトの出力を制御するために使用します。

問題文のようにセキュリティ上重要な情報(パスワードなど)を隠し、表示したい情報だけを選んで返せます!

__debugInfo()がない場合は、アクセス可能なすべてのプロパティが表示されます。

よって選択肢は⭕です