php有一類很神奇的方法,這些方法是保留方法,通常不會在外部被顯式調用,他們使用雙下劃線(__)開頭,他們被稱為魔術方法(Magic Methods)。php官方也不建議定義其他雙下劃線開頭的方法。
這次介紹屬性重載方法:get/set/isset/unset
public void __set ( string $name , mixed $value ) public mixed __get ( string $name ) public bool __isset ( string $name ) public void __unset ( string $name )
這些方法觸發的時機,都是在訪問不可訪問的屬性的時候。
如以下:
1 <?php 2 3 class Cls{ 4 5 private $a = 0; 6 protected $b = 1; 7 public $c = 2; 8 var $d = 3; 9 10 private $_properties = array(0); 11 12 public function __set($name, $value){ 13 echo __METHOD__ . ' is called! ' . json_encode(func_get_args()) . "\n"; 14 return $this->_properties[$name] = $value; 15 } 16 17 public function __get($name){ 18 echo __METHOD__ . ' is called! ' . json_encode(func_get_args()) . "\n"; 19 return $this->_properties[$name]; 20 } 21 22 public function __isset($name){ 23 echo __METHOD__ . ' is called! ' . json_encode(func_get_args()) . "\n"; 24 return isset($this->_properties[$name]); 25 } 26 27 public function __unset($name){ 28 echo __METHOD__ . ' is called! ' . json_encode(func_get_args()) . "\n"; 29 unset($this->_properties[$name]); 30 } 31 32 public function dump(){ 33 echo "====================== DUMP START ====================\n"; 34 echo <<<STR 35 a: $this->a 36 b: $this->b 37 c: $this->c 38 d: $this->d\n 39 STR; 40 foreach($this->_properties as $k => $v){ 41 echo <<<STR 42 Property $k: $v\n 43 STR; 44 } 45 46 echo "====================== DUMP STOP =====================\n"; 47 48 } 49 } 50 $string = 'abcdef'; 51 $obj = new Cls(); 52 53 $list = array('isset', 'get', 'set', 'isset', 'unset', 'isset'); 54 $obj->dump(); 55 foreach($list as $method){ 56 for($i = 0 ; $i < strlen($string) ; $i ++){ 57 $char = $string{$i}; 58 echo "------ $method : $char ------\n"; 59 switch($method){ 60 case 'isset': 61 echo json_encode($tmp = isset($obj->$char)) . "\n"; 62 break; 63 case 'get': 64 echo json_encode($tmp = $obj->$char) . "\n"; 65 break; 66 case 'set': 67 echo json_encode($tmp = $obj->$char = $char . "-val") . "\n"; 68 break; 69 case 'unset': 70 unset($obj->$char); 71 break; 72 default: 73 break; 74 } 75 } 76 $obj->dump(); 77 }
結果輸出:
====================== DUMP START ==================== a: 0 b: 1 c: 2 d: 3 Property 0: 0 ====================== DUMP STOP ===================== ------ isset : a ------ Cls::__isset is called! ["a"] false ------ isset : b ------ Cls::__isset is called! ["b"] false ------ isset : c ------ true ------ isset : d ------ true ------ isset : e ------ Cls::__isset is called! ["e"] false ------ isset : f ------ Cls::__isset is called! ["f"] false ====================== DUMP START ==================== a: 0 b: 1 c: 2 d: 3 Property 0: 0 ====================== DUMP STOP ===================== ------ get : a ------ Cls::__get is called! ["a"] null ------ get : b ------ Cls::__get is called! ["b"] null ------ get : c ------ 2 ------ get : d ------ 3 ------ get : e ------ Cls::__get is called! ["e"] null ------ get : f ------ Cls::__get is called! ["f"] null ====================== DUMP START ==================== a: 0 b: 1 c: 2 d: 3 Property 0: 0 ====================== DUMP STOP ===================== ------ set : a ------ Cls::__set is called! ["a","a-val"] "a-val" ------ set : b ------ Cls::__set is called! ["b","b-val"] "b-val" ------ set : c ------ "c-val" ------ set : d ------ "d-val" ------ set : e ------ Cls::__set is called! ["e","e-val"] "e-val" ------ set : f ------ Cls::__set is called! ["f","f-val"] "f-val" ====================== DUMP START ==================== a: 0 b: 1 c: c-val d: d-val Property 0: 0 Property a: a-val Property b: b-val Property e: e-val Property f: f-val ====================== DUMP STOP ===================== ------ isset : a ------ Cls::__isset is called! ["a"] true ------ isset : b ------ Cls::__isset is called! ["b"] true ------ isset : c ------ true ------ isset : d ------ true ------ isset : e ------ Cls::__isset is called! ["e"] true ------ isset : f ------ Cls::__isset is called! ["f"] true ====================== DUMP START ==================== a: 0 b: 1 c: c-val d: d-val Property 0: 0 Property a: a-val Property b: b-val Property e: e-val Property f: f-val ====================== DUMP STOP ===================== ------ unset : a ------ Cls::__unset is called! ["a"] ------ unset : b ------ Cls::__unset is called! ["b"] ------ unset : c ------ ------ unset : d ------ ------ unset : e ------ Cls::__unset is called! ["e"] ------ unset : f ------ Cls::__unset is called! ["f"] ====================== DUMP START ==================== Cls::__get is called! ["c"] Cls::__get is called! ["d"] a: 0 b: 1 c: d: Property 0: 0 ====================== DUMP STOP ===================== ------ isset : a ------ Cls::__isset is called! ["a"] false ------ isset : b ------ Cls::__isset is called! ["b"] false ------ isset : c ------ Cls::__isset is called! ["c"] false ------ isset : d ------ Cls::__isset is called! ["d"] false ------ isset : e ------ Cls::__isset is called! ["e"] false ------ isset : f ------ Cls::__isset is called! ["f"] false ====================== DUMP START ==================== Cls::__get is called! ["c"] Cls::__get is called! ["d"] a: 0 b: 1 c: d: Property 0: 0 ====================== DUMP STOP =====================
特別強調,當這個屬性不存在(基於當前訪問權限)的時候,才會觸發這些方法。因此嚴重建議使用確定的鍵值進行get/set處理,特別是對應的鍵值不要有訪問權限問題,如上例中的a和b,會導致在類內部、類外部、子類內處理的方式不同,開發時產生不可預知的返回值,極易產生bug。
empty方法不能調用屬性重載方法。
$tmp = $obj->$char = $char . "-val"
此例中,__set自身的返回值將被忽略,同樣__get也不會被調用。
此外:屬性重載方法必須聲明為public,同時參數不能使用引用傳遞,靜態屬性不能通過這些方法重載,這些方法也不能被聲明為static。
當然,使用魔術方法的時候,也會對反射系統造成影響。