プログラミング

プログラミング初心者のPHP修羅の道#7〜PHP8上級問題を徹底追求する〜

この記事のキーワード

マジックメソッド・__toString()・__invoke()・__get()・__debugInfo()・オーバーロード

PHP8上級資格取得へ向けたアウトプット記事になります。

PHP試験運営団体より公開されている模擬試験を1つ1つみていく形で事細かくみていきますが、勉強途中ゆえ間違っている部分に関してはご了承ください。

PHP上級資格のメイン教材はPHPマニュアルと指定されていますので、このマニュアルを主に踏襲した内容となっています。

主な勉強教材は以下になります。

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におけるオーバーロード

・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"
}
*/

 

よって、この選択肢は合っています。

ABOUT ME
ヒロ
社会人4年目/25歳/食品商社で2年間営業した後、IT業界にシステムエンジニアとして転職/Java,PHP言語を扱う開発エンジニア