• PHP
【php8上級/準上級試験】模擬問題解説 問題4. __construct() __destruct __call() __callStatic()

「第1回 PHP 8 上級 模擬試験」を解説していきます

本記事ではひたすら下記記事の模擬試験の解説をしていきたいと思います!

第1回 PHP 8 上級 模擬試験
https://study.prime-strategy.co.jp/study/ph8ex1/

解説記事一覧

模擬問題 4

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

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

declare(strict_types=1);とは指定された型に厳密に従わないとエラーとなります

error_reporting(-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

今回の問題でのポイントは子クラスでのコンストラクタの継承についてになります。

そもそも「コンストラクタ」とは

コンストラクタは「最初の設定をする特別な関数」

  • 新しいものを作るときの「最初の設定」
  • 必ず必要な情報を設定できる
  • クラスから実体(インスタンス)を作る時に自動的に動く
クラス User 定義 コンストラクタ定義 必須パラメータ: name, age オブジェクト生成 new User(“John”, 25) 定義された必須パラメータを渡す コンストラクタ実行 自動的に__constructが実行 渡されたパラメータで値を設定 class User { function __construct($name, $age) { // ここで必須パラメータを定義 }

子クラスで新しいコンストラクタを定義すると、親のコンストラクタは自動で呼ばれない

PHPでは、子クラスに新しいコンストラクタを定義すると、親クラスのコンストラクタは自動的に呼ばれず、必要に応じて parent::__construct() を明示的に呼び出す必要があります。

一方、子クラスでコンストラクタを定義しない場合は、親クラスのコンストラクタがそのまま継承され、自動的に呼び出されます。

親クラス User function __construct($name) { $this->name = $name; } 子クラス BasicUser // コンストラクタなし 自動呼び出し ✓ 子クラス AdminUser function __construct($name, $role){} 自動呼び出しなし ✗ $basic = new BasicUser(“John”); // 親のコンストラクタが自動実行 $admin = new AdminUser(“John”, “admin”); // 要parent::__construct()

今回の問題文は親のコンストラクタの呼び出しもしてあるので、親と子両方のコンストラクタが実行されます。よって選択肢は⭕です

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

選択肢2でのポイントはデストラクタですね

デストラクタとは?

デストラクタは、オブジェクトが破棄(削除)される時に自動的に呼び出されるメソッドです。 PHPでは __destruct() という名前で定義します。

クラス継承において親クラスのデストラクタは自動的に呼び出されます。

<?php
class ParentClass {
    public function __destruct() {
        echo "親のデストラクタ実行\n";
    }
}

class ChildClass extends ParentClass {
}

$obj = new ChildClass();

// 実行結果
親のデストラクタ実行

ただし

子クラスでデストラクタの定義がある場合は親のデストラクタは呼び出されません

(※ 親クラスのデストラクタの処理も必要であれば、子クラスの __destruct() の中で parent::__destruct() を明示的に呼ぶ必要があります)

PHPにおける子クラス/親クラスの__destruct() 呼び出し ① 子クラスのみ __destruct() 定義 ChildClass::__destruct() ( 親の__destruct()は自動で呼ばれない ) ② 子クラスで parent::__destruct() を呼ぶ ChildClass::__destruct() ParentClass::__destruct()
<?php
class ParentClass {
    public function __destruct() {
        echo "親のデストラクタ実行\n";
    }
}

class ChildClass extends ParentClass {
    public function __destruct() {
        echo "子のデストラクタ実行\n";
    }
}

$obj = new ChildClass();

// 実行結果
子のデストラクタ実行

上記より親クラスのデストラクタが呼び出されず、子クラスのデストラクタのみ実行されます!

選択肢は❌です

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

__callの動作

__call()未定義のインスタンスメソッドが呼ばれた時に実行されます。

第1引数の$methodには呼び出されたメソッド名が入る 第2引数の$argsには引数が配列として渡されます。

$obj->test(“abc”, 123); $method: “test” $args: [abc, 123] 未定義メソッド呼び出し → __call() が実行される 引数は配列として渡される

よって選択肢は⭕です。

__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 …

__callStaticの呼び出し

流れとしては下記です

  1. 静的メソッドを呼び出します
  2. PHPはそのメソッドが実際にクラスに存在するかチェックします
  3. メソッドが存在しない場合、自動的に __callStatic が呼び出されます
    注意)
MyClass::nonExistentMethod() メソッドは存在する? No __callStatic($name, $arguments) ① 静的メソッド呼び出し ② メソッド存在チェック ③ __callStaticが実行

__callStaticインスタンス呼び出しではエラー

静的メソッドでない場合は「__callStatic」呼び出しはできません、よって選択肢は⭕です!

インスタンス(静的メソッドでない場合)は__call()を使用します!

メソッド呼び出しの違いと__callStaticの動作 静的呼び出し // 静的呼び出し MyClass::undefinedMethod(); MyClass::staticTest(); __callStatic が呼ばれる (定義されている場合) 正常に処理可能 非静的(インスタンス)呼び出し // インスタンス呼び出し $obj = new MyClass(); $obj->undefinedMethod(); __callStaticは動作しない! 代わりに__call()が必要 (ない場合はエラー) インスタンスメソッド($obj->method())の呼び出しには__call()を使用