[PHP] マジックメソッド - 未定義属性の処理

マジックメソッドとは、クラスに定義されるメソッドで、あらかじめ役割が決められたいくつかのメソッドのことを言います。

以下の記事で取り上げたコンストラクタとデストラクタについても、マジックメソッドの一種です。

https://www.s-watanabe.work/2022/12/php_11.html

ここでは、未定義の属性への操作しようとしたときに呼び出されるメソッドについてまとめていきます。

__setメソッドと__getメソッド

PHPは基本的に未定義の属性へのアクセスが可能になっています。例えば以下のコードはエラーにならずに実行可能です。

class Test {
    
}

$test = new Test();
$test->name = 'Hoge';

echo "name=", $test->name, "\n\n";

var_dump($test);

実行結果は以下のようになります。

$ php test_magic-method.php
name=Hoge

object(Test)#1 (1) {
    ["name"]=>
    string(4) "Hoge"
}

この属性をセットする時の処理を制御するのが __setメソッド、属性を取得する処理を制御するのが __getメソッドです。

マジックメソッドのシグネチャ

__setメソッド

1. __setメソッドのシグネチャ
アクセス修飾子 public or なし
メソッド名 __set
第一引数 string (属性名)
第二引数 mixed (値)
戻り値 void

__getメソッド

2. __getメソッドのシグネチャ
アクセス修飾子 public or なし
メソッド名 __get
第一引数 string (属性名)
戻り値 mixed

[実装例]__setメソッドと__getメソッド

コード

以下のコードは、__setメソッドで任意の属性を保存、__getメソッドで取り出す例です。__getメソッドで未定義だった場合は、例外を発生させています。

class Test {
    private array $values = [];
    
    public function __set($key, $value) {
        $this->values[$key] = $value;
    }

    public function __get($key) {
        return $this->values[$key] ??
            throw new Exception("Undefined property[{$key}]");
    }
}

$test = new Test();
$test->name = "Hoge";

echo "name=", $test->name, "\n\n";

var_dump($test);

実行結果

$ php test_magic-method.php
name=Hoge

object(Test)#1 (1) {
    ["values":"Test":private]=>
    array(1) {
    ["name"]=>
    string(4) "Hoge"
    }
}

__issetメソッドと__unsetメソッド

__issetメソッドと__unsetメソッドは、それぞれ未定義の属性にisset関数、unset関数を実行したときに呼び出されるメソッドです。

マジックメソッドのシグネチャ

__issetメソッド

3. __issetメソッドのシグネチャ
アクセス修飾子 public or なし
メソッド名 __isset
第一引数 string (属性名)
戻り値 bool

__unsetメソッド

4. __unsetメソッドのシグネチャ
アクセス修飾子 public or なし
メソッド名 __unset
第一引数 string (属性名)
戻り値 void

[実装例]__issetメソッドと__unsetメソッド

コード

以下のコードは、__setメソッドと__getメソッドの例で使用したコードに、__issetメソッドと__unsetメソッドを加えました。このように__isset、__unsetは基本的に__set、__getメソッドとセットで使います。

class Test {
    private array $values = [];
    
    public function __set($key, $value) : void {
        $this->values[$key] = $value;
    }

    public function __get($key) {
        return $this->values[$key] ??
            throw new Exception("Undefined property[{$key}]");
    }

    public function __isset(string $key) : bool {
        echo __METHOD__, "\n";
        return isset($this->values[$key]);
    }

    public function __unset(string $key) : void {
        echo __METHOD__, "\n";
        unset($this->values[$key]);
    }
}

$test = new Test();
$test->var = 1;

$a = isset($test->var);
echo "isset=", $a, "\n\n";

unset($test->var);
echo "unset\n\n";

$b = isset($test->var);
echo "isset=", $b, "\n\n";

var_dump($test);

実行結果

実行結果を確認すると、isset関数とunset関数に対応したメソッドがそれぞれのタイミングで呼び出されていることが分かります。

$ php test_magic-method.php
Test::__isset
isset=1

Test::__unset
unset

Test::__isset
isset=

object(Test)#1 (1) {
  ["values":"Test":private]=>
  array(0) {
  }
}

__callメソッドと__callStaticメソッド

__callメソッドと__callStaticメソッドは未定義のメソッドを実行したときに呼び出されるマジックメソッドです。属性の操作とは直接関係ありませんが、__getメソッド・__setメソッドとよく似た動作をするのでこちらでまとめておきます。

マジックメソッドのシグネチャ

__callメソッド

5. __callメソッドのシグネチャ
アクセス修飾子 public or なし
メソッド名 __call
第一引数 string (メソッド名)
第二引数 array
戻り値 mixed

__callStaticメソッド

6. __callStaticメソッドのシグネチャ
アクセス修飾子 public static or static
メソッド名 __callStatic
第一引数 string (メソッド名)
第二引数 array
戻り値 mixed

[実装例]__callメソッド呼び出し確認

コード

class Test {
    public function __call(string $function, array $args) {
        echo __METHOD__, "\n";
        var_dump($function, $args);
    }
}

$test = new Test();
$test->addVar(1);

実行結果

$ php test_magic-method.php
Test::__call
string(6) "addVar"
array(1) {
  [0]=>
  int(1)
}

[実装例]Getter、Setterを__callメソッドで実装する

__getメソッドと__setメソッドの実装例のコードは、__callメソッドを使って次のように置き換えることが出来ます。

コード

class Test {
    private array $values = [];
    
    public function __call(string $function, array $args) {
        if (empty($args)) {
            if (!array_key_exists($function, $this->values))
                throw new Exception("Undefined property[{$function}]");
            return $this->values[$function];
        } else {
            $this->values[$function] = $args[0];
        }
    }
}

$test = new Test();
$test->name("Hoge");

echo "name=", $test->name(), "\n\n";

var_dump($test);

実行結果

$ php test_magic-method.php
name=Hoge

object(Test)#1 (1) {
  ["values":"Test":private]=>
  array(1) {
    ["name"]=>
    string(4) "Hoge"
  }
}

コメント

このブログの人気の投稿

docker-compose up で proxyconnect tcp: dial tcp: lookup proxy.example.com: no such host

docker-compose で起動したweb、MySQLに接続できない事象

【PHP】PHP_CodeSnifferを使う(コーディングルールのカスタマイズ)