マジックメソッド(特殊メソッド) /   sleep()   wakeup()   toString()   invoke()   set_state()   clone()

マジック関数は特定条件により自動的に起動する関数。アンダースコアx2 __ から始まる関数。__construct(), __get(), __call(), __autoload() などもマジック関数。

で、 __sleep() , __wakeup() , __toString() , __invoke() , __set_state() , __clone() のおべんきょ。LINK:PHP: マジックメソッド - Manual

概要

array __sleep()

void __wakeup()

__sleep() は selialize() がコールされると起動。シリアル化したいプロパティ名を配列にして返すように定義する。シリアル化したい部分だけを抽出するなどの処理を行う。

__wakeup() は unserialize() がコールされると起動。マニュアルによると意図される __wakeup の使用法は、 シリアル化の際に失われたデータベース接続を再度確立したり、 その他の再初期化を行うことです。ってさ。

string __toString()

オブジェクトを文字列として出力しようとしたとき(? 要するに echo $object みたいな感じ)に起動する。引数は無し。返り値は必ず文字列型でなければならず異なる場合はE_RECOVERABLE_ERROR レベルの致命的なエラーが発生。

__invoke()

(PHP5.3.0~) これは他のマジックメソッドと違ってかなり自由に作れる。オブジェクトを関数としてコールしたときに実行される。

mixed __set_state($array)

var_export($obj) でオブジェクトのプロパティをエクスポートできる。 で、そのエクスポートデータをオブジェクトに戻すときに __set_state($array) がコールされる。 いったんエクスポートされなければコールされる機会は無い。。 この特殊メソッドは必ずスタティックメソッドとなる。

__sleep() => __wakeup() に似てるな…

void __clone()

clone() によりオブジェクトのディープコピーを作成したときに*複製されたオブジェクト側でコールされる*。引数も返り値も無い。

__sleep(), __wakeup()

<?php
class SleepWakeup
{
  private $name, $age, $life;
  function __construct($name,$age){
    $this->name = $name;
    $this->age = $age;
    $this->life = TRUE;
  }
  function __sleep(){
    return array("name", "age");
  }
  function __wakeup(){
    $this->age += "100";
    $this->life = "END";
  }
}
$obj = new SleepWakeup("太郎","18");
echo str_replace(
  array(";}",   "{",     ";"),
  array(";}\n", "{\n\t", ";\n\t"),
  serialize($obj)
);
print_r(unserialize(serialize($obj)));
?>

結果

O:11:"SleepWakeup":2:{
  s:17:"SleepWakeupname";
  s:6:"太郎";
  s:16:"SleepWakeupage";
  s:2:"18";
  }
SleepWakeup Object
(
    [name:SleepWakeup:private] => 太郎
    [age:SleepWakeup:private] => 118
    [life:SleepWakeup:private] => END
)

__toString()

<?php
class MagicMethod__toString{
  public $pub = "ぱぶりっく";
  protected $pro = "ぷろてくてど";
  public function __toString(){
    $string = serialize($this);
    $string = str_replace("{", "{\n\t", $string);
    $string = str_replace(";", ";\n\t", $string);
    return $string;
  }
}
$obj = new MagicMethod__toString();
echo $obj;
?>

結果

O:21:"MagicMethod__toString":2:{
  s:3:"pub";
  s:15:"ぱぶりっく";
  s:6:"*pro";
  s:18:"ぷろてくてど";
  }

__invoke()

<?php
class Invoke1
{
  function __invoke(){
    echo "Invoke1.\n";
  }
}
class Invoke2
{
  function __invoke($a,$b){
    echo $a, $b;
  }
}
$obj = new Invoke1;
$obj();
$obj = new Invoke2;
$obj("お前はもう", "死んでいた");
?>

結果

Invoke1.
お前はもう死んでいた

__set_state()

<?php
class MM__set_state
{
  private $pri = "プライベート";
  public $var1, $var2;
  function __set_state($array)
  {
    $obj = new MM__set_state;
    foreach($array as $k => $v){
      $obj->{$k} = $v;
    }
    return $obj;
  }
}
$obj = new MM__set_state;
$obj->var1 = "パブリック";

echo "***エクスポートの中身***\n";
$export = var_export($obj, TRUE);
var_dump($export);

echo "***インポートしたデータ***\n";
eval("\$import = $export;");
var_dump($import);
?>

結果

***エクスポートの中身***
string(121) "MM__set_state::__set_state(array(
   'pri' => 'プライベート',
   'var1' => 'パブリック',
   'var2' => NULL,
))"
***インポートしたデータ***
object(MM__set_state)#2 (3) {
  ["pri":"MM__set_state":private]=>
  string(18) "プライベート"
  ["var1"]=>
  string(15) "パブリック"
  ["var2"]=>
  NULL
}

インポートしたデータというか、コールされたMM__set_state::__set_state() の返り値だね。。

__sleep() __wakeup() と似てるけどこっちは返す値は自由

__clone()

<?php
class MagicMethod__clone
{
  private $var;
  public function __clone(){
    echo "__clone() is called.\n";
    $this->var = 2;
  }
  public function __construct($flag){
    echo "__construct() is called ({$flag})\n";
    $this->var = 1;
  }
}
$obj = new MagicMethod__clone(1);
$clone = clone $obj;
var_dump($clone);
?>

結果

__construct() is called (1)
__clone() is called.
object(MagicMethod__clone)#2 (1) {
  ["var":"MagicMethod__clone":private]=>
  int(2)
}

クローンされた側のオブジェクトではコンストラクタは起動せずに、__clone() が起動する。

<?php
class MM__clone
{
  private $attr;
  public $nest;
  public function __construct(){
    $this->attr = "Original.\n";
  }
  public function __clone(){
    $this->attr = "Clone.\n";
  }
}
class Nest extends MM__clone{}

$obj = new MM__clone;
$obj->nest = new Nest;
$cloned = clone $obj;
print_r($obj);
print_r($cloned);
?>

結果

MM__clone Object
(
    [attr:MM__clone:private] => Original.

    [nest] => Nest Object
        (
            [attr:MM__clone:private] => Original.

            [nest] =>
        )

)
MM__clone Object
(
    [attr:MM__clone:private] => Clone.

    [nest] => Nest Object
        (
            [attr:MM__clone:private] => Original.

            [nest] =>
        )

)

オブジェクトの中に入れた別のオブジェクトは当然だけどクローンされない。

<?php
class MM__clone
{
  private $attr;
  public $nest;
  public function __construct(){
    $this->attr = "Original.\n";
  }
  public function __clone(){
    $this->clone_nest();
    $this->attr = "Clone.\n";
  }
  public function clone_nest(){
    $this->nest = clone $this->nest;
  }
}
class Nest extends MM__clone{
  public function clone_nest(){}
}

$obj = new MM__clone;
$obj->nest = new Nest;
$cloned = clone $obj;
print_r($obj);
print_r($cloned);
?>

結果

MM__clone Object
(
    [attr:MM__clone:private] => Original.

    [nest] => Nest Object
        (
            [attr:MM__clone:private] => Original.

            [nest] =>
        )

)
MM__clone Object
(
    [attr:MM__clone:private] => Clone.

    [nest] => Nest Object
        (
            [attr:MM__clone:private] => Clone.

            [nest] =>
        )

)

ということでクローンする関数側の__clone()メソッドの中で、入れ子のオブジェクトをクローンしてやれば、入れ子のオブジェクトもクローンになる。

$this->nest = clone $this->nest; がどんな動作しているのかちょっと混乱した…下のコードが理解できればOK。

<?php
$obj = new stdClass;
$obj = clone $obj;
?>

この場合は最初に作ったstdClassのインスタンスはクローンした後にref_count=0になるので消滅する。

Share
関連記事