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

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

目次

問題

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

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

選択肢

選択肢1
PHP において、コンストラクタは、__construct() メソッドで実装される。
そのため、次のコード

class Hoge {
    public function __construct() {
        echo __METHOD__, PHP_EOL;
    }
}

$obj = new Hoge();

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

なお、コンストラクタを有している場合、親クラスのコンストラクタが暗黙の内にコールされることはない。
そのため、次のコード

class Hoge {
    public function __construct() {
        echo __METHOD__, PHP_EOL;
    }
}

class Foo extends Hoge{
    public function __construct() {
        echo __METHOD__, PHP_EOL;
    }
}

$obj = new Foo();

を実行すると、結果は Foo::__construct となる。

親クラスのコンストラクタを実装する場合には、parent::をコールすることが必要となる。
そのため、次のコード

class Hoge {
    public function __construct() {
        echo __METHOD__, PHP_EOL;
    }
}

class Foo extends Hoge{
    public function __construct() {
        parent::__construct();
        echo __METHOD__, PHP_EOL;
    }
}

$obj = new Foo();

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

Hoge::__construct
Foo::__construct

選択肢2
PHP において、デストラクタは、__destruct() メソッドで実装される。
そのため、次のコード

class Hoge {
    public function __destruct() {
        echo __METHOD__, PHP_EOL;
    }
}

$obj = new Hoge();

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

なお、デストラクタを有している場合、親クラスのデストラクタは、暗黙の内にコールされる。コールされる順番は「子クラスのデストラクタ → 親クラスのデストラク
タ」の順番である。
そのため、次のコード

class Hoge {
    public function __destruct() {
        echo __METHOD__, PHP_EOL;
    }
}

class Foo extends Hoge{
    public function __destruct() {
        echo __METHOD__, PHP_EOL;
    }
}

$obj = new Foo();

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

Foo::__destruct
Hoge::__destruct

選択肢3
__call() マジックメソッドを使うと「アクセス不能なメソッド(存在しない、またはアクセスできないメソッド)がオブジェクトのコンテキストで呼び出された時」に、
処理を入れることができる。
そのため、次のコード

class Hoge {
    public function __call(string $name, array $arguments) {
        echo "call: {$name}", PHP_EOL;
        var_dump($arguments);
        echo PHP_EOL;
    }
}

$obj = new Hoge();
$obj->test();
$obj->test2(1, '2', [3]);

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

call: test
array(0) {
}

call: test2
array(3) {
  [0]=>
  int(1)
  [1]=>
  string(1) "2"
  [2]=>
  array(1) {
    [0]=>
    int(3)
  }
}

選択肢4
__callStatic() マジックメソッドを使うと「アクセス不能なメソッド(存在しない、またはアクセスできないメソッド)が静的コンテキストで呼び出された時」に、処理を入れることができる。

そのため、次のコード

class Hoge {
    public static function __callStatic(string $name, array $arguments) {
        echo "call: {$name}", PHP_EOL;
        var_dump($arguments);
    }
}

Hoge::test(1, '2', [3]);

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

call: test
array(3) {
  [0]=>
  int(1)
  [1]=>
  string(1) "2"
  [2]=>
  array(1) {
    [0]=>
    int(3)
  }
}

なお、__callStatic() マジックメソッドが動くのは「静的コンテキストで呼び出された時」だけのため、「オブジェクトのコンテキストで呼び出された時」には動かない。
そのため、次のコード

class Hoge {
    public static function __callStatic(string $name, array $arguments) {
        echo "call: {$name}", PHP_EOL;
        var_dump($arguments);
    }
}

$obj = new Hoge();
$obj->test();

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

Fatal error: Uncaught Error: Call to undefined method Hoge::test() in ...

回答と解説

問題は「マジックメソッド」についてです。
公式マニュアルの「マジックメソッド」のページを読んでおきましょう。
https://www.php.net/manual/ja/language.oop5.magic.php
また、いくつかのコードではコンストラクタとデストラクタについて書かれています。
PHPではコンストラクタとデストラクタもマジックメソッドですが、現在は別URLに記載があるので、以下のページも読んでおきましょう。
https://www.php.net/manual/ja/language.oop5.decon.php#object.construct

選択肢1を見ていきましょう。
まず、マジックメソッドは「ある動作がオブジェクトに対して行われた場合に、PHPのデフォルトの動作を上書きする特別なメソッド」です。
また、コンストラクタは「newで新たにオブジェクトが生成されるタイミングで呼び出される」マジックメソッドです。
そのため、選択肢の最初のコード

class Hoge {
    public function __construct() {
        echo __METHOD__, PHP_EOL;
    }
}
$obj = new Hoge();

が実行されると、new Hoge() のタイミングで __construct() が呼び出されるため

Hoge::__construct

が出力されます。

また、PHPでは、コンストラクタによらず、どんなメソッドであっても、メソッドをオーバーライドして子クラスのメソッドを呼び出した時に「親クラスの同名のメソッ
ドが暗黙の内にコールされる」ことはありません。
https://www.php.net/manual/ja/language.oop5.decon.php
「注意: 子クラスがコンストラクタを有している場合、親クラスのコンストラクタが 暗黙の内にコールされることはありません」
そのため、二番目のコード

class Hoge {
    public function __construct() {
        echo __METHOD__, PHP_EOL;
    }
}
class Foo extends Hoge{
    public function __construct() {
        echo __METHOD__, PHP_EOL;
    }
}
$obj = new Foo();

においては、「Fooクラスのコンストラクタ」しか呼ばれないため

Foo::__construct

となります。

オーバーライドしたメソッドは、parent::を使って「オーバーライドした元のメソッド」を呼び出すことができます。
https://www.php.net/manual/ja/language.oop5.basic.php#language.oop5.basic.extends
「オーバーライドされた元のメソッドやstaticプロパティにアクセスするには、 parent:: で参照します。」
そのため、三番目のコード

class Hoge {
    public function __construct() {
        echo __METHOD__, PHP_EOL;
    }
}
class Foo extends Hoge{
    public function __construct() {
        parent::__construct();
        echo __METHOD__, PHP_EOL;
    }
}
$obj = new Foo();

を実行すると
・Fooのコンストラクタが呼び出される
・parent::__construct() があるので、Hogeのコンストラクタが呼ばれる
・Foo コンストラクタ側の echo __METHOD__, PHP_EOL; が実行される
ため

Hoge::__construct
Foo::__construct

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

選択肢2を見ていきましょう。
デストラクタは「オブジェクトが破棄されるタイミングで呼び出される」マジックメソッドです。
そのため、選択肢の最初のコード

class Hoge {
    public function __destruct() {
        echo __METHOD__, PHP_EOL;
    }
}
$obj = new Hoge();

は、$objが破棄されるタイミングでデストラクタが動くため
Hoge::__destruct
となります。

コンストラクタのところで書きましたが、PHPではどんなメソッドであっても、メソッドをオーバーライドして子クラスのメソッドを呼び出した時に「親クラスの同名のメソッドが暗黙の内にコールされる」ことはありません。
https://www.php.net/manual/ja/language.oop5.decon.php#language.oop5.decon.destructor
「コンストラクタと同様、親クラスのデストラクタがエンジンにより暗黙のうちにコールされるということはありません。」
そのため、二番目のコード

class Hoge {
    public function __destruct() {
        echo __METHOD__, PHP_EOL;
    }
}
class Foo extends Hoge{
    public function __destruct() {
        echo __METHOD__, PHP_EOL;
    }
}
$obj = new Foo();

を実行すると、$objが破棄されるタイミングで・Fooのデストラクタが呼び出される
・(暗黙的にオーバーライドされた元のメソッドを呼び出すことはないので、Hoge クラスのデストラクタはcallされない)
・Foo デストラクタ側の echo __METHOD__, PHP_EOL; が実行される
ため、

Foo::__destruct

となります。

設問には
・暗黙的にオーバーライドされた元のメソッドを呼び出す・結果として、 Hoge::__destruct も出力されているため、この選択肢には誤りがあります。

選択肢3を見ていきましょう。
__call() マジックメソッドを使うと「アクセス不能なメソッドがオブジェクトのコンテキストで呼び出された時」に、処理を入れることができます。
https://www.php.net/manual/ja/language.oop5.overloading.php#object.call
関数宣言は以下の通りです。

public __call(string $name, array $arguments): mixed

引数の、$nameには「呼び出そうとしたメソッド名」が、$argumentsには「引数の配列」が入ってきます。
そのため

class Hoge {
    public function __call(string $name, array $arguments) {
        echo "call: {$name}", PHP_EOL;
        var_dump($arguments);
        echo PHP_EOL;
    }
}
$obj = new Hoge();
$obj->test();
$obj->test2(1, '2', [3]);

を実行すると、まず

$obj->test();

のところで
・呼び出そうとしたメソッド名が test なので、 call: test
・引数は空なので

array(0) {
}

となります。
次に

$obj->test2(1, '2', [3]);

が実行されると
・呼び出そうとしたメソッド名が test2 なので、 call: test2
・引数があるため

array(3) {
  [0]=>
  int(1)
  [1]=>
  string(1) "2"
  [2]=>
  array(1) {
    [0]=>
    int(3)
  }
}

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

選択肢4を見ていきましょう。
__callStatic() マジックメソッドを使うと「アクセス不能なメソッドが静的コンテキストで呼び出された時」に、処理を入れることができます。
https://www.php.net/manual/ja/language.oop5.overloading.php#object.callstatic
関数宣言は以下の通りです。

public static __callStatic(string $name, array $arguments): mixed

引数の、$nameには「呼び出そうとしたメソッド名」が、$argumentsには「引数の配列」が入ってきます、というあたりは、選択肢3の__call()と同様になります。

そのため

class Hoge {
    public static function __callStatic(string $name, array $arguments) {
        echo "call: {$name}", PHP_EOL;
        var_dump($arguments);
    }
}
Hoge::test(1, '2', [3]);

を実行すると、 Hoge::test(1, ‘2’, [3]); のタイミングで
・呼び出そうとしたメソッド名が test なので、 call: test
・引数があるため

array(3) {
  [0]=>
  int(1)
  [1]=>
  string(1) "2"
  [2]=>
  array(1) {
    [0]=>
    int(3)
  }
}

となります。
また、__callStatic() はあくまでも「静的コンテキストで呼び出された時」に呼び出されるため、二番目のコード

class Hoge {
    public static function __callStatic(string $name, array $arguments) {
        echo "call: {$name}", PHP_EOL;
        var_dump($arguments);
    }
}
$obj = new Hoge();
$obj->test();

を実行すると、$obj->test(); のタイミングで
・メソッドは存在しない
・__call()も存在しない
・そのため、エラーになる
ので、選択肢の通り

Fatal error: Uncaught Error: Call to undefined method Hoge::test() in ...

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

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

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
www.phpexam.jp

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

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