PHP中要實現類似於Java中的getter
和setter
有多種方法,比較常用的有:
- 直接箭頭
->
調用屬性(最常用),不管有沒有聲明這個屬性,都可以使用,但會報Notice
級別的錯誤
$dog = new Dog();
$dog->name = 'hey';
- 添加
setter
和getter
方法,類似於Java
class Dog
{
private $name = ‘’;
public function setName($name) {
$this->name = $name;
}
public function getName() {
return $this->name;
}
}
- 使用魔術方法(最裝x)
class Dog1
{
private $_name = "";
function __set($property, $value) {
if ($property === 'name') $this->_name = $value;
}
function __get($property) {
if ($property === 'name') return $this->_name;
}
}
上面三種方法,大部分人能都想到的也就是前兩種方法,對於第三種方法PHP小白看了第一感覺就是好厲害(心中暗想這人一定是大佬),但是這樣寫真的能體現出編程水平嗎?
對這幾種方法,我們來對比下它們的執行效率:
方法一代碼:
方法二代碼:
方法三代碼:

主要就是兩個for
循環,外層循環10次,內層一百萬次
,總計循環了一千萬次
,convert
函數只是用來輸出可讀性更高的內存使用情況。現在在我本地測試一下,測試的機器時2015款的MBP,i5 16GB內存,PHP
是7.2.13(cli)
版本,執行結果分別如下:
方法一:
方法二:
方法三:
會什麼方法三會這麼慢?有人可能會說可能因爲魔術方法裏面的if
判斷,那我現在把if
去掉試試:

執行結果如下:
發現if
的影響很小,而且這種寫法也並不推薦,這裏的魔術方法就相當於一個攔截器,當調用未定義的屬性時就會調用魔術方法,但這裏只是測試,真實環境一定不能這麼寫。
從結果可以看出,我們直接使用箭頭函數速度是最快的,最常用最簡單的方法執行效率也是最高的,後面兩種方法不僅代碼行數多了一些,而且執行效率不及第一種,特別是使用魔術方法,執行效率是第一種的6倍
左右,是第二種的2倍
左右,古人常說“智者千慮必有一失,愚者千慮必有一得”大概就是這個意思吧,在這裏代碼行數和執行效率都增多了。
不過,對於第一種方法,可讀性就不是很高,不管屬性有沒有定義都能隨便調用,代碼並不規範,其他人在審查你的代碼時就不是很方便,建議屬性屬性使用前聲明下。
魔術方法還有哪些?
PHP中,__call()
方法可能是最有用的魔術方法了,用它可以實現很tricky的東西。當要調用類中未定義的方法時,__call()
會被調用,第一個參數是調用未定義的方法名稱,第二個參數是傳遞給調用方法的所有參數,是一個數組,__call()
的返回值會返回給調用者,這樣就好像調用一個真實存在的方法一樣。
同時__call
也可以用來實現委託
,委託
是指一個對象轉發一個請求給另一個對象,把請求的處理委託給另一個對象。這就有點類似於繼承,和在子類中調用父類的方法有點相似。但在繼承是父類與子類的關係是固定的,而使用委託可以在運行時改變使用的對象,委託比繼承具有更大的靈活性。代碼如下:
代碼中Doctor
類接收一個PersonWriter
對象作爲構造函數的參數,並將它存儲在$printer
中,在__call()
中檢查PersonWriter
中是否存在$methodName
方法,如果存在,就委託PersonWriter
對象來處理,並將當前類Doctor
的實例傳給它,運行結果如下:
這樣我們就不用在Doctor
中手動調用如下方法:
$this->printer->printMe($this);
如果此時給PersonWriter
增加幾個新的方法,使用委託可以節省很多時間,但代碼也會變得不清晰,不易理解。對於調用者來說,你提供的是一個動態的接口,沒有辦法進行反射(reflection),因爲調用的類與被委託的類之間的交互比較模糊,使用時需要提供說明文檔。
回到文章主題,我們對PHP的getter
和setter
相關使用進行了對比,以Java
程序猿的思維看第二種方法中規中矩,沒有任何套路,第一種和第三種應該是PHP纔有的,但第三種方式執行的效率遠不及前兩種,而第一種方式雖然效率最高,但使用時儘量還是把屬性聲明下,使代碼的結構更清晰。