摘要:魔術方法知識點整理代碼使用語法編寫一構造函數和析構函數構造函數具有構造函數的類會在每次創建新對象時先調用此方法,所以非常適合在使用對象之前做一些初始化工作。在析構函數中調用將會中止其余關閉操作的運行。析構函數中拋異常會導致致命錯誤。
PHP魔術方法知識點整理
代碼使用PHP7.2語法編寫一、構造函數和析構函數 __construct() 構造函數
__construct ([ mixed $args [, $... ]] ) : void
具有構造函數的類會在每次創建新對象時先調用此方法,所以非常適合在使用對象之前做一些初始化工作。
如果子類中定義了構造函數則不會隱式調用其父類的構造函數。要執行父類的構造函數,需要在子類的構造函數中調用 parent::__construct()。如果子類沒有定義構造函數則會從父類繼承。Code
當子類的 __construct() 與父類 __construct() 具有不同參數不同時,PHP 不會產生錯誤信息。這一點與其他的類方法不同。
書籍類
/** * 書籍類 * Class Book */ class Book { /** * 書籍名稱 * @var string $name */ public $name; /** * 書籍作者 * @var string $author */ public $author; /** * 構造函數 * @param $name * @param $author */ public function __construct(string $name, string $author) { $this->name = $name; $this->author = $author; } }
計算機書籍類繼承自Book類,且增加了一個屬性 $category
/** * 計算機書籍類 * Class ComputerBook */ class ComputerBook extends Book { /** * 計算機書籍多了分類屬性 * @var string $category */ public $category; /** * 構造函數 * 這里的構造函數幣父類的構造函數多了一個參數,但是PHP并不會報錯。 * 而其他的類方法在這種情況下會報錯。 * @param $name * @param $author * @param $category */ public function __construct(string $name, string $author, string $category) { parent::__construct($name, $author); $this->category = $category; } }
$book = new Book("茶館", "老舍"); echo <<name} 書籍作者: {$book->author} --- TXT ; $computerBook = new ComputerBook("高性能MySQL","Baron", "數據庫"); echo << name} 書籍作者: {$computerBook->author} 書籍分類: {$computerBook->category} --- TXT ;
書籍名稱: 茶館 書籍作者: 老舍 --- 書籍名稱: 高性能MySQL 書籍作者: Baron 書籍分類: 數據庫 ---__destruct() 析構函數
__destruct ( void ) : void
析構函數會在到某個對象的所有引用都被刪除或者當對象被顯式銷毀時執行。
如果子類中定義了析構函數則不會隱式調用其父類的析構函數。要執行父類的析構函數,必須在子類的析構函數體中顯式調用 parent::__destruct()。如果子類沒有定義析構函數則會從父類繼承。Code
析構函數即使在使用 exit() 終止腳本運行時也會被調用。
在析構函數中調用 exit() 將會中止其余關閉操作的運行。
析構函數中拋異常會導致致命錯誤。
析構函數即使在使用 exit() 終止腳本運行時也會被調用。
/** * 房子類 * Class House */ class House { /** * 析構函數 */ public function __destruct() { echo "要開始拆房子了 "; $this->dismantleRoof(); $this->demolishWall(); } /** * 拆除屋頂 */ private function dismantleRoof() { echo "屋頂被拆除 "; } /** * 推掉墻 */ private function demolishWall() { echo "墻被推到 "; } } // 新建一座房子 $house = new House(); // 腳本停止運行 exit();
要開始拆房子了 屋頂被拆除 墻被推到
在析構函數中調用 exit() 將會中止其余關閉操作的運行。
/** * 別墅類 * Class House */ class Villa extends House { /** * 析構函數 */ public function __destruct() { $this->demolishSwimmingPool(); // 拆掉游泳池后后悔了,不想繼續拆了 exit(); parent::__destruct(); } /** * 拆掉游泳池 */ private function demolishSwimmingPool() { echo "拆掉游泳池 "; } } // 新建一棟別墅 $villa = new Villa(); // 腳本停止運行 exit();
拆掉游泳池
析構函數中拋異常會導致致命錯誤。
/** * 大廈類 * Class House */ class Mansion extends House { /** * 析構函數 */ public function __destruct() { $this->evacuateCrowd(); parent::__destruct(); } /** * 疏散大廈里的人 */ private function evacuateCrowd() { throw new Exception("大廈里還有人,不能拆!"); } } // 新建一座大廈 $mansion = new Mansion(); // 腳本停止運行 exit();
PHP Fatal error: Uncaught Exception: 大廈里還有人,不能拆!二、對象復制 __clone()
__clone ( void ) : void
通過 clone 關鍵字克隆對象,當復制完成時,如果對象定義了 __clone() 方法,則新創建的對象(復制生成的對象)中的 __clone() 方法會被調用。
對象中的 __clone() 方法不能被直接調用。Code
當對象被復制后,PHP 5 會對對象的所有屬性執行一個淺復制(shallow copy)。所有的引用屬性仍然會是一個指向原來的變量的引用。此時我們需要在 __clone 中強制復制一份對象中的引用屬性。
/** * Class MyCloneable */ class MyCloneable { public $objectA; public $objectB; function __clone() { echo "MyCloneable::__clone "; // 強制復制一份this->objectA, 否則仍然指向同一個對象 $this->objectA = clone $this->objectA; } } /** * Class SubObject */ class SubObject { /** * 靜態計數器,對象每被實例化或clone一次,則+1 * @var int */ public static $counter = 0; public $instance; public function __construct() { echo "SubObject::__construct "; $this->instance = ++ self::$counter; } public function __clone() { echo "SubObject::__clone "; $this->instance = ++ self::$counter; } }
$myCloneable = new MyCloneable(); echo "實例化對象SubObject,并復制給 $myCloneable->objectA : "; $myCloneable->objectA = new SubObject(); echo "實例化對象SubObject,并復制給 $myCloneable->objectB : "; $myCloneable->objectB = new SubObject(); echo "復制對象 $myCloneable "; $myCloneable2 = clone $myCloneable; echo "------------------- "; echo "原始對象: "; print_r($myCloneable); echo "復制的對象: "; print_r($myCloneable2);
實例化對象SubObject,并復制給 $myCloneable->objectA : SubObject::__construct 實例化對象SubObject,并復制給 $myCloneable->objectB : SubObject::__construct 復制對象 $myCloneable MyCloneable::__clone SubObject::__clone ------------------- 原始對象: MyCloneable Object ( [objectA] => SubObject Object ( [instance] => 1 ) [objectB] => SubObject Object ( [instance] => 2 ) ) 復制的對象: MyCloneable Object ( [objectA] => SubObject Object ( [instance] => 3 ) [objectB] => SubObject Object ( [instance] => 2 ) )三、重載魔術方法
PHP中的重載與其它絕大多數面向對象語言不同。傳統的重載是用于提供多個同名的類方法,但各方法的參數類型和個數不同。
PHP的重載(overloading)是指動態地創建類屬性和方法。是通過魔術方法(magic methods)來實現的。
當調用當前環境下不可訪問(未定義或不可見)的類屬性或方法時,重載方法會被調用。
所有的重載方法都必須被聲明為 public,且參數都不能通過引用傳遞。
屬性重載的魔術方法有:__set(), __get(), __isset(), __unset()。
方法重載的魔術方法有:__call(), __callStatic()
在給不可訪問屬性賦值時,__set() 會被調用。
public __set ( string $name , mixed $value ) : void
$name 為要賦值的屬性名,$value 為屬性值。
__get()讀取不可訪問屬性的值時,__get() 會被調用。
public __get ( string $name ) : mixed
$name 為要訪問的屬性名。
__isset()當對不可訪問屬性調用 isset() 或 empty() 時,__isset() 會被調用。
public __isset ( string $name ) : bool
$name 為屬性名。
在除 isset() 外的其它語言結構中無法使用重載的屬性,這意味著當對一個重載的屬性使用 empty() 時,重載魔術方法將不會被調用。為避開此限制,必須將重載屬性賦值到變量再使用 empty()。
__unset()當對不可訪問屬性調用 unset() 時,__unset() 會被調用。
public __unset ( string $name ) : void
$name 為屬性名。
Codeclass Property { /** * 被重載的數據保存在該屬性 * @var array */ private $data = []; /** * 已經定義的屬性不會調用重載魔術方法 * @var int */ public $declared = 1; /** * 只有從類外部訪問屬性時,才會調用重載魔術方法 * @var int */ private $hidden = 2; /** * @param $name * @param $value */ public function __set($name, $value) { echo "Setting "{$name}" to "{$value}" "; $this->data[$name] = $value; } /** * @param $name * @return mixed|null */ public function __get($name) { echo "Getting "$name" "; if (array_key_exists($name, $this->data)) { return $this->data[$name]; } } /** * @param $name * @return bool */ public function __isset($name) { echo "Is "$name" set? "; return isset($this->data[$name]); } /** * @param $name */ public function __unset($name) { echo "Unsetting "$name" "; unset($this->data[$name]); } /** * 非魔術方法 * @return int */ public function getHidden() { return $this->hidden; } }
$obj = new Property; echo "為不存在的屬性a賦值:"; $obj->a = 1; echo "獲取不存在的屬性a的值:", $obj->a . " "; echo "---------------------------- "; echo "使用empty判斷屬性a是否為空 "; var_dump(empty($obj->a)); echo "使用isset判斷屬性a是否存在 "; var_dump(isset($obj->a)); echo "---------------------------- "; echo "使用unset注銷掉屬性a的值 "; unset($obj->a); echo "使用empty判斷屬性a是否為空 "; var_dump(empty($obj->a)); echo "使用isset判斷屬性a是否存在 "; var_dump(isset($obj->a)); echo "---------------------------- "; echo "獲取可訪問的屬性declared的值:", $obj->declared . " "; echo "為不可訪問的屬性hidden賦值0,此時調用了魔術方法__set() "; $obj->hidden = 0; echo "通過public方法getHidden()從類內部訪問私有屬性hidden的值,不會調用魔術方法: ", $obj->getHidden() . " "; echo "獲取不可訪問的屬性hidden的值,此時調用了魔術方法__get(): " , $obj->hidden . " ";
為不存在的屬性a賦值:Setting "a" to "1" 獲取不存在的屬性a的值:Getting "a" 1 ---------------------------- 使用empty判斷屬性a是否為空 Is "a" set? Getting "a" /vagrant/magic/overloading.php:119: bool(false) 使用isset判斷屬性a是否存在 Is "a" set? /vagrant/magic/overloading.php:122: bool(true) ---------------------------- 使用unset注銷掉屬性a的值 Unsetting "a" 使用empty判斷屬性a是否為空 Is "a" set? /vagrant/magic/overloading.php:130: bool(true) 使用isset判斷屬性a是否存在 Is "a" set? /vagrant/magic/overloading.php:133: bool(false) ---------------------------- 獲取可訪問的屬性declared的值:1 為不可訪問的屬性hidden賦值0,此時調用了魔術方法__set() Setting "hidden" to "0" 通過public方法getHidden()從類內部訪問私有屬性hidden的值,不會調用魔術方法: 2 獲取不可訪問的屬性hidden的值,此時調用了魔術方法__get(): Getting "hidden" 0方法重載 __call()
在對象中調用一個不可訪問的方法時,__call() 會被調用。
public __call ( string $name , array $arguments ) : mixed
$name 參數是要調用的方法名稱。$arguments 參數是一個枚舉數組,包含著要傳遞給方法 $name 的參數。
__callStatic()調用一個不可訪問的類靜態方法時,__callStatic() 會被調用。
public static __callStatic ( string $name , array $arguments ) : mixed
$name 參數是要調用的方法名稱。$arguments 參數是一個枚舉數組,包含著要傳遞給方法 $name 的參數。
Codeclass Method { public function __call($name, $arguments) { // 注意: $name 的值區分大小寫 echo "調用對象不可訪問的方法 "$name" ", implode(", ", $arguments). " "; } public static function __callStatic($name, $arguments) { // 注意: $name 的值區分大小寫 echo "調用不可訪問的類靜態方法 "$name" ", implode(", ", $arguments). " "; } }
$obj = new Method; $obj->run("參數1", "參數2", "參數3"); Method::run("參數1", "參數2", "參數3");
調用對象不可訪問的方法 "run" 參數1, 參數2, 參數3 調用不可訪問的類靜態方法 "run" 參數1, 參數2, 參數3四、序列化魔術方法 __sleep()
public __sleep ( void ) : array
使用 serialize() 函數對對象進行序列化時,若類中存在魔術方法 __sleep() ,則會先被調用 __sleep(),然后才執行序列化操作。
此功能可以用于清理對象,并返回一個包含對象中所有應被序列化的變量名稱的數組。如果該方法未返回任何內容,則 NULL 被序列化,并產生一個 E_NOTICE 級別的錯誤。
__sleep() 方法常用于提交未提交的數據,或類似的清理操作。同時,如果有一些很大的對象,但不需要全部保存,這個功能就很好用。
__sleep() 不能返回父類的私有成員的名字。這樣做會產生一個 E_NOTICE 級別的錯誤。可以用 Serializable 接口來替代。__wakeup()
__wakeup ( void ) : void
使用 unserialize() 函數對對象進行反序列化時,若類中存在魔術方法 __wakeup() ,則會先被調用 __wakeup() ,然后才執行反序列化操作。
在反序列化操作中,__wakeup() 方法常用于重新建立數據庫連接,或執行其它初始化操作。
class Connection { /** * 數據庫連接資源 * @var */ protected $link; /** * 連接數據庫所需要的屬性 * @var */ private $server, $username, $password, $db; /** * 構造函數,建立數據庫連接 * unserialize 執行反序列化時不會調用構造函數 * @param $server * @param $username * @param $password * @param $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 = mysqli_connect($this->server, $this->username, $this->password); mysqli_select_db($this->db, $this->link); } /** * 使用serialize序列化對象時,只保存以下四個對象屬性 * @return array */ public function __sleep() { return ["server", "username", "password", "db"]; } /** * unserialize 執行反序列化時不會調用構造函數 * 因此使用 __wakeup 函數重新進行數據庫連接 */ public function __wakeup() { $this->connect(); } }五、其他魔術方法 __toString()
public __toString ( void ) : string
當對象被當做字符串使用時,調用 __toString() 方法,該方法必須返回一個字符串。
若對象未定義 __toString() 方法,或 __toString() 方法返回值不是字符串,則會產生 E_RECOVERABLE_ERROR 級別的致命錯誤。
不能在 __toString() 方法中拋出異常。這么做會導致致命錯誤。Code
class ToString { public $foo; public function __construct($foo) { $this->foo = $foo; } public function __toString() { return $this->foo; } } $class = new ToString("Hello World!"); echo $class;
Hello World!__invoke()
__invoke ([ $... ] ) : mixed
當嘗試以調用函數的方式調用一個對象時,__invoke() 方法會被自動調用。
Codeclass CallableClass { function __invoke($foo) { return $foo; } } $callable = new CallableClass(); echo $callable("Hello World! "); var_dump(is_callable($callable));
Hello World! /vagrant/magic/__invoke.php:19: bool(true)
class UncallableClass { } $uncallable = new UncallableClass(); var_dump(is_callable($uncallable));
/vagrant/magic/__invoke.php:27: bool(false)
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/31678.html
摘要:使用中文函數名和變量名面積長寬長寬面積合法,輸出中文符號函數名。類型聲明類型聲明允許函數在調用時要求參數為特定類型。需要使用自己的包裝函數來將這些結構用作可變函數。匿名函數目前是通過類來實現的。 一、函數的定義 1. 函數的命名規則 函數名可以包含字母、數字、下劃線,不能以數字開頭。 function Func_1(){ } //合法 function func1(){ } //合法 ...
摘要:繼上一篇面試常考內容之面向對象發表后,今天更新,需要的可以直接點擊文字進行跳轉獲取。析構函數,當對象被銷毀時調用。 PHP面試專欄正式起更,每周一、三、五更新,提供最好最優質的PHP面試內容。繼上一篇PHP面試常考內容之面向對象(1)發表后,今天更新(2),需要(1)的可以直接點擊文字進行跳轉獲取。整個面向對象文章的結構涉及的內容模塊有: 一、面向對象與面向過程有什么區別?二、面向對...
摘要:是決定正則表達式匹配規則的主要部分。二分隔符分隔符的選擇當使用函數的時候,正則表達式必須由分隔符閉合包裹。果分隔符經常在正則表達式內出現,最好使用其他分隔符來提高可讀性。需要將一個字符串放入正則表達式中使用時,可以用函數對其進行轉義。 一、簡介 1. 什么是正則表達式 正則表達式(Regular Expression)就是用某種模式去匹配一類字符串的一種公式。正則表達式使用單個字符串來...
摘要:消息隊列技術介紹后端掘金一消息隊列概述消息隊列中間件是分布式系統中重要的組件,主要解決應用耦合異步消息流量削鋒等問題。的內存優化后端掘金聲明本文內容來自開發與運維一書第八章,如轉載請聲明。 消息隊列技術介紹 - 后端 - 掘金一、 消息隊列概述 消息隊列中間件是分布式系統中重要的組件,主要解決應用耦合、異步消息、流量削鋒等問題。實現高性能、高可用、可伸縮和最終一致性架構。是大型分布式系...
摘要:聲明靜態變量時不能用表達式的結果對其賦值正確錯誤使用表達式的結果賦值錯誤使用表達式的結果賦值靜態變量與遞歸函數靜態變量提供了一種處理遞歸函數的方法。 一、變量的定義 1. 變量的命名規則 變量名可以包含字母、數字、下劃線,不能以數字開頭。 $Var_1 = foo; // 合法 $var1 = foo; // 合法 $_var1 = foo; // 合法 $Var-1 = foo; /...
閱讀 1565·2021-11-02 14:42
閱讀 2308·2021-10-11 10:58
閱讀 656·2021-09-26 09:46
閱讀 2908·2021-09-08 09:35
閱讀 1403·2021-08-24 10:01
閱讀 1228·2019-08-30 15:54
閱讀 3597·2019-08-30 15:44
閱讀 1792·2019-08-30 10:49