Wabow Information Inc. Blog
分類: 技術分享 作者: jaceju
29 六月 2009PHP5 在物件導向開發上加了不少新特色,而為了讓語法本身更靠近 Java 卻又不失動態語言的特性,因此 PHP5 引進了許多魔術方法,讓我們在開發類別上有更多的彈性。
以下是常見的幾個 PHP 5.2 版中所提供的魔術方法。
在使用 new 這個敘述來建立物件時,就會呼叫到類別的 __construct 方法。
<?php class BaseClass { function __construct() { print "In BaseClass constructor\n"; } } class SubClass extends BaseClass { function __construct() { parent::__construct(); print "In SubClass constructor\n"; } } $obj = new BaseClass(); $obj = new SubClass();
__construct 常用在建立物件時,需要初始部份屬性的狀況;通常我們不會直接在這裡執行物件要進行的動作。
在頁面結束,或是將物件變數設為 null 時,就會呼叫到類別的 __destruct 方法。
<?php class MyDestructableClass { function __construct() { print "In constructor\n"; $this->name = "MyDestructableClass"; } function __destruct() { print "Destroying " . $this->name . "\n"; } } $obj = new MyDestructableClass(); $obj = null; echo 'END';
__destruct 大多是用在物件被銷毀時,一定要執行的動作,例如關閉資料庫連線等。
這四個魔術方法主要是用在當我們直接存取物件某些屬性,但在物件內部找不到這些對應的公開屬性時,就會觸發這些方法。
當要設定某物件的屬性值時,就會觸發 __set 方法;反之,當要取得某物件的屬性值時,就會觸發 __get 方法。
而當利用 isset 或 empty 函式來判斷某物件屬性是否存在時,就會觸發 __isset 方法;反之,當利用 unset 函式來判斷某物件屬性是否存在時,就會觸發 __unset 方法。
<?php class MemberTest { /** Location for overloaded data. */ private $data = array(); /** Overloading not used on declared members. */ public $declared = 1; /** Overloading only used on this when accessed outside the class. */ private $hidden = 2; public function __set($name, $value) { echo "Setting '$name' to '$value'\n"; $this->data[$name] = $value; } public function __get($name) { echo "Getting '$name'\n"; if (array_key_exists($name, $this->data)) { return $this->data[$name]; } $trace = debug_backtrace(); trigger_error( 'Undefined property via __get(): ' . $name . ' in ' . $trace[0]['file'] . ' on line ' . $trace[0]['line'], E_USER_NOTICE); return null; } /** As of PHP 5.1.0 */ public function __isset($name) { echo "Is '$name' set?\n"; return isset($this->data[$name]); } /** As of PHP 5.1.0 */ public function __unset($name) { echo "Unsetting '$name'\n"; unset($this->data[$name]); } /** Not a magic method, just here for example. */ public function getHidden() { return $this->hidden; } } echo "<pre>\n"; $obj = new MemberTest; $obj->a = 1; echo $obj->a . "\n\n"; var_dump(isset($obj->a)); unset($obj->a); var_dump(isset($obj->a)); echo "\n"; echo $obj->declared . "\n\n"; echo "Let's experiment with the private property named 'hidden':\n"; echo "Privates are visible inside the class, so __get() not used...\n"; echo $obj->getHidden() . "\n"; echo "Privates not visible outside of class, so __get() is used...\n"; echo $obj->hidden . "\n";
__set 、 __get 、 __isset 及 __unset 常用在資料列模型上 (也就是用物件來表示資料列) ,我們會將其欄位和對應的值放在模型的件內部屬性裡;這時我們再用這四個魔術方法來避免使用者直接的不當存取,以保護整個模型物件。
當要呼叫某物件的方法時,若在物件內部找不到對應的公開方法時,就會觸發 __call 方法。
<?php class MethodTest { public function __call($name, $arguments) { // Note: value of $name is case sensitive. echo "Calling object method '$name' " . implode(', ', $arguments). "\n"; } } $obj = new MethodTest; $obj->runTest('in object context');
我們通常會利用 __call 來處理一些名稱可能會隨著用途而改變的方法,像是 Zend Framework 裡 Zend_Db_Table 的 findXxxxByYyyyy() 等。
當利用 serialize() 處理物件時,會檢查該物件所屬類別有沒有 __sleep 方法;我們可以在 __sleep 方法中以陣列格式回傳要讓 serialize() 保存的資料。
反之,當 unserialize() 還原物件時,會檢查該物件所屬類別有沒有 __wakeup 方法;我們可以在 __wakeup 方法中重新建構原物件 (例如資料庫重新連線) 。
<?php class Connection { protected $link; private $server, $username, $password, $db; public function __construct($server, $username, $password, $db) { $this->server = $server; $this->username = $username; $this->password = $password; $this->db = $db; $this->connect(); } private function connect() { $this->link = mysql_connect($this->server, $this->username, $this->password); mysql_select_db($this->db, $this->link); } public function __sleep() { return array('server', 'username', 'password', 'db'); } public function __wakeup() { $this->connect(); } }
在使用 clone 這個敘述來建立物件時,就會觸發該物件所屬類別的 __clone 方法。因為 PHP 預設的 clone 屬於淺層複製,我們可以利用 __clone 方法進行深層複製。
<?php class SubObject { static $instances = 0; public $instance; public function __construct() { $this->instance = ++self::$instances; } public function __clone() { $this->instance = ++self::$instances; } } class MyCloneable { public $object1; public $object2; function __clone() { // Force a copy of this->object, otherwise // it will point to same object. $this->object1 = clone $this->object1; } } $obj = new MyCloneable(); $obj->object1 = new SubObject(); $obj->object2 = new SubObject(); $obj2 = clone $obj; print("Original Object:\n"); print_r($obj); print("Cloned Object:\n"); print_r($obj2);
將物件轉換為字串 (例如 echo ) 時,會觸發 __toString 方法。
<?php // Declare a simple class class TestClass { public $foo; public function __construct($foo) { $this->foo = $foo; } public function __toString() { return $this->foo; } } $class = new TestClass('Hello'); echo $class;
這個部落格分享了哇寶在電子商務領域的技術及資訊,希望能讓更多人一起為台灣的網路產業加油。