PHP中以雙下劃線「__」開始命名的內建方法被稱作魔術方法,包括「__construct()」、「__set()」、「__get()」、「__isset()」、「__unset()」、「__sleep()」、「__wakeup()」、「__call()」、「__invoke()」等;其中「__construct()」是類的構造方法,是物件建立完成後第一個被物件自動呼叫的方法。
本教學操作環境:windows7系統、PHP8版、DELL G3電腦
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
官方網站獲得更多幫助。
推薦學習:《》
以上就是php魔術方法是什麼的詳細內容,更多請關注TW511.COM其它相關文章!