1
0

More than 3 years have passed since last update.

PHP初心者によるマジックメソッドの理解

Last updated at Posted at 2020-03-12

マジックメソッドとは

コンストラクタ「__construct()」等のアンダースコア2つから始まるメソッドで、
基本的には、ある状況において実行されるメソッド。

主なマジックメソッド

constructとdestruct

__construct()

 インスタンスの生成時に実行される

__destruct()

 プロセスの終了時や、unset()で明示的にインスタンスを破棄した場合に実行される

getとset

__get()

 アクセスできないプロパティにアクセスした場合に実行される

__set()

 アクセスできないプロパティに対して値をセットした場合に実行される

issetとunset

__isset()

 アクセスできないプロパティに対してisset()を行った場合に実行される

__unset()

 アクセスできないプロパティに対してunset()を行った場合に実行される

isset()

 変数が宣言されていて、NULLでなければ、true
 NULLが代入されているか、変数が存在しない場合は、falseを返す。

  isset(/*$変数名*/);

unset()

 指定した変数を破棄する。
 ※あるグローバル変数を、ある関数の中でunset()した場合は、ローカル変数のみが破棄される。

  unset(/*$変数名*/);

callとcallStatic

__call()

 未定義のインスタンスメソッドを呼び出した場合に実行される

__callStatic()

 未定義のクラスメソッドを呼び出した場合に実行される
 

sleep()とwakeup()

__sleep()

 インスタンスをserializeする場合に実行される

__wakeup()

 インスタンスをunserializeする場合に実行される

serialize() unserialize()とは

 インスタンスをデータベースやファイルに保存したい場合、プロパティをそれぞれ保存して行くと、データが膨大になるため、
 シリアライズ化することで、クラス内部の情報を文字列に変換して保存することが可能となる。
 シリアライズ化した情報を再度生成したい場合は、アンシリアライズすることで再構築できる。

<?php

class Test
{
    public $firstProperty = "プロパティ1";
    public $secondProperty = "プロパティ2";

    public function __sleep()  //serialize()されたときに実行される
    {
        echo "シリアライズしました。\n";
        return array('firstProperty', 'secondProperty');
    }

    public function __wakeup()  //unserialize()されたときに実行される
    {
        echo "アンシリアライズしました。\n";
    }
}


$test = new Test();

$serialized = serialize($test);
$unserialized = unserialize($serialized);
echo $unserialized->firstProperty . "\n";
echo $unserialized->secondProperty . "\n";

// 実行結果↓
// シリアライズしました。
// アンシリアライズしました。
// プロパティ1
// プロパティ2

toString

__toString()

 インスタンスを文字列として扱いたい場合に利用できる

<?php
class Test
{
  private $property = 'プロパティ';
  public function __toString() {
    return $this->property;
  }
}

  $test = new Test();
  echo $test;

// 実行結果↓
// プロパティ

invoke

__invoke()

 __invokeを実装したクラスを関数のように扱うことが可能になる

<?php
class Test
{
  public function __invoke($params) {
    print_r($params);
    echo 'を受け取りました。';
  }
}

  $test = new Test();
  //関数として利用(__invoke()が実行される)
  $test('hoge');

// 実行結果↓
// hogeを受け取りました。

clone

__clone()

 インスタンスがcloneされた場合に実行される

clone

 インスタンスを複製することができる。

代入するだけでは複製できない
class Test
{
    public $property = "オリジナル";
}

$test = new Test();
$test1 = $test;  //インスタンスはポインタであり、代入では複製されない
$test1->property = "コピー";  //上記の理由から、$testも$test1も同じプロパティということになる

echo "original:" . $test->property;
echo "\ncopy:" . $test1->property . "\n";

// 実行結果↓
//original:コピー
//copy:コピー
複製するにはcloneを使う
<?php

class Test
{
    public $property = "オリジナル";
}

$test = new Test();
$test1 = clone $test;  //cloneでインスタンスを複製
$test1->property = "コピー";

echo "original:" . $test->property;
echo "\ncopy:" . $test1->property . "\n";

// 実行結果↓
//original:オリジナル
//copy:コピー

__clone()を使うケース

cloneした$testの内部のBarインスタンスはcloneされていないため、変更されてしまう。
<?php

class Test
{
    public $bar;
}

class Bar
{
    public $val;
}

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

$test1 = clone $test;

echo "\n before fuga";
echo "\n original:" . $test->bar->val;
echo "\n copy:" . $test1->bar->val . "\n";

$test1->bar->val = 'fuga';
echo "\n after fuga";
echo "\n original:" . $test->bar->val;
echo "\n copy:" . $test1->bar->val . "\n\n";


// 実行結果↓
// before fuga
// original:hoge
// copy:hoge

// after fuga
// original:fuga オリジナルにもfugaが代入されてしまう!
// copy:fuga

 上記のプログラムでは、
 ①$testにTestインスタンスを代入
 ②$test(Testインスタンス)の$barにBarインスタンスを代入
 ③$test(Testインスタンス)の$bar(Barインスタンス)のvalに'hoge'を代入
 ④$test1に$testをcloneして代入
 
 この時点での$testと$test1を見てみると...

$testと$test1のvar_dump()結果
#$test
object(Test)#1 (1) {
  ["bar"]=>
  object(Bar)#2 (1) {
    ["val"]=>
    string(4) "hoge"
  }
}
#$test1
object(Test)#3 (1) {  #cloneしたので、Testインスタンスは新しく生成(#3)されている。
  ["bar"]=>
  object(Bar)#2 (1) { #$Barインスタンスは$testと同じもの(#2)を指している
    ["val"]=>
    string(4) "hoge"
  }
}

 ⑤$test1の$var->valに'fuga'を代入

 $test1の$var->valは$testの$var->valと同じものを指すので、オリジナルもコピーも値が変更されてしまう。

 上記は、__clone()を使うと解決できる

Testクラスを以下のように変える
<?php

class Test
{
    public $bar;
    public function __clone()      //cloneされた時に実行されるメソッドを追加
    {
        $this->bar = clone $this->bar;
    }
}

//...省略...

// 実行結果↓
// before fuga
// original:hoge
// copy:hoge

// after fuga
// original:hoge オリジナルはそのまま!
// copy:fuga
$testと$test1のvar_dump()結果
#$test
object(Test)#1 (1) {
  ["bar"]=>
  object(Bar)#2 (1) {
    ["val"]=>
    string(4) "hoge"
  }
}
#$test1
object(Test)#3 (1) {
  ["bar"]=>
  object(Bar)#4 (1) { #新たなBarインスタンスがcloneされている!
    ["val"]=>
    string(4) "fuga"
  }
}
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0