この記事のキーワード
マジックメソッド・__toString()・__invoke()・__get()・__debugInfo()・オーバーロード
PHP8上級資格取得へ向けたアウトプット記事になります。
PHP試験運営団体より公開されている模擬試験を1つ1つみていく形で事細かくみていきますが、勉強途中ゆえ間違っている部分に関してはご了承ください。
PHP上級資格のメイン教材はPHPマニュアルと指定されていますので、このマニュアルを主に踏襲した内容となっています。
試験問題1-2 | 模擬試験を解く#1 |
---|---|
試験問題3 | 模擬試験を解く#2 |
試験問題4 | 模擬試験を解く#3 |
本記事 | 模擬試験を解く#4 |
試験問題6 | 模擬試験を解く#5 |
試験問題7 | 模擬試験を解く#6 |
試験問題8 | 模擬試験を解く#7 |
主な勉強教材は以下になります。
1.独習PHP第4版
2.はじめてのPHP
3.PHP公式マニュアル
PHP8上級試験模擬問題
試験問題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 ***
となる。
前回の記事「試験問題④」でマジックメソッドの説明を行いました。
この選択肢ではそのうちの1つ「__toString()」について聞かれています。
既定ではオブジェクトをprint命令などで出力することはできませんが、__toString()はオブジェクトがこれらの命令によって要求されたタイミングで呼び出されます。
以下のコードはコンストラクタによって初期化された変数$a,$bに対して、__toString()メソッドを利用することでプロパティ値にアクセスすることなく文字列表現を取得しています。
class TestClass{
public $a;
public $b;
public function __construct($a,$b){
$this->a = $a;
$this->b = $b;
}
public function __toString(){
return $this->a.$this->b;
}
}
$obj = new TestClass("aaa","bbb");
var_dump ($obj);
//////////////////出力結果//////////////////
object(TestClass)#1 (2) {
["a"]=>
string(3) "aaa"
["b"]=>
string(3) "bbb"
}
・戻り値が必要
オブジェクトが文字列に変換された場合の結果を決めるメソッドのため、オブジェクトの文字列表現を返す必要があります。
・PHP7.4.0以前
戻り値が文字列でなければE_RECOVERABLE_ERRORが発生。
また、__toString()メソッド内から例外をスローすることができないため、致命的なエラーが発生します。
・PHP7.4
戻り値が文字列でなければERRORがスローされる。
例外はスロー可能。
・PHP8.0.0以降
厳密な型付けが無効になっている場合、可能であれば文字列型に強制されます。
また、__toString()メソッドを含む全てのクラスは、暗黙のうちにStringable インターフェイスも実装するようになりました。
例外はスロー可能。
よって、この選択肢は合っています。
選択肢②
__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"
「試験問題④」で扱ったマジックメソッドのうちの1つ「__invoke()」について聞かれています。
こちらのメソッドは「オブジェクトが関数の形式で呼び出された場合」に実行されます。
__invoke()メソッドは他のマジックメソッドとは異なり、任意の引数を受け取り、任意の戻り値を返すことが可能です。
以下のコードでは、オブジェクトを関数形式で呼び出すことで、__invoke()メソッドが実行されていることを確認できます。
class Sort
{
private $key;
public function __construct(string $key)
{
$this->key = $key;
}
public function __invoke(array $a, array $b): int
{
return $a[$this->key] <=> $b[$this->key];
}
}
$customers = [
['id' => 1, 'first_name' => 'John', 'last_name' => 'Do'],
['id' => 3, 'first_name' => 'Alice', 'last_name' => 'Gustav'],
['id' => 2, 'first_name' => 'Bob', 'last_name' => 'Filipe']
];
// $customers を 'first_name' でソートします
usort($customers, new Sort('first_name')); //オブジェクトを関数形式で呼び出す
print_r($customers);
// $customers を 'last_name' でソートします
usort($customers, new Sort('last_name')); //オブジェクトを関数形式で呼び出す
print_r($customers);
出力結果は以下のようになります。
Array
(
[0] => Array
(
[id] => 3
[first_name] => Alice
[last_name] => Gustav
)
[1] => Array
(
[id] => 2
[first_name] => Bob
[last_name] => Filipe
)
[2] => Array
(
[id] => 1
[first_name] => John
[last_name] => Do
)
)
Array
(
[0] => Array
(
[id] => 1
[first_name] => John
[last_name] => Do
)
[1] => Array
(
[id] => 2
[first_name] => Bob
[last_name] => Filipe
)
[2] => Array
(
[id] => 3
[first_name] => Alice
[last_name] => Gustav
)
)
よって、この選択肢は合っています。
選択肢③
__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
PHPにおいてマジックメソッドを扱う上で関連するワードに「オーバーロード」というものがあります。
・PHP におけるオーバーロード機能は、 プロパティやメソッドを動的に 作成する
ための手法で、これはマジックメソッドを用いて処理される。
・オーバーロードメソッドはすべてpublicで定義される必要がある。
・マジックメソッドの引数はリファレンス渡しできない
・プロパティのオーバーロードはオブジェクトのコンテキストでのみ動作するため、これらのマジックメソッドはstaticメソッドとしては呼び出されず、staticメソッドとして宣言してもいけません。
一般的な「オーバーロード」とはPHPでの解釈とは異なり、 「名前は同じだけれども引数の数や型が異なるメソッドを複数用意できる」 という機能のことを指します。
プロパティのオーバーロード
$name:プロパティ名、 $value:プロパティ値
・public __set(string $name
, mixed $value
): void
・public __get(string $name
): mixed
・public __isset(string $name
): bool
・public __unset(string $name
): void
プロパティのオーバーロードの例は以下のようになります。
class PropertyTest{
/** オーバーロードされるデータの場所 */
private $data = array();
public function __set($name, $value){
echo "Setting '$name' to '$value'\n";
$this->data[$name] = $value;
}
public function __get($name){
echo "Getting '$name'\n";
if (array_key_exists($name, $this->data)) {
return $this->data[$name];
}
}
}
$obj = new PropertyTest;
$obj->a = 1;
echo $obj->a;
//出力結果
//Setting 'a' to '1'
//Getting 'a'
//1
よって、この選択肢は誤りです。
選択肢④
__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()」はマジックメソッドの1つです。
このメソッドは「var_dump()」がオブジェクトを出力する時に、プロパティの情報を取得するために呼ばれます。
・デフォルトでは全ての「public, protected, private」プロパティを出力
・出力値を「名前=>値」のようにカスタマイズできる
class C {
private $str;
public function __construct($str) {
$this->str = $str;
}
public function __debugInfo():array {
return [
'proper' => $this->str . 'add',
];
}
}
$a = new C('value');
var_dump($a);
//出力結果
/*
object(C)#1 (1) {
["proper"]=>
string(8) "valueadd"
}
*/
よって、この選択肢は合っています。