
PHP8 上級/準上級試験の認定模擬問題として、プライム・ストラテジー様のPRIME STUDYが認定されているようです。ただ、この模擬問題には解説がありませんので解説をまとめていこうと思います。シリーズ第5回目です。

PHP初級レベルから脱却して中級/上級レベルにいきたい人にピッタリの内容ですね。

今回はメソッドについての問題です。__toString() や __invoke()、__get()、__debugInfo() などのマジックメソッドの挙動について理解しているかが問われます。PHPのクラス機能を深く扱う上で重要な知識なので、動作の仕組みを一つひとつ確認しておきましょう。
PRIME STUDY認定模擬問題のリンクはこちらです → https://study.prime-strategy.co.jp/
問題文
メソッドに関する説明の中で、誤っているものを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)
}
解説
選択肢1
正しいです。
__toString() メソッドによりクラスが文字列に変換される際の動作を定義できます。また、PHP 7.4 以降では例外をスローすることも可能です。
順を追ってみていきましょう。
__toString() は、オブジェクトが文字列として扱われるときの振る舞いを決める特別なメソッドです。たとえば、通常はオブジェクトを echo で出力しようとするとエラーになります。ですが、__toString() を定義すると、オブジェクトを文字列に変換できるようになります。
1つ目のコードを見ていきます。
(1~2行目)まず、クラス Hoge には __toString() が定義されています。
(3行目)次に、__toString() の中では、$this->s を返しています。
(6行目)$s は ‘string’ という値を持っています。
(11行目)echo ‘object: ‘ . $obj の部分で、オブジェクトを文字列として出力しようとしたため、 __toString() が実行され、$s の値 ‘string’ が返されます。その結果、「object: string」が出力されます。
次に2つ目のコードです。
PHP 7.3 までは __toString() の中で例外(エラー)を投げることができませんでしたが、PHP 7.4 からは可能になりました。
(3行目)クラス Hoge の __toString() メソッドは、throw new \Exception(‘*** test string ***’); によって例外を投げます。
(11行目)echo ‘object: ‘ . $obj を実行すると __toString() が呼ばれ、例外が発生します。
(9~14行目)例外が発生すると、 try { … } catch (\Throwable $e) { … } の catch に処理が移り、$e->getMessage() を出力します。 getMessage() の内容は *** test string *** なので、最終的に *** test string *** が出力されます。
選択肢2
正しいです。
__invoke() は、オブジェクトを 関数のように呼び出したときに実行される特別なメソッドです。
通常、オブジェクトをそのまま () をつけて実行しようとするとエラーになりますが、__invoke() を定義すると、その中で必要な処理を書くことができます。
コードを追っていきましょう。
(2~4行目)まず、Hoge クラスを作成。 __invoke() メソッドを定義しています。var_dump($this); で自分自身($this)を表示した後、’string’ という文字列をreturnしています。
(8~10行目)Hogeクラスをnewしたあと、オブジェクトを関数のように呼び出します。$obj(); のように () をつけて呼び出しています。すると、__invoke() が自動的に実行されます。
これにより、var_dump($this); が実行され、自分自身(Hoge のオブジェクト)が表示されます。また、’string’ を返すので、それが $r に代入されます。最後に、$r の値(’string’)を var_dump() で表示します。
選択肢3
誤っています。
__get() は、存在しないプロパティ や アクセスできないプロパティ(private / protected) にアクセスしようとしたときに自動的に呼び出されます。
コードを見ていきましょう。
Hoge クラスには test というプロパティはありません。ですが、 $obj->test にアクセスすると、__get(‘test’) が自動的に呼ばれ、 “not exist test” という文字列を返します。
__get() は インスタンスのプロパティ にアクセスしたときのみ呼ばれます。静的プロパティ (Hoge::$test2) に対しては動作せず、エラーとなります。
PHPには __getStatic() というメソッドは存在しません。もし静的なプロパティアクセス (Hoge::$test2) に対して似た動作をさせたい場合は、代替として__callStatic() を使いましょう。
選択肢4
正しいです。
__debugInfo() は、var_dump() を使ってオブジェクトを出力するときに、表示されるプロパティの情報をカスタマイズできる 特殊なメソッド(マジックメソッド)です。
順を追って解説していきます。
PHP で var_dump($obj) を実行すると、オブジェクト内のすべてのプロパティ(public / protected / private) が表示されます。例えば、次のようなクラスを考えます。
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)
}
private なプロパティ pass, s, i もすべて var_dump() によって表示されてしまいます。ですが、pass にはパスワードのような 機密情報 が含まれているため、ログやデバッグ時に表示されると セキュリティリスク になることがあります。
__debugInfo() を実装するとどうなるか?
__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)
}
pass が var_dump() の出力から 除外され、 代わりに、s と i だけが表示されています。
このように、__debugInfo() メソッドを実装すると、var_dump() の出力をカスタマイズできます。
正解選択肢
以上より選択肢3が正解となります。
他の問題へ
模擬問題(1)インストール
模擬問題(2)変数の型
模擬問題(3)クラス
模擬問題(4)メソッドその1
模擬問題(5)メソッドその2
模擬問題(6)リファレンス
模擬問題(7)名前空間
模擬問題(8)エラー
模擬問題(9)定義済みのインターフェイスとクラスおよび SPL インターフェイス
模擬問題(10)SPL
模擬問題(11)定義済の変数
模擬問題(12)PHP7.0.xからPHP7.1.xへの移行
模擬問題(13)可変変数
模擬問題(14)PHP7.3.xからPHP7.4.xへの移行
模擬問題(15)PHP7.4.xからPHP8.0.xへの移行
模擬問題(16)制御構造
模擬問題(17)XSS(クロスサイトスクリプティング)
模擬問題(18)ファイルアップロード
模擬問題(19)推測困難なトークン
模擬問題(20)セッション
模擬問題(21)PHP のメモリ消費
模擬問題(22)関数その1
模擬問題(23)関数その2
模擬問題(24)関数その3
模擬問題(25)関数その4
模擬問題(26)関数その5
模擬問題(27)関数その6
模擬問題(28)関数その7
模擬問題(29)関数その8
模擬問題(30)関数その9
コメント