目次
マジック関数は特定条件により自動的に起動する関数。アンダースコア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になるので消滅する。