什麼是魔術方法?這篇文章帶大家了解16個PHP 開發者必知必會的魔術方法,希望對大家有幫助!
在PHP中,以雙底線(__)開始命名的方法被稱為PHP中的魔術方法,它們在PHP中充當很重要的角色。魔術方法包括:
方法名稱 | 描述 |
---|---|
__construct() | 類別的建構子 |
__destruct() | #類別的析構函式 |
__call($ funName, $arguments) | 當呼叫一個未定義或不可達方法時, __call() 方法將會被呼叫。 |
__callStatic($funName, $arguments) | 當呼叫一個未定義或不可達的靜態方法時, __callStatic() 方法將會被呼叫。 |
__get($propertyName) | 當取得一個類別的成員變數時, __get() 方法將會被呼叫。 |
__set($property, $value) | 當賦值一個類別的成員變數時, __set() 方法將會被呼叫。 |
__isset($content) | 當呼叫isset() 或empty() 對一個未定義或不可達的成員賦值時, __isset() 方法將會被調用。 |
__unset($content) | 當呼叫 reset() 對一個未定義或不可達的成員更新時, __unset() 方法將會被呼叫。 |
__sleep() | 當執行序列化 serialize() 時,__sleep() 方法將會先被呼叫。 |
__wakeup() | 當執行反序列化 deserialization() 時, __wakeup() 方法會先被呼叫。 |
__toString() | 當使用 echo 方法直接輸出顯示物件時,__toString() 方法首先被呼叫。 |
__invoke() | 使用呼叫函數(function)存取一個物件時, __invoke() 方法會先被呼叫。 |
__set_state($an_array) | 當呼叫 var_export() 方法時,__set_state() 方法將會被呼叫。 |
__clone() | 當物件被複製賦值時,__clone() 方法將會被呼叫。 |
__autoload($className) | 試圖載入一個未定義的類別時呼叫。 |
__debugInfo() | #輸出 debug 資訊。 |
本文將使用一些實例來展示 PHP 魔術方法的運用。
當建立物件時,PHP 類別的建構方法是第一個被呼叫的方法。每個類別都有構造方法。若你沒在類別中明確聲明定義它,將會有一個預設的無參類構造方法存在,雖然它不會在類別中定義出現。
1) 建構方法的運用
類別的建構方法通常用於執行一些初始化任務,諸如當建立物件時,為成員初始化賦值。
2) 類別中建構方法的宣告格式
function __constrct([parameter list]){ 方法具体实现 //通常为成员变量初始赋值。 }
注意: 在多數類別中只可以宣告一個建構方法。因為, PHP 不支援構造方法重載。
以下是個完整的範例:
<?php class Person { public $name; public $age; public $sex; /** * 明确定义含参的构造方法 */ public function __construct($name="", $sex="Male", $age=22) { $this->name = $name; $this->sex = $sex; $this->age = $age; } /** * say 方法定义 */ public function say() { echo "Name:" . $this->name . ",Sex:" . $this->sex . ",Age:" . $this->age; } }
無參照建立 $Person1 物件。
$Person1 = new Person(); echo $Person1->say(); //显示:Name:,Sex:Male,Age:22
使用一個參數 "Jams" 呼叫建立 $Person2 物件。
$Person2 = new Person("Jams"); echo $Person2->say(); // 显示: Name: Jams, Sex: Male, Age: 22
使用3個參數呼叫建立 $Person3 物件。
$Person3 = new Person ("Jack", "Male", 25); echo $Person3->say(); // 显示:Name: Jack, Sex: Male, Age: 25
析構函數與建構子相反。
析構函數允許你在銷毀物件之前執行一些操作,例如關閉文件,清空結果集等等。
析構函數是 PHP 5 引進的新功能。
析構函式的宣告與建構子類似,以兩個底線開頭,名稱固定為 __destruct()
。
function __destruct() { //method body }
析構函數不能帶參數。
析構函數在類別中一般不常見。它是類別的可選部分,通常用於在類別銷毀之前完成一些清理任務。
這是使用析構函數的範例:
<?php class Person{ public $name; public $age; public $sex; public function __construct($name="", $sex="Male", $age=22) { $this->name = $name; $this->sex = $sex; $this->age = $age; } /** * say method */ public function say() { echo "Name:".$this->name.",Sex:".$this->sex.",Age:".$this->age; } /** * declare a destructor method */ public function __destruct() { echo "Well, my name is ".$this->name; } } $Person = new Person("John"); unset($Person); //destroy the object of $Person created above
輸出結果
Well, my name is John
此方法接受兩個參數。第一個參數為未定義的方法名稱,第二個參數則為傳入方法的參數所構成的陣列
function __call(string $function_name, array $arguments) { // method body }
在程式中呼叫未定義方法時,__call()
方法將會被呼叫。
範例
<?php class Person { function say() { echo "Hello, world!<br>"; } function __call($funName, $arguments) { echo "The function you called:" . $funName . "(parameter:" ; // Print the method's name that is not existed. print_r($arguments); // Print the parameter list of the method that is not existed. echo ")does not exist!!<br>\n"; } } $Person = new Person(); $Person->run("teacher"); // If the method which is not existed is called within the object, then the __call() method will be called automatically. $Person->eat("John", "apple"); $Person->say();
顯示結果
The function you called: run (parameter: Array([0] => teacher)) does not exist! The function you called: eat (parameter: Array([0] => John[1] => apple)) does not exist! Hello world!
當程式中呼叫未定義的靜態方法,__callStatic( )
方法將會自動呼叫。
__callStatic()
的用法類似於 __call()
。以下舉個例子:
<?php class Person { function say() { echo "Hello, world!<br>"; } public static function __callStatic($funName, $arguments) { echo "The static method you called:" . $funName . "(parameter:" ; // 打印出未定义的方法名。 print_r($arguments); // 打印出未定义方法的参数列表。 echo ")does not exist!<br>\n"; } } $Person = new Person(); $Person::run("teacher"); // 如果此项目内不存在的方法被调用了,那么 __callStatic() 方法将被自动调用。 $Person::eat("John", "apple"); $Person->say();
執行結果如下:
The static method you called: run (parameter: Array([0] => teacher)) does not exist! The static method you called: eat (parameter: Array([0] => John[1] => apple)) does not exist! Hello world!
當你嘗試在外部存取物件的私有屬性時,應用程式將會拋出例外並結束運行。我們可以使用 __get
方法來解決該問題。此方法可以取得從物件外部取得私有屬性的值。舉例如下
<?php class Person { private $name; private $age; function __construct($name="", $age=1) { $this->name = $name; $this->age = $age; } public function __get($propertyName) { if ($propertyName == "age") { if ($this->age > 30) { return $this->age - 10; } else { return $this->$propertyName; } } else { return $this->$propertyName; } } } $Person = new Person("John", 60); // Instantiate the object with the Person class and assign initial values to the properties with the constructor. echo "Name:" . $Person->name . "<br>"; // When the private property is accessed, the __get() method will be called automatically,so we can get the property value indirectly. echo "Age:" . $Person->age . "<br>"; // The __get() method is called automatically,and it returns different values according to the object itself.
結果顯示如下
Name: John Age: 50
set($property,$value)方法用來設定類別的私有屬性。分配了未定義的屬性後,將觸發set()方法,並且傳遞的參數是設定的屬性名稱和值。
下面是示範程式碼:
<?php class Person { private $name; private $age; public function __construct($name="", $age=25) { $this->name = $name; $this->age = $age; } public function __set($property, $value) { if ($property=="age") { if ($value > 150 || $value < 0) { return; } } $this->$property = $value; } public function say(){ echo "My name is ".$this->name.",I'm ".$this->age." years old"; } } $Person=new Person("John", 25); //请注意,类初始化并为“name”和“age”分配初始值。 $Person->name = "Lili"; // "name" 属性值被成功修改。如果没有__set()方法,程序将报错。 $Person->age = 16; // "age"属性修改成功。 $Person->age = 160; //160是无效值,因此修改失败。 $Person->say(); //输出:My name is Lili, I'm 16 years old。
程式碼運行結果:
My name is Lili, I'm 16 years old
在使用__isset()方法之前,讓我先解釋一下isset()方法的用法。 isset()方法主要用於確定是否設定了此變數。
如果在物件外部使用isset()方法,則有兩種情況:
那麼對於私有屬性,有什麼辦法知道它是否被設定了嗎?當然,只要在類別中定義__isset()方法,就可以在類別外部使用isset()方法來決定是否設定了私有屬性。
當在未定義或不可存取的屬性上呼叫isset()或empty()時,將呼叫__isset()方法。以下是一個範例:
<?php class Person { public $sex; private $name; private $age; public function __construct($name="", $age=25, $sex='Male') { $this->name = $name; $this->age = $age; $this->sex = $sex; } /** * @param $content * * @return bool */ public function __isset($content) { echo "The {$content} property is private,the __isset() method is called automatically.<br>"; echo isset($this->$content); } } $person = new Person("John", 25); // Initially assigned. echo isset($person->sex),"<br>"; echo isset($person->name),"<br>"; echo isset($person->age),"<br>";
程式碼執行結果如下:
1 The name property is private,the __isset() method is called automatically. 1 The age property is private,the __isset() method is called automatically. 1
與isset()方法類似,當在未定義或在不可存取的屬性上呼叫unset()方法時,將呼叫unset()方法。以下是一個範例:
<?php class Person { public $sex; private $name; private $age; public function __construct($name="", $age=25, $sex='Male') { $this->name = $name; $this->age = $age; $this->sex = $sex; } /** * @param $content * * @return bool */ public function __unset($content) { echo "It is called automatically when we use the unset() method outside the class.<br>"; echo isset($this->$content); } } $person = new Person("John", 25); // Initially assigned. unset($person->sex),"<br>"; unset($person->name),"<br>"; unset($person->age),"<br>";
程式碼的執行結果如下:
It is called automatically when we use the unset() method outside the class. 1 It is called automatically when we use the unset() method outside the class. 1
serialize()方法將檢查類別中是否有魔術方法__sleep ()。如果存在,將首先呼叫該方法,然後執行序列化操作。
__sleep()方法通常用於指定在儲存資料之前需要序列化的屬性。如果有一些非常大的物件不需要全部保存,那麼您會發現此功能非常有用。
有關詳細信息,請參考以下程式碼:
<?php class Person { public $sex; public $name; public $age; public function __construct($name="", $age=25, $sex='Male') { $this->name = $name; $this->age = $age; $this->sex = $sex; } /** * @return array */ public function __sleep() { echo "It is called when the serialize() method is called outside the class.<br>"; $this->name = base64_encode($this->name); return array('name', 'age'); // It must return a value of which the elements are the name of the properties returned. } } $person = new Person('John'); // Initially assigned. echo serialize($person); echo '<br/>';
程式碼運行結果如下:
It is called when the serialize() method is called outside the class. O:6:"Person":2:{s:4:"name";s:8:"5bCP5piO";s:3:"age";i:25;}
與sleep ()方法相比,wakeup()方法通常用於反序列化操作,例如重建資料庫連接或執行其他初始化操作。
下面是相關實例:
<?php class Person { public $sex; public $name; public $age; public function __construct($name="", $age=25, $sex='Male') { $this->name = $name; $this->age = $age; $this->sex = $sex; } /** * @return array */ public function __sleep() { echo "It is called when the serialize() method is called outside the class.<br>"; $this->name = base64_encode($this->name); return array('name', 'age'); // It must return a value of which the elements are the name of the properties returned. } /** * __wakeup */ public function __wakeup() { echo "It is called when the unserialize() method is called outside the class.<br>"; $this->name = 2; $this->sex = 'Male'; // There is no need to return an array here. } } $person = new Person('John'); // Initially assigned. var_dump(serialize($person)); var_dump(unserialize(serialize($person)));
程式碼運行結果如下:
It is called when the serialize() method is called outside the class. string(58) "O:6:"Person":2:{s:4:"name";s:8:"5bCP5piO";s:3:"age";i:25;}" It is called when the unserialize() method is called outside the class. object(Person)#2 (3) { ["sex"]=> string(3) "Male" ["name"]=> int(2) ["age"]=> int(25) }
使用echo方法直接列印物件時,將呼叫__toString()方法。
注意:此方法必須傳回字串,否則將在E_RECOVERABLE_ERROR
層級上引發致命錯誤。而且您也不能在__toString()方法中拋出異常。
下面是相關的實例:
<?php class Person { public $sex; public $name; public $age; public function __construct($name="", $age=25, $sex='Male') { $this->name = $name; $this->age = $age; $this->sex = $sex; } public function __toString() { return 'go go go'; } } $person = new Person('John'); // Initially assigned. echo $person;
執行程式碼結果如下:
go go go
那麼,如果在類別中未定義__toString()方法怎麼辦?讓我們嘗試一下。
<?php class Person { public $sex; public $name; public $age; public function __construct($name="", $age=25, $sex='Male') { $this->name = $name; $this->age = $age; $this->sex = $sex; } } $person = new Person('John'); // Initially assigned. echo $person;
執行程式碼結果如下:
Catchable fatal error: Object of class Person could not be converted to string in D:\phpStudy\WWW\test\index.php on line 18
显然,它在页面上报告了一个致命错误,PHP语法不支持这样的写法。
当您尝试以调用函数的方式调用对象时,__ invoke()方法将被自动调用。
注意:此功能仅在PHP 5.3.0及更高版本中有效。
下面是相关实例:
<?php class Person { public $sex; public $name; public $age; public function __construct($name="", $age=25, $sex='Male') { $this->name = $name; $this->age = $age; $this->sex = $sex; } public function __invoke() { echo 'This is an object'; } } $person = new Person('John'); // Initially assigned. $person();
运行代码结果如下:
This is an object
如果坚持使用对象作为方法(但未定义__invoke()方法),则将得到以下结果:
Fatal error: Function name must be a string in D:\phpStudy\WWW\test\index.php on line 18
从PHP 5.1.0开始,在调用var_export()导出类代码时会自动调用__set_state()方法。
__set_state()方法的参数是一个包含所有属性值的数组,其格式为array('property'=> value,...)
在以下示例中,我们没有定义__set_state()方法:
<?php class Person { public $sex; public $name; public $age; public function __construct($name="", $age=25, $sex='Male') { $this->name = $name; $this->age = $age; $this->sex = $sex; } } $person = new Person('John'); // Initially assigned. var_export($person);
执行代码结果如下:
Person::__set_state(array( 'sex' => 'Male', 'name' => 'John', 'age' => 25, ))
显然,对象的属性已打印。
现在让我们看看定义__set_state()方法的另一种情况:
<?php class Person { public $sex; public $name; public $age; public function __construct($name="", $age=25, $sex='Male') { $this->name = $name; $this->age = $age; $this->sex = $sex; } public static function __set_state($an_array) { $a = new Person(); $a->name = $an_array['name']; return $a; } } $person = new Person('John'); // Initially assigned. $person->name = 'Jams'; var_export($person);
执行代码结果如下:
Person::__set_state(array( 'sex' => 'Male', 'name' => 'Jams', 'age' => 25, ))
在PHP中,我们可以使用clone关键字通过以下语法克隆对象:
$copy_of_object = clone $object;
但是,使用clone关键字只是一个浅拷贝,因为所有引用的属性仍将指向原始变量。
如果在对象中定义了clone()方法,则将在复制生成的对象中调用clone()方法,该方法可用于修改属性的值(如有必要)。
下面是相关的示例:
<?php class Person { public $sex; public $name; public $age; public function __construct($name="", $age=25, $sex='Male') { $this->name = $name; $this->age = $age; $this->sex = $sex; } public function __clone() { echo __METHOD__."your are cloning the object.<br>"; } } $person = new Person('John'); // Initially assigned. $person2 = clone $person; var_dump('persion1:'); var_dump($person); echo '<br>'; var_dump('persion2:'); var_dump($person2);
运行代码结果如下:
Person::__clone your are cloning the object. string(9) "persion1:" object(Person)#1 (3) { ["sex"]=> string(3) "Male" ["name"]=> string(6) "John" ["age"]=> int(25) } string(9) "persion2:" object(Person)#2 (3) { ["sex"]=> string(3) "Male" ["name"]=> string(6) "John" ["age"]=> int(25) }
__autoload()方法可以尝试加载未定义的类。
过去,如果要在程序文件中创建100个对象,则必须使用include()或require()来包含100个类文件,或者必须在同一类文件中定义100个类。 例如以下:
/** * file non_autoload.php */ require_once('project/class/A.php'); require_once('project/class/B.php'); require_once('project/class/C.php'); . . . if (ConditionA) { $a = new A(); $b = new B(); $c = new C(); // … } else if (ConditionB) { $a = newA(); $b = new B(); // … }
那么,如果我们使用__autoload()方法呢?
/** * file autoload_demo.php */ function __autoload($className) { $filePath = “project/class/{$className}.php”; if (is_readable($filePath)) { require($filePath); } } if (ConditionA) { $a = new A(); $b = new B(); $c = new C(); // … } else if (ConditionB) { $a = newA(); $b = new B(); // … }
当PHP引擎第一次使用类A时,如果未找到类A,则autoload方法将被自动调用,并且类名称“ A”将作为参数传递。因此,我们在autoload()方法中需要做的是根据类名找到相应的类文件,然后将其包含在内。如果找不到该文件,则php引擎将抛出异常。
当执行 var_dump()
方法时,__debugInfo()
方法会被自动调用。如果 __debugInfo()
方法未被定义,那么 var_dump
方法或打印出这个对象的所有属性。
举例说明:
<?php class C { private $prop; public function __construct($val) { $this->prop = $val; } /** * @return array */ public function __debugInfo() { return [ 'propSquared' => $this->prop ** 2, ]; } } var_dump(new C(42));
执行结果:
object(C)#1 (1) { ["propSquared"]=> int(1764) }
注意:__debugInfo() 方法应该在 PHP 5.6.0 及以上版本中使用。
以上就是我所了解的 PHP
魔术方法,其中常用的包括 __set()
还有 __get()
和 __autoload()
。如果你还有其他疑问,可以从 PHP
官方网站获得更多帮助。
原文地址:https://www.tutorialdocs.com/article/16-php-magic-methods.html
译文地址:https://learnku.com/php/t/40919
推荐学习:《PHP视频教程》
以上是帶你吃透16個PHP魔術方法的詳細內容。更多資訊請關注PHP中文網其他相關文章!