@blog.justoneplanet.info

日々勉強

PHPのマジックメソッド

ヒョッコリまとめておくことにした

■__constructと__destruct

まぁ、説明するまでもないヤツ( ̄△ ̄;)エッ・・?

<?php
class Dog
{
    private $_name;
    public function __construct($name)
    {
        $this->_name = $name;
    }
    public function __destruct()
    {
        echo "{$this->_name}, Bye!";
    }
}
$pochi = new Dog('pochi');
unset($pochi);// pochi, Bye!
print('end...');

まぁ、こんな感じ。実用性のあるコードを書くとしたら以下のようなコードが良いかもしれない。

class Db
{
    private $dbh;
    public function __construct()
    {
        $this->_dbh = $this->_connect();
    }
    public function __destruct()
    {
        unset($this->_dbh);
    }
}

これにより接続をどこかでunsetすればリソースが早く開放できる。

■__setと__get

これらは明示されていないプロパティへの操作がトリガとなる。

class Dog
{
    private $_name;
    public function __construct($name)
    {
        $this->_name = $name;
    }
    public function __set($name, $value)
    {
        throw new Exception("Don't tell me that {$name} is {$value}!");
    }
    public function __get($name)
    {
        throw new Exception("Don't ask me about {$name}!");
    }
}
$pochi = new Dog('pochi');
try{
    $pochi->age = 25;
}
catch(Exception $e){
    print($e->getMessage());// Don't tell me that age is 25!
}
try{
    print($pochi->age);
}
catch(Exception $e){
    print($e->getMessage());// Don't ask me about gender!
}

まぁ、こんな感じだね。

■__issetと__unset

プロパティに対してのisset()やunset()がトリガとなる。

class Dog
{
    private $_name;
    public function __construct($name)
    {
        $this->_name = $name;
    }
    public function __isset($name)
    {
        throw new Exception('__isset');
    }
    public function __unset($name)
    {
        throw new Exception('__unset');
    }
}

$pochi = new Dog('pochi');
try{
    if(isset($pochi->age)){
        // something
    }
}
catch(Exception $e){
    print($e->getMessage());// __isset
}
try{
    unset($pochi->age);
}
catch(Exception $e){
    print($e->getMessage());// __unset
}

■__callと__callStatic

定義されていないインスタンスメソッドやクラスメソッドの呼び出しがトリガとなる。通常、定義されていないメソッドをコールするとエラーが発生する一方で、この2つのメソッドを定義するとエラーが発生しなくなる。従って何らかの処理を挟んだ後、該当しない場合は例外を発生させたほうが良い。

class Dog
{
    private $_name;
    public function __construct($name)
    {
        $this->_name = $name;
    }
    public function __call($method, $args)
    {
        throw new Exception('__call');
    }
    public static function __callStatic($method, $args)
    {
        throw new Exception('__callStatic');
    }
}

$pochi = new Dog('pochi');
try{
    $pochi->cry('bow!');
}
catch(Exception $e){
    print($e->getMessage());// __call
}
try{
    Dog::getData();
}
catch(Exception $e){
    print($e->getMessage());// __callStatic
}

■__sleepと__wakeup

オブジェクトがserializeやunserializeがトリガとなる。通常のクラスでは有用性があまり分からないが、リソースがプロパティに含まれている場合、serializeはリソース型をシリアライズできない。従って、__sleepと__wakeupを実装すると良いかもしれない。

class Db
{
    private $_host   = 'localhost';
    private $_dbname = 'db';
    private $_dbuser = 'user';
    private $_dbpass = 'pass';
    private $_dbh;
    public function __construct()
    {
        $this->_connect();
    }
    public function __sleep()
    {
        return array('_host', '_dbname', '_dbuser', '_dbpass');
    }
    public function __wakeup()
    {
        $this->_connect();
    }
    public function getConn()
    {
        return $this->_dbh;
    }
    private function _connect()
    {
        try {
            $this->_dbh = new PDO("mysql:host={$this->_host};dbname={$this->_dbname}", $this->_dbuser, $this->_dbpass);
            $this->_dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
            $this->_dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
        }
        catch(PDOException $e){
            print($e->getMessage());
        }
    }
}
$db = new Db();
var_dump($db->getConn());// object(PDO)#2 (0) { }
$val = serialize($db);
$db = unserialize($val);
var_dump($db->getConn());// object(PDO)#4 (0) { }

ちなみに__sleepと__wakeupを記述せずに実行すると以下のようなエラーがFatalが発生する。

class Db
{
    private $_host   = 'localhost';
    private $_dbname = 'db';
    private $_dbuser = 'user';
    private $_dbpass = 'pass';
    private $_dbh;
    public function __construct()
    {
        $this->_connect();
    }
    public function getConn()
    {
        return $this->_dbh;
    }
    private function _connect()
    {
        try {
            $this->_dbh = new PDO("mysql:host={$this->_host};dbname={$this->_dbname}", $this->_dbuser, $this->_dbpass);
            $this->_dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
            $this->_dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
        }
        catch(PDOException $e){
            print($e->getMessage());
        }
    }
}
$db = new Db();
var_dump($db->getConn());// object(PDO)#2 (0) { } 
$val = serialize($db);// Fatal error: Uncaught exception 'PDOException' with message 'You cannot serialize or unserialize PDO instances'
$db = unserialize($val);
var_dump($db->getConn());

■__invoke

こいつを使うとコールバック関数とかが気持ちよく書ける。

class Group
{
    public function __invoke($params)
    {
        return $params . ', bow!';
    }
}
$group = new Group();
$result = array_map($group, array('Pochi', 'Shiro'));
var_dump($result);
/*
array(2) {
  [0]=>
  string(11) "Pochi, bow!"
  [1]=>
  string(11) "Shiro, bow!"
}
*/

今までPHPのコールバック関数が気持ち悪くて書きたくなかったが改心しました。

コメントはまだありません»

No comments yet.

RSS feed for comments on this post.TrackBack URL

Leave a comment