未経験からweb系プログラマーになるための独学履歴~マジックメソッド編~

##このコーナーについて

 私は根っからの文系なのでプログラミングや情報技術についてはITパスポート程度の知識しかなく、現在は全くの独学でコードや言語等のスキルの勉強はチュートリアル等でコードを書いて成果物を作りながら学んでいけるが、それらの過程で出てくる用語や技術についてはイマイチ学びきれない。
なので、折に触れて書籍及びQlita記事等のネットサーフィンで学んだ座学的知識をアウトプットすることがこのコーナーの目的である。

## 今回

パーフェクトPHPによるマジックメソッド

## マジックメソッドとは

マジックメソッドとは実装すると、特定の条件を満たした場合自動的に呼び出されるメソッドであり、 `__メソッド名`の形で表現される。
以前取り上げた`construct`と`destruct`もこれの仲間である。
ドキュメントを見ると色々と種類があるが、別項で取り上げるオートロードで使うものを除くと以下のものを今回は読み解いていく。


## __sleep・__wakeupメソッド

```php:serialize関数とsleep・wakeupメソッド
<?php 


class TestSerialize {

    private $score = 0;
    private $subject = "";

    public function __construct($score,$subject) {
        $this->setScore($score);
        $this->setSubject($subject);
    }

    public function setScore($score) {
        $this->score = (int)filter_var($score);

    }
    
    public function setSubject($subject) {
        $this->subject = (string)filter_var($subject);
    }


    public function getScore() {
        return $this->score;
    }
    
    public function getSubject() {
        return $this->subject;
    }

    public function __sleep() {
        echo "シリアライズしました。"."\n";
        return array("score","subject");
    }

    public function __wakeup() {
        echo "アンシリアライズしました。"."\n";
    }
}

$test = new TestSerialize(90,"数学");

echo $test->getScore()."\n";
echo $test->getSubject()."\n";

$serialized = serialize($test);
$unserialized = unserialize($serialized);

 ?>
```
 `serialize`はクラス内部の変数を一つの文字列に変換して保存しておく関数であり、`unserialize`はその逆でシリアライズ化した情報を再度生成するための関数。
 これらの関数を使うために実装しておくべきマジックメソッドが`sleep`及び`wakeup`メソッドであり、それぞれ`serialize・unserialize`関数をキーとして起動する。
 前者は保存、後者は再構築の役割を持つ。


## toStringメソッド

```php:toStringメソッドの場合

<?php 

class TestClass {

	public $foo;

	public function __construct($foo) {
		$this->foo = $foo;
	}

	public function __toString() {
		return $this->foo;
	}
}

$class = new TestClass("Hello");
echo $class;


 ?>

```

`toStirng`メソッドはクラスを文字列として扱った場合をキーとして起動するマジックメソッドである。
この場合、インスタンス（オブジェクト）である`$class`が`echo $class;`という形で文字列として扱われたため、この場合`$class`を何と`echo`するかということを`toString`が決めているのである。

コードを見ると、`return $this->foo;`となっている、そしてコンストラクタにより引数にインスタンス(オブジェクト)の引数に`$foo`を指定して、その上で`$this->foo = $foo;`と定義されているので

```php:出力結果
Hello
```
となる。

## invokeメソッド

```php:invokeメソッドの場合

<?php 

class CallableClass {

	public function __invoke($x) {
		var_dump($x);
	}

}

$test = new CallableClass();
$test(5);
var_dump(is_callable(($test)));


 ?>


```

`invoke`はクラスを関数として扱った場合をキーにして起動するマジックメソッドである。
`invoke`メソッドに引数が与えられているので、それに従い`$test`に5という引数を与えると`invoke`メソッドで定義した処理が実行されるという仕組み。
`is_callable()`は引数に指定した内容が関数としてコール可能かどうかを検証する関数である。すなわち、一連の実行結果は以下の通りになる。


```php:出力結果
int(5)
bool(true)

```

##cloneメソッド
```php:cloneメソッドの場合

<?php

class Test {
    
    public $bar;
    public function __clone() {
        $this->bar = clone $this->bar;　// $thisー＞barのコピーを作成。オリジナルは$testが、コピーは$test1が参照する。
    }

}

class Bar {

   public $val;
}


$test = new Test();
$test->bar = new Bar();
$test->bar->val = 'bar';

$test1 = clone $test;
echo '=baz前' . "\n";
echo "original：" . $test->bar->val . "\n";
echo "cloned：" . $test1->bar->val . "\n";

$test1->bar->val = 'baz';

echo '=baz後' . "\n";
echo "original：" . $test->bar->val . "\n";
echo "cloned：" . $test1->bar->val . "\n";
?>
```

`clone`メソッドはクラスが`clone`されることをキーにして起動するマジックメソッドである。
クラスのクローンに関してはクラスの基本の部分で学習した通り、同じクラスから複数のインスタンス（オブジェクト）を作る際に例えば`$test=$test1`としてしまい、そこから各インスタンス（オブジェクト)を作成すると、それぞれのオブジェクトに因果関係が生まれ、1つのオブジェクトで変更された内容はすべてのオブジェクトで変更されてしまう。

なのでそれを嫌う場合は、インスタンスの単なるコピーを作ることでそれを回避しなければならない。
その際に利用されるのが`clone`であり、その処理を具体的に定義するのがこのメソッドであると考えていい。

今回の場合は、`$test`と`$test1`で`$val`の値を異なるものにしたいということになるので
`Test`クラスの方に`clone`メソッドを用意しておく。
あとは`$test`でインスタンスを作り、さらにその中の`bar`にアクセスしてさらに`Bar`クラスのインスタンスを作り、`val`にアクセスして`bar`という値を代入する。

この時点で`clone`すると、`$test・$test1`ともに`val=bar`である。
ここで、`$test1->bar->val = 'baz';`と`$test1`だけ別の値に変えてやる。
そうなると、`$test1はval=baz`となるが、`$tes`tは一見同じ`$this->ba`rを参照しているように見えても、`clone`メソッドによりそれはオリジナルを参照しているのか、そのコピーを参照しているのかという違いが生まれているので、`$test`は変わらず`val=bar`のままであるということになる。