[模擬問題] PHP8上級 – マジックメソッド(CTO古庄道明からの出題)

こんにちは。PHP技術者認定機構 CTOの古庄道明です。
今日もPHP8上級試験の模擬問題と解説を紹介します。
以下を解いて出題のイメージと知識を補っていただけると幸いです。

目次

問題

メソッドに関する説明の中で、誤っているものを1つ選びなさい。
なお、すべてのコードの先頭には下記のコードが書かれているものとする。

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

選択肢

選択肢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 *** となる。

選択肢2
__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"

選択肢3
__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

選択肢4
__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)
}

回答と解説

問題は「マジックメソッド」についてです。
公式マニュアルの「マジックメソッド」のページを読んでおきましょう。
https://www.php.net/manual/ja/language.oop5.magic.php

出題のうち __get() は以下のページに記載されています。
https://www.php.net/manual/ja/language.oop5.overloading.php#object.get
こちらも併せて読んでおきましょう。

選択肢1を見ていきましょう。
__toString() は「クラスが文字列に変換される際の動作」を決めることができます。
そのため選択肢の最初のコード

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

    private $s = 'string';
}

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

を実行すると、
echo ‘object: ‘ . $obj , PHP_EOL;
のところで「インスタンスを文字列として解釈しようとしている」ため、__toString() が暗黙的に呼び出され、その結果以下が表示されます。
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;
}

を実行すると、catch 節のコードが動いて
*** test string ***
が出力されます。
なお蛇足ですが、PHP7.3の場合は
Fatal error: Method Hoge::__toString() must not throw an exception, caught Exception: *** test string *** in …
となります。

したがってこの選択肢には正しいことが書かれています。

選択肢2を見ていきましょう。
__invoke() メソッドは、 スクリプトがオブジェクトを関数として呼び出そうとした際に呼び出されます。
選択肢のコード

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

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

が実行されると、
$r = $obj();
のところが「オブジェクトを関数として呼び出そうとしている」ため、暗黙的に__invoke() が呼び出されます。
そのため、まず__invoke() が動いたタイミングで
object(Hoge)#1 (0) {
}
が出力されます。
また、
return ‘string’;
があるため左辺の $r にこれが代入されるので、
var_dump($r);
の結果として
string(6) “string”
が出力されます。

まとめると、以下が出力されます。

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

したがってこの選択肢には正しいことが書かれています。

選択肢3を見ていきましょう。
__get() は、アクセス不能(protected または private)または存在しないプロパティからデータを読み込む際に使用します。
そのため、選択肢のコードのうち

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

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

は「存在しないプロパティからデータを読み込む」挙動のため、__get() が呼び出されます。
その結果
not exist test
の文字が返されるので、そのまま出力されます。

__getStatic() は存在しません。
ただ、__get() は「プロパティのオーバーロードはオブジェクトのコンテキストでのみ動作します(マニュアルより)」。そのため、静的コンテキストでは動きません。
そのため
echo Hoge::$test2, PHP_EOL;
の部分は「存在しない static プロパティからデータを読み込む」動作のため、__get() は呼び出されず、以下のエラーとなります。

Fatal error: Uncaught Error: Access to undeclared static property Hoge::$test2 in …

したがってこの選択肢には誤りがあります。

選択肢4を見ていきましょう。
__debugInfo() はvar_dump() がオブジェクトをダンプするときに、プロパティの情報を取得するために呼ばれます。
このメソッドを使って、var_dump() で出力する内容を制御することができます。

選択肢のコードを少し改変した以下のコード

class Hoge {
    private $pass = 'password';
    private $s = 'string';
    private $i = 999;
}
$obj = new Hoge();
var_dump($obj);

を実行すると

object(Hoge)#1 (3) {
  ["pass":"Hoge":private]=>
  string(8) "password"
  ["s":"Hoge":private]=>
  string(6) "string"
  ["i":"Hoge":private]=>
  int(999)
}

となります。
一方で、選択肢にある、__debugInfo() を実装した以下のコード

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)
}

となります。
これは、var_dump() のタイミングで __debugInfo() が呼び出されて、returnしている s と i だけが出力されるためです。

したがってこの選択肢には正しいことが書かれています。

選択肢3に誤りがあるため、解答は3になります。

PHP8上級試験と公式問題集に興味がある方は、以下のページをご覧のうえ、この機会
に是非受験ください。

PHP8上級試験
https://www.phpexam.jp/summary/expert8

公式問題集:PHP8技術者認定上級試験公式問題集上巻
https://www.amazon.co.jp/dp/B0FQ1LTTQ4/

公式問題集:PHP8技術者認定上級試験公式問題集下巻ベータ
https://www.amazon.co.jp/dp/B0F7F4H8FP/

また、試験対策セミナーは毎月開催しています。興味がある方は以下のページをご覧いただき是非セミナーにもご参加ください。(フォローいただけるとセミナーのご案内が届くようになります)

PHP技術者認定機構の公式Peatixグループ
https://peatix.com/group/43424

この記事が気に入ったら
いいね または フォローしてね!

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次