php魔法函式
介紹
php中把以兩個下劃線__開頭的方法稱為魔法函式(Magic methods),這些方法在php中充當了舉足輕重的作用。魔法函式包括:
- __construct(),類的建構函式
- __destruct(),類的解構函式
- __call(),在物件中呼叫一個不可訪問方法時呼叫
- __callStatic(),在靜態上下文中呼叫一個不可訪問的方法時呼叫
- __get(),讀取不可訪問屬性的值時呼叫
- __set(),在給不可訪問屬性賦值時呼叫
- __isset(),當對不可訪問屬性呼叫isset()和empty()時,__isset()被呼叫
- __unset(),當對不可訪問屬性呼叫unset()時,__unset()會被呼叫
- __sleep(),執行serialize()時,先會呼叫這個函式
- __wakeup(),執行unserialize()時,先會呼叫這個函式
- __toString(),類被當成字串時的迴應方法
- __invoke(),當嘗試以呼叫函式的方式呼叫一個物件時,__invoke()方法會被自動呼叫
- __set_state(),當呼叫var_export()匯出類時,此靜態方法會被呼叫
- __clone(),當物件複製完成時呼叫
- __debuginfo(),當轉儲物件以獲取應顯示的屬性時,會被呼叫
- __autoload(),嘗試載入未定義的類
- __serialize(),執行serialize()時,先會呼叫這個函式(這個和__sleep()的區別後面會詳細介紹)
- __unserialize(),執行unserialize()時,先會呼叫這個函式(這個和__wakeup()的區別後面會詳細介紹)
範例
下面讓我們以例項的形式向大家講解下這幾個魔術方法時如何使用的。
1.__construct()
建構函式:
__construct(mixed ...$values = ""): void
PHP 允許開發者在一個類中定義一個方法作為建構函式。具有建構函式的類會在每次建立新物件時先呼叫此方法,所以非常適合在使用物件之前做一些初始化工作。如果沒有顯示地宣告它,那麼類中都會預設存在一個沒有引數且內容為空的構造方法。
__construct()建構函式的作用
通常構造方法被用來執行一些有用的初始化任務,如對成員屬性在建立物件時賦予初始值。
__construct()建構函式在類中的宣告格式
function __constrct([引數列表]){ 方法體 //通常用來對成員屬性進行初始化賦值
}
__construct()在類中宣告構造方法需要注意的事項
1、在同一個類中只能宣告一個構造方法,原因是,PHP不支援建構函式過載。 2、構造方法名稱是以兩個下畫線開始的__construct()
示例:
<?php
class Person
{
public $name;
public $age;
public $sex;
//顯示宣告一個構造方法且帶引數
public function __construct($name="", $sex="男", $age=22)
{
$this->name = $name;
$this->sex = $sex;
$this->age = $age;
}
public function say()
{
echo "我叫:" . $this->name . ",性別:" . $this->sex . ",年齡:" .$this->age;
}
}
建立物件$Person1且不帶任引數
$Person1 = new Person();
echo $Person1->say(); //輸出:我叫:,性別:男,年齡:22
建立物件$Person2且帶引數“小明”
$Person2 = new Person("小明");
echo $Person2->say(); //輸出:我叫:小明,性別:男,年齡:22
建立物件$Person3且帶三個引數
$Person3 = new Person("李四","男",25);
echo $Person3->say(); //輸出:我叫:李四,性別:男,年齡:25
2.__destruct()
通過上面的講解,現在我們已經知道了什麼叫構造方法。那麼與構造方法對應的就是析構方法。
析構方法允許在銷燬一個類之前執行的一些操作或完成一些功能,比如說關閉檔案、釋放結果集等。
解構函式會在到某個物件的所有引用都被刪除或者當物件被顯式銷燬時,__destruct()會被呼叫
析構方法是PHP5才引進的新內容。
__destruct()析構方法的宣告格式
function __destruct()
{
//方法體
}
注意:解構函式不能帶有任何引數。
__destruct()析構方法的呼叫
一般來說,析構方法在PHP中並不是很常用,它屬類中可選擇的一部分,通常用來完成一些在物件銷燬前的清理任務。
示例:
<?php
class Person{
public $name;
public $age;
public $sex;
public function __construct($name="", $sex="男", $age=22)
{
$this->name = $name;
$this->sex = $sex;
$this->age = $age;
}
//say方法
public function say()
{
echo "我叫:".$this->name.",性別:".$this->sex.",年齡:".$this->age;
}
//宣告一個解構函式
public function __destruct()
{
echo "我覺得我還可以再搶救一下,我的名字叫".$this->name;
}
}
$Person = new Person("小明");
unset($Person); //銷燬上面建立的物件$Person
輸出結果:
我覺得我還可以再搶救一下,我的名字叫小明
3.__call()
該方法有兩個引數,第一個引數 $function_name
會自動接收不存在的方法名,第二個 $arguments
則以數
組的方式接收不存在方法的多個引數。
在物件中呼叫一個不可訪問方法時,__call() 會被呼叫。
__call()方法的格式
function __call(string $function_name, array $arguments)
{
// 方法體
}
__call() 方法的作用
為了避免當呼叫的方法不存在時產生錯誤,而意外的導致程式中止,可以使用 __call() 方法來避免。
該方法在呼叫的方法不存在時會自動呼叫,程式仍會繼續執行下去。
示例:
<?php
class Person
{
function say()
{
echo "Hello, world!<br>";
}
//宣告一個__call函式
function __call($funName, $arguments)
{
echo "你所呼叫的函式:" . $funName . "(引數:" ; // 輸出呼叫不存在的方法名
print_r($arguments); // 輸出呼叫不存在的方法時的引數列表
echo ")不存在!<br>\n"; // 結束換行
}
}
$Person = new Person();
$Person->run("teacher"); // 呼叫物件中不存在的方法,則自動呼叫了物件中的__call()方法
$Person->eat("小明", "蘋果");
$Person->say();
輸出結果:
你所呼叫的函式:run(引數:Array ( [0] => teacher ) )不存在!
你所呼叫的函式:eat(引數:Array ( [0] => 小明 [1] => 蘋果 ) )不存在!
Hello, world!
4.__callStatic
在靜態上下文中呼叫一個不可訪問方法時,__callStatic() 會被呼叫。
此方法與上面所說的__call() 功能除了 __callStatic() 是為靜態方法準備的之外,其它都是一樣的
示例:
<?php
class Person
{
function say()
{
echo "Hello, world!<br>";
}
//宣告__callStatic函式
public static function __callStatic($funName, $arguments)
{
echo "你所呼叫的靜態方法:" . $funName . "(引數:" ; // 輸出呼叫不存在的方法名
print_r($arguments); // 輸出呼叫不存在的方法時的引數列表
echo ")不存在!<br>\n"; // 結束換行
}
}
//$Person = new Person();
Person::run("teacher"); // 呼叫物件中不存在的方法,則自動呼叫了物件中的__call()方法
Person::eat("小明", "蘋果");
//靜態方法不需要例項化也可呼叫,所以不需要建立一個新的類Person()
輸出結果:
你所呼叫的靜態方法:run(引數:Array ( [0] => teacher ) )不存在!
你所呼叫的靜態方法:eat(引數:Array ( [0] => 小明 [1] => 蘋果 ) )不存在!
注意:
- 靜態屬性不需要例項化即可呼叫。因為靜態屬性存放的位置是在類裡,呼叫方法為 "類名::屬性名";
- 靜態方法不需要例項化即可呼叫。同上
- 靜態方法不能呼叫非靜態屬性。因為非靜態屬性需要例項化後,存放在物件裡;
- 靜態方法可以呼叫非靜態方法,使用 self 關鍵詞。php 裡,一個方法被 self:: 後,它就自動轉變為靜態方法;
- 呼叫類的靜態函式時不會自動呼叫類的建構函式。
5.__get()
在 php 面向物件程式設計中,類的成員屬性被設定為 private
後,如果我們試圖在外面呼叫它則會出現“不能訪
問某個私有屬性”的錯誤。那麼為了解決這個問題,我們可以使用魔術方法 __get()
。
讀取不可訪問屬性的值時,__get() 會被呼叫。
魔術方法__get的作用
在程式執行過程中,通過它可以在物件的外部獲取私有成員屬性的值。
示例:
<?php
class Person
{
private $name;
private $age;
function __construct($name="", $age=1)
{
$this->name = $name;
$this->age = $age;
}
/**
* 在類中新增__get()方法,在直接獲取屬性值時自動呼叫一次,以屬性名作為引數傳入並處理
* @param $propertyName
*
* @return int
*/
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("小明", 60); // 通過Person類例項化的物件,並通過構造方法為屬性賦初值
echo "姓名:" . $Person->name . "<br>"; // 直接訪問私有屬性name,自動呼叫了__get()方法可以間接獲取
echo "年齡:" . $Person->age . "<br>"; // 自動呼叫了__get()方法,根據物件本身的情況會返回不同的值
輸出結果:
姓名:小明
年齡:50
6.__set()
__set()的作用:
__set( $property, $value )方法用來設定私有屬性, 給一個未定義的屬性賦值時,此方法會被觸發,傳遞的引數是被設定的屬性名和值。
在給不可訪問屬性賦值時,__set() 會被呼叫。
示例:
<?php
class Person
{
private $name;
private $age;
public function __construct($name="", $age=25)
{
$this->name = $name;
$this->age = $age;
}
/**
* 宣告魔術方法需要兩個引數,直接為私有屬性賦值時自動呼叫,並可以遮蔽一些非法賦值
* @param $property:指要操作的變數名稱
* @param $value:指定了 $property 變數的值
*/
public function __set($property, $value) {
if ($property=="age")
{
if ($value > 150 || $value < 0) {
return;
}
}
$this->$property = $value;
}
public function say(){
echo "我叫".$this->name.",今年".$this->age."歲了";
}
}
$Person=new Person("小明", 35); //注意,初始值將被下面所改變
//自動呼叫了__set()函式,將屬性名name傳給第一個引數,將屬性值”李四”傳給第二個引數
$Person->name = "小紅"; //賦值成功。如果沒有__set(),則出錯。
//自動呼叫了__set()函式,將屬性名age傳給第一個引數,將屬性值26傳給第二個引數
$Person->age = 16; //賦值成功
$Person->age = 160; //160是一個非法值,賦值失效
$Person->say(); //輸出:我叫小紅,今年16歲了
輸出結果:
我叫小紅,今年16歲了
7.__isset()
在看這個方法之前我們看一下isset()
函式的應用,isset()
是測定變數是否設定用的函式,傳入一個變數
作為引數,如果傳入的變數存在則傳回"true"或"1",否則傳回"false"或""(空)。
那麼如果在一個物件外面使用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='男')
{
$this->name = $name;
$this->age = $age;
$this->sex = $sex;
}
/**
* @param $content
*
* @return bool
*/
public function __isset($content) {
echo "當在類外部使用isset()函式測定私有成員{$content}時,自動呼叫\n";
echo isset($this->$content);
}
}
$person = new Person("小明", 25); // 初始賦值
echo isset($person->sex),"\n";
echo isset($person->name),"\n";
echo isset($person->age),"\n";
echo isset($person->email),"\n"; //訪問類中不存在的變數時返回""空
輸出結果:
1
當在類外部使用isset()函式測定私有成員name時,自動呼叫
1
當在類外部使用isset()函式測定私有成員age時,自動呼叫
1
當在類外部使用isset()函式測定私有成員email時,自動呼叫
//第7行返回為空,有資料
8.__unset()
看這個方法之前呢,我們也先來看一下 unset()
函式,unset()
這個函式的作用是刪除指定的變數且傳回
true,引數為要刪除的變數。
那麼如果在一個物件外部去刪除物件內部的成員屬性用unset()
函式可以嗎?
這裡自然也是分兩種情況:
1、 如果一個物件裡面的成員屬性是公有的,就可以使用這個函式在物件外面刪除物件的公有屬性。
2、 如果物件的成員屬性是私有的,我使用這個函式就沒有許可權去刪除。
雖然有以上兩種情況,但我想說的是同樣如果你在一個物件裡面加上__unset()
這個方法,就可以在物件的
外部去刪除物件的私有成員屬性了。在物件裡面加上了__unset()
這個方法之後,在物件外部使
用“unset()”函式刪除物件內部的私有成員屬性時,物件會自動呼叫__unset()
函式來幫我們刪除物件內部的
私有成員屬性。
示例:
<?php
class Person
{
public $sex;
private $name;
private $age;
public function __construct($name="", $age=25, $sex='男')
{
$this->name = $name;
$this->age = $age;
$this->sex = $sex;
}
/**
* @param $content
*
* @return bool
*/
public function __unset($content) {
echo "當在類外部使用unset()函式來刪除私有成員{$content}時自動呼叫的\n";
echo isset($this->$content);
}
}
$person = new Person("小明", 28); // 初始賦值
unset($person->sex);
unset($person->name);
echo "\n";
unset($person->age);
輸出結果:
當在類外部使用unset()函式來刪除私有成員name時自動呼叫的
1
當在類外部使用unset()函式來刪除私有成員age時自動呼叫的
1
9.__sleep()
serialize()
函式會檢查類中是否存在一個魔術方法 __sleep()
。如果存在,則該方法會優先被呼叫,然
後才執行序列化操作。
此功能可以用於清理物件,並返回一個包含物件中所有應被序列化的變數名稱的陣列。
如果該方法未返回任何內容,則 NULL 被序列化,併產生一個 E_NOTICE 級別的錯誤。
注意:
__sleep() 不能返回父類的私有成員的名字。這樣做會產生一個 E_NOTICE 級別的錯誤。可以用 Serializable 介面來替代。
作用:
__sleep() 方法常用於提交未提交的資料,或類似的清理操作。同時,如果有一些很大的物件,但不需要全部儲存,這個功能就很好用。
示例:
<?php
class Person
{
public $sex;
public $name;
public $age;
public function __construct($name="", $age=25, $sex='男')
{
$this->name = $name;
$this->age = $age;
$this->sex = $sex;
}
/**
* @return array
*/
public function __sleep() {
echo "當在類外部使用serialize()時會呼叫這裡的__sleep()方法\n";
$this->name = base64_encode($this->name);
return array('name', 'age'); // 這裡必須返回一個數值,裡邊的元素表示返回的屬性名稱
}
}
$person = new Person('小明'); // 初始賦值
echo serialize($person);
輸出結果:
當在類外部使用serialize()時會呼叫這裡的__sleep()方法
O:6:"Person":2:{s:4:"name";s:8:"5bCP5piO";s:3:"age";i:25;}
//"5bCP5piO"是小明經過base64加密過後的密文
php反序列化的內容可以參考一下連結:
https://www.cnblogs.com/tzf1/p/14991057.html
10.__wakeup()
如果說 __sleep()
是白的,那麼 __wakeup()
就是黑的了。
那麼為什麼呢?
與之相反,unserialize()
會檢查是否存在一個 __wakeup()
方法。如果存在,則會先呼叫 __wakeup
方
法,預先準備物件需要的資源。
作用:
__wakeup() 經常用在反序列化操作中,例如重新建立資料庫連線,或執行其它初始化操作。
示例:
<?php
class Person
{
public $sex;
public $name;
public $age;
public function __construct($name="", $age=25, $sex='男')
{
$this->name = $name;
$this->age = $age;
$this->sex = $sex;
}
/**
* @return array
*/
public function __sleep() {
echo "當在類外部使用serialize()時會呼叫這裡的__sleep()方法\n";
$this->name = base64_encode($this->name);
return array('name', 'age'); // 這裡必須返回一個數值,裡邊的元素表示返回的屬性名稱
}
/**
* __wakeup
*/
public function __wakeup() {
echo "當在類外部使用unserialize()時會呼叫這裡的__wakeup()方法\n";
$this->name = 2;
$this->sex = '男';
// 這裡不需要返回陣列
}
}
$person = new Person('小明'); // 初始賦值
$b=serialize($person);
var_dump($b);
var_dump(unserialize($b));
輸出結果:
當在類外部使用serialize()時會呼叫這裡的__sleep()方法
string(58) "O:6:"Person":2:{s:4:"name";s:8:"5bCP5piO";s:3:"age";i:25;}"
當在類外部使用unserialize()時會呼叫這裡的__wakeup()方法
object(Person)#2 (3) {
["sex"]=>
string(3) "男"
["name"]=>
int(2)
["age"]=>
int(25)
}
//這裡我們可以看到外面通過__sleep()將name改為"5bCP5piO",又通過__wakeup()改成了2
11.__toString()
作用:
__toString() 方法用於一個類被當成字串時應怎樣迴應。例如
echo $obj;
應該顯示些什麼。注意:
此方法必須返回一個字串,否則將發出一條
E_RECOVERABLE_ERROR
級別的致命錯誤。警告:
不能在 __toString() 方法中丟擲異常。這麼做會導致致命錯誤。
示例:
<?php
class Person
{
public $sex;
public $name;
public $age;
public function __construct($name="", $age=25, $sex='男')
{
$this->name = $name;
$this->age = $age;
$this->sex = $sex;
}
public function __toString()
{
return 'go go go';
}
}
$person = new Person('小明'); // 初始賦值
echo $person;
輸出結果:
go go go
那麼如果類中沒有 __toString() 這個魔術方法執行會發生什麼呢?讓我們來測試下:
Fatal error: Uncaught Error: Object of class Person could not be converted to string in /box/script.php:17
Stack trace:
#0 {main}
thrown in /box/script.php on line 17
Exited with error status 255
輸出結果:
Fatal error: Uncaught Error: Object of class Person could not be converted to string in /box/script.php:17
Stack trace:
#0 {main}
thrown in /box/script.php on line 17
Exited with error status 255
很明顯,頁面報了一個致命錯誤,這是語法所不允許的。
12.__invoke()
作用:
當嘗試以呼叫函式的方式呼叫一個物件時,__invoke() 方法會被自動呼叫。
注意:
本特性只在 PHP 5.3.0 及以上版本有效。
示例:
<?php
class Person
{
public $sex;
public $name;
public $age;
public function __construct($name="", $age=25, $sex='男')
{
$this->name = $name;
$this->age = $age;
$this->sex = $sex;
}
public function __invoke() {
echo '這可是一個物件哦';
}
}
$person = new Person('小明'); // 初始賦值
$person();
輸出結果:
這可是一個物件哦
當然,如果你執意要將物件當函式方法使用,那麼會得到下面結果:
Fatal error: Uncaught Error: Function name must be a string in /box/script.php:18
Stack trace:
#0 {main}
thrown in /box/script.php on line 18
Exited with error status 255
很明顯,會報錯。
13.__set_state()
作用
自 PHP 5.1.0 起,當呼叫 var_export() 匯出類時,此靜態方法會被自動呼叫。
引數
本方法的唯一引數是一個數組,其中包含按 array('property' => value, ...) 格式排列的類屬性。
下面我們先來看看在沒有加 __set_state() 情況按下,程式碼及執行結果如何:
程式碼:
<?php
class Person
{
public $sex;
public $name;
public $age;
public function __construct($name="", $age=25, $sex='男')
{
$this->name = $name;
$this->age = $age;
$this->sex = $sex;
}
}
$person = new Person('小明'); // 初始賦值
var_export($person);
輸出結果:
Person::__set_state(array(
'sex' => '男',
'name' => '小明',
'age' => 25,
))
很明顯,將物件中的屬性都打印出來了
加了 __set_state() 之後:
程式碼:
<?php
class Person
{
public $sex;
public $name;
public $age;
public function __construct($name="", $age=25, $sex='男')
{
$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('小明'); // 初始賦值
$person->name = '小紅';
var_export($person);
輸出結果:
Person::__set_state(array(
'sex' => '男',
'name' => '小紅',
'age' => 25,
))
14.__clone()
在多數情況下,我們並不需要完全複製一個物件來獲得其中屬性。但有一個情況下確實需要:如果你有一個
GTK 視窗物件,該物件持有視窗相關的資源。你可能會想複製一個新的視窗,保持所有屬性與原來的視窗相
同,但必須是一個新的物件(因為如果不是新的物件,那麼一個視窗中的改變就會影響到另一個視窗)。還有
一種情況:如果物件 A 中儲存著物件 B 的引用,當你複製物件 A 時,你想其中使用的物件不再是物件 B 而是
B 的一個副本,那麼你必須得到物件 A 的一個副本。
作用:
物件複製可以通過 clone 關鍵字來完成(如果可能,這將呼叫物件的 __clone() 方法)。物件中的 __clone() 方法不能被直接呼叫。
當物件複製完成時,__clone()會被呼叫
語法:
$copy_of_object = clone $object;
注意:
當物件被複制後,PHP 5 會對物件的所有屬性執行一個淺複製(shallow copy)。所有的引用屬性 仍然會是一個指向原來的變數的引用。
當複製完成時,如果定義了 __clone() 方法,則新建立的物件(複製生成的物件)中的 __clone() 方法會被呼叫,可用於修改屬性的值(如果有必要的話)。
示例:
<?php
class Person
{
public $sex;
public $name;
public $age;
public function __construct($name="", $age=25, $sex='男')
{
$this->name = $name;
$this->age = $age;
$this->sex = $sex;
}
public function __clone()
{
echo __METHOD__."你正在克隆物件\n";
}
}
$person = new Person('小明'); // 初始賦值
$person2 = clone $person;
//這裡克隆完成時,會自動呼叫__clone()函式
var_dump('persion1:');
var_dump($person);
echo "\n";
var_dump('persion2:');
var_dump($person2);
輸出結果:
Person::__clone你正在克隆物件
string(9) "persion1:"
object(Person)#1 (3) {
["sex"]=>
string(3) "男"
["name"]=>
string(6) "小明"
["age"]=>
int(25)
}
string(9) "persion2:"
object(Person)#2 (3) {
["sex"]=>
string(3) "男"
["name"]=>
string(6) "小明"
["age"]=>
int(25)
}
克隆成功
15.__debuginfo()
注意
該方法在PHP 5.6.0及其以上版本才可以用,如果你發現使用無效或者報錯,請檢視啊你的版本。
呼叫
__debuginfo(),當轉儲物件以獲取應顯示的屬性時,會被呼叫
示例:
<?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(84)
}
16.__autoload()
作用
你可以通過定義這個函式來啟用類的自動載入。
在魔術函式 __autoload()
方法出現以前,如果你要在一個程式檔案中例項化100個物件,那麼你必須用
include或者require包含進來100個類檔案,或者你把這100個類定義在同一個類檔案中 —— 相信這個檔案一
定會非常大,然後你就痛苦了。
但是有了 __autoload()
方法,以後就不必為此大傷腦筋了,這個類會在你例項化物件之前自動載入制定的
檔案。
示例:
我們先看看以往的方式:
/**
* 檔案non_autoload.php
*/
require_once('project/class/A.php');
require_once('project/class/B.php');
require_once('project/class/C.php');
if (條件A) {
$a = new A();
$b = new B();
$c = new C();
// … 業務邏輯
} else if (條件B) {
$a = newA();
$b = new B();
// … 業務邏輯
}
看到了嗎?不用100個,只是3個看起來就有點煩了。而且這樣就會有一個問題:如果指令碼執行“條件B”這個分支時,C.php這個檔案其實沒有必要包含。因為,任何一個被包含的檔案,無論是否使用,均會被php引擎編譯。如果不使用,卻被編譯,這樣可以被視作一種資源浪費。更進一步,如果C.php包含了D.php,D.php包含了E.php。並且大部分情況都執行“條件B”分支,那麼就會浪費一部分資源去編譯C.php,D.php,E.php三個“無用”的檔案
那麼如果使用 __autoload()
方式呢?
/**
* 檔案autoload_demo.php
*/
function __autoload($className) {
$filePath = “project/class/{$className}.php”;
if (is_readable($filePath)) {
require($filePath);
}
}
if (條件A) {
$a = new A();
$b = new B();
$c = new C();
// … 業務邏輯
} else if (條件B) {
$a = newA();
$b = new B();
// … 業務邏輯
}
ok,不論效率怎麼用,最起碼介面看起來舒服多了,沒有太多冗餘的代。
再來看看這裡的效率如何,我們分析下:
當php引擎第一次使用類A,但是找不到時,會自動呼叫 __autoload
方法,並將類名“A”作為引數傳入。所以,我們在 __autoload()
中需要的做的就是根據類名,找到相應的檔案,幷包含進來,如果我們的方法也找不到,那麼php引擎就會報錯了。
注意:
這裡可以只用require,因為一旦包含進來後,php引擎再遇到類A時,將不會呼叫__autoload,而是直接使用記憶體中的類A,不會導致多次包含。
擴充套件:
其實php發展到今天,已經有將 spl_autoload_register — 註冊給定的函式作為 __autoload 的實現了,但是這個不在啊本文講解之內,有興趣可以自行看手冊。
17.__serialize()
serialize() 函式會檢查類中是否存在一個魔術方法 __serialize()。如果存在,該方法將在任何序列化之前優先
執行。它必須以一個代表物件序列化形式的 鍵/值 成對的關聯陣列形式來返回,如果沒有返回陣列,將會丟擲
一個 TypeError 錯誤。
注意:
如果類中同時定義了 __serialize() 和 __sleep() 兩個魔術方法,則只有 __serialize() 方法會被呼叫。 __sleep() 方法會被忽略掉。如果物件實現了 Serializable 介面,介面的 serialize() 方法會被忽略,做為代替類中的 __serialize() 方法會被呼叫。
此特性自 PHP 7.4.0 起可用。
示例:
<?php
class Person
{
public $sex;
public $name;
public $age;
public function __construct($name="", $age=25, $sex='男')
{
$this->name = $name;
$this->age = $age;
$this->sex = $sex;
}
/**
* @return array
*/
public function __sleep() {
echo "當在類外部使用serialize()時會呼叫這裡的__sleep()方法\n";
$this->name = base64_encode($this->name);
return array('name', 'age'); // 這裡必須返回一個數值,裡邊的元素表示返回的屬性名稱
}
public function __serialize()
{
return [
'name' => $this->name,
'age' => $this->age,
'sex' => $this->sex,
];
}
}
$person = new Person('小明'); // 初始賦值
echo serialize($person);
輸出結果:
O:6:"Person":3:{s:4:"name";s:6:"小明";s:3:"age";i:25;s:3:"sex";s:3:"男";}
//我們發現__sleep()函式沒有呼叫,沒有將"小明"經過base64加密
18.__unserialize()
serialize() 函式會檢查類中是否存在一個魔術方法 __serialize()。如果存在,該方法將在任何序列化之前優先
執行。它必須以一個代表物件序列化形式的 鍵/值 成對的關聯陣列形式來返回,如果沒有返回陣列,將會丟擲
一個 TypeError 錯誤。
注意:
如果類中同時定義了 __unserialize() 和 __wakeup() 兩個魔術方法,則只有 __unserialize() 方法會生效,__wakeup() 方法會被忽略。
此特性自 PHP 7.4.0 起可用。
示例:
<?php
class Person
{
public $sex;
public $name;
public $age;
public function __construct($name="", $age=25, $sex='男')
{
$this->name = $name;
$this->age = $age;
$this->sex = $sex;
}
/**
* __wakeup
*/
public function __wakeup() {
echo "當在類外部使用unserialize()時會呼叫這裡的__wakeup()方法\n";
$this->name = 2;
$this->sex = '男';
// 這裡不需要返回陣列
}
public function __unserialize(array $b)
{
$this->name = '小李';
$this->age = 20;
$this->sex = '女';
}
}
$person = new Person('小明'); // 初始賦值
$b=serialize($person);
var_dump(unserialize($b));
輸出結果:
object(Person)#2 (3) {
["sex"]=>
string(3) "女"
["name"]=>
string(6) "小李"
["age"]=>
int(20)
}
我們發現__wakeup()函式並沒有呼叫,直接呼叫了__serialize()函式。
總結:
以上就是PHP中我瞭解到的魔術方法了,常用的包括 __set()
__get()
__autoload
() 等應該熟悉。這些都是自己參考網上文件以及網上資料整理而成,希望對大家有所幫助!
php官網文件:https://www.php.net/manual/zh/language.oop5.php
最近感覺自己太菜了!!!!