[模擬問題] PHP8上級 ? クラスとオブジェクト(CTO古庄道明からの出題)

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

目次

問題

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

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

選択肢

選択肢1
三つの特別なキーワード self と parent そして static がクラス定義の内部からプロパティまたはメソッドにアクセスする際に使用される。
そのため、以下のコード

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

    public static function testStatic() {
        echo __METHOD__, PHP_EOL;
    }

    public static function staticCall() {
        self::testStatic();
        static::testStatic();
    }
}

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

    public static function testStatic() {
      echo __METHOD__, PHP_EOL;
    }
}

$obj = new Foo();
$obj->test_1();
Foo::staticCall();

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

Hoge::test_1
Foo::test_1
Hoge::testStatic
Foo::testStatic

選択肢2
static メソッドはオブジェクトのインスタンスを生成せずにコールするため、疑似変数 $this は、 static として宣言されたメソッドの内部から利用することはでき
ない。
そのため、以下のコード

class Hoge {
    public static function test() {
        var_dump($this);
    }
}

Hoge::test();

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

Fatal error: Uncaught Error: Using $this when not in object context in ...

選択肢3
static プロパティは、アロー演算子 -> によりオブジェクトからアクセスすることはできない。
そのため、以下のコード

class Hoge {
    static $i = 100;

    public function test() {
        echo $this->i;
    }
}

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

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

Notice: Accessing static property Hoge::$i as non static in ...
Warning: Undefined property: Hoge::$i in ...

選択肢4
抽象クラスから継承する際、親クラスの宣言で abstract としてマークされた全てのメソッドは、子クラスで定義されなければならない。

また、これらのメソッドは同等 (あるいはより緩い制約) の可視性で定義される必要がある。

一方でメソッドのシグネチャ (引数の型と順番) については、必須引数の数は同じである必要があるが、型宣言は異なってもかまわない。

そのため、以下のコード

abstract class Hoge {
    abstract public function __construct(string $s, array $a);
    protected $s;
    protected $a;
}

class Foo extends Hoge {
    public function __construct(string $s, \arrayObject $a) {
        $this->s = $s;
        $this->a = $a;
    }
}

$obj = new Foo('', new \arrayObject());
var_dump($obj);

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

object(Foo)#1 (2) {
  ["s":protected]=>
  string(0) ""
  ["a":protected]=>
  object(ArrayObject)#2 (1) {
    ["storage":"ArrayObject":private]=>
    array(0) {
    }
  }
}

回答と解説

問題は「クラスとオブジェクト」についてです。
公式マニュアルの「クラスとオブジェクト」のページを読んでおきましょう。
https://www.php.net/manual/ja/language.oop5.php

選択肢1を見ていきましょう。
self と parent そして static は、いずれもクラス定義の内部からプロパティまたはメソッドにアクセスする際に使用されます。
https://www.php.net/manual/ja/language.oop5.paamayim-nekudotayim.php

parentは「継承元の親クラス」を指します。
そのため、Fooクラスにある

        parent::test_1();

の行は、継承元であるHogeクラスのtest_1()メソッドを呼び出します。

selfは「記述があるクラス」を指します。
そのため、HogeクラスのstaticCall()メソッドにある

        self::testStatic();

は、Hoge::testStatic(); と同じ意味になります。

同じくHogeクラスのstaticCall()メソッドにある

        static::testStatic();

に出てくるstaticですが、これはまず「自身がどのように呼ばれたか?」を確認
します。
今回は

Foo::staticCall();

という呼ばれ方をしているため、staticは「Fooとして」振る舞います。
そのため

        Foo::testStatic();

と等価な動きをします。

最後の static:: は、遅延静的束縛 (Late Static Bindings)といいます。
https://www.php.net/manual/ja/language.oop5.late-static-bindings.php
とても重要なものなので、しっかり理解しておきましょう。

選択肢2を見ていきましょう。
public function で定義されるメソッドは、インスタンス(オブジェクト)から呼び出されます。
そのときに、自分自身を $this でアクセスすることができます。

一方で、staticメソッドはインスタンス(メソッド)からではなく、クラス名から直接呼び出されるため、インスタンスとしての「自身」を持っていません。
そのため $this は存在しません。

エラーメッセージはPHPのバージョンによって少し異なりますが、PHP8.0の場合は選択肢の通りとなります。

選択肢3を見ていきましょう。
static プロパティは、クラス名::$プロパティ名 でアクセスをします。
そのため、通常のメソッド内でも、クラス名::$プロパティ名 や self::$プロパティ名 であればアクセス可能です。
しかし、アロー演算子でのアクセスはできません。

選択肢4を見ていきましょう。
継承において、親クラスのメソッドをオーバーライドして子クラスのメソッドを書く場合、引数の型は「同じ、またはより抽象的な広い型」である必要があります。
これを反変性といいます。
https://www.php.net/manual/ja/language.oop5.basic.php#language.oop.lsp
https://www.php.net/manual/ja/language.oop5.variance.php

ただし、コンストラクタはこの反変性が適用されません。
https://www.php.net/manual/ja/language.oop5.decon.php

ここだけを見ると設問のコードはあっているように思われます。
しかし、abstract がついているため、この場合は「シグネチャの互換性に関するルール」に従う必要があります。
そして、例題のコードは「array型から\arrayObject型に変更になっている」ため、「同じ、またはより抽象的な広い型」というルールに反しています。

そのため、このコードを実行しようとすると

Fatal error: Declaration of Foo::__construct(string $s, arrayObject $a) must
 be compatible with Hoge::__construct(string $s, array $a) in ...

というエラーになります。

なお、Fooのコンストラクタを

    public function __construct(string $s, \arrayObject|array $a) {

とすれば、エラーは生じません。

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

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をコピーしました!
目次