一文了解“最好程式語言”PHP 必知的 16 個程式設計法則!
點選進入“PHP開源社群”
免費獲取進階面試、文件、影片資源
PHP是最好的程式語言。對於PHP開發者來說,掌握一些程式設計法則是十分重要的。而在PHP中,以雙下劃線(__)開頭的方法稱為魔術方法,它們扮演著非常重要的角色。
常用的魔術方法包括:
-__construct():類的構造方法; -__destruct():類的析構方法; -__call($funName, $arguments):當訪問未定義或沒有訪問許可權的方法時,__call()會被呼叫; -__callStatic($funName, $arguments):當訪問未定義或沒有訪問許可權的靜態方法時,__call()會被呼叫; -__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():像呼叫函式一樣呼叫物件時,物件的__invoke()會被呼叫; -__set_state($an_array):呼叫var_export()時__set_state()會被呼叫; -__clone():複製物件時__clone()會被呼叫; -__autoload($className):試圖載入未定義的類; -__debuginfo():輸出除錯資訊。
本文將通過具體點的例子說明,這些PHP魔術方法的用法。
1. __construct()
PHP構造方法是物件建立之後自動呼叫的第一個方法。任何類都有構造方法。如果沒有顯式定義,那麼類會有個預設的構造方法,該方法沒有引數,方法體為空。
1) 構造方法的用法
建構函式通常用來執行初始化工作,如在建立物件時設定成員變數的初始值。
2) 宣告類的構造方法的格式
function __constrct([引數列表]){
方法體 // 通常用於設定成員變數的初始值
}
注意:同一個類只能有一個構造方法,因為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
使用引數"James"建立物件$Person2。
$Person2 = new Person("Jams");
echo $Person2->say(); // 輸出: Name: Jams, Sex: Male, Age: 22
使用三個引數建立$Person3。
$Person3 = new Person ("Jack", "Male", 25);
echo $Person3->say(); // 輸出: Name: Jack, Sex: Male, Age: 25
2. __destruct()
現在我們知道了構造方法,那麼相對的就是析構方法。
析構方法可以在物件銷燬之前執行一些操作,如關閉檔案、清空結果集,等等。
析構方法是PHP5引入的新特性。
析構方法的宣告格式與構造方法 __construct() 類似,就是說__destruct()也以雙下劃線開頭,其名稱也是固定的。
1) 析構方法的宣告格式
function __destruct()
{
// 方法體
}
注意:析構方法不能帶任何引數。
2) 析構方法的用法
一般來說,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;
}
/**
* 定義析構方法
*/
public function __destruct()
{
echo "Well, my name is ".$this->name;
}
}
$Person = new Person("John");
unset($Person); // 銷燬上面建立的$Person物件
以上程式的輸出結果為:
Well, my name is John
3. __call()
該方法有兩個引數。第一個引數$function_name自動接收未定義方法的名稱,第二個引數$arguments以陣列的方式接收該方法呼叫的多個引數。
1) __call()方法的用法
function __call(string $function_name, array $arguments)
{
// 方法體
}
程式中呼叫未定義的方法時,__call()方法會自動被呼叫。
示例如下:
<?php
class Person
{
function say()
{
echo "Hello, world!<br>";
}
function __call($funName, $arguments)
{
echo "The function you called:" . $funName . "(parameter:" ; // 輸出不存在的方法的名稱
print_r($arguments); // 輸出不存在的方法的引數列表
echo ")does not exist!!<br>\n";
}
}
$Person = new Person();
$Person->run("teacher"); // 如果物件內不存在的方法被呼叫,則 __call() 方法會被自動呼叫
$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!
4. __callStatic()
當程式中呼叫未定義的靜態方法時,__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!
5. __get()
當試圖訪問外部物件的私有屬性時,程式會丟擲異常並結束執行。但我們可以使用__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); // 用Person類初始化物件,並通過構造方法給屬性賦初始值
echo "Name:" . $Person->name . "<br>"; // 訪問私有屬性時, __get() 方法會自動被呼叫,這樣就能間接取得屬性值
echo "Age:" . $Person->age . "<br>"; // __get() 方法自動被呼叫,並返回不同的值
輸出結果如下:
Name: John
Age: 50
6. __set()
__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); // 注意下面的程式碼會改變初始值
$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
7. __isset()
在介紹__isset()方法之前,我先介紹下issset()方法。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); // 賦初始值
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
8. __unset()
與__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); // 賦初始值
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
9. __sleep()
serialize()方法會檢查類中是否存在__sleep()魔術方法。如果存在,就會呼叫該方法來執行序列化操作。
__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'); // 返回值中的元素必須是屬性的名稱
}
}
$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;}
10. __wakeup()
與__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'); // 返回值中的元素必須是屬性的名稱
}
/**
* __wakeup
*/
public function __wakeup() {
echo "It is called when the unserialize() method is called outside the class.<br>";
$this->name = 2;
$this->sex = 'Male';
// 這裡不需要返回陣列
}
}
$person = new Person('John'); // 賦初始值
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) }
11. __toString()
使用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'); // 賦初始值
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'); // 賦初始值
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
可見,它會在頁面上報告致命錯誤,說明這種用法不允許。
12. __invoke()
當試圖用呼叫函式的方式呼叫物件時,就會自動呼叫其__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'); // 賦初始值
$person();
輸出結果如下:
This is an object
如果在未定義__invoke()方法的情況下將物件作為函式使用,就會得到以下的結果:
Fatal error: Function name must be a string in D:\phpStudy\WWW\test\index.php on line 18
13. __set_state()
從PHP 5.1.0開始,__set_state()方法會在呼叫var_export()匯出類程式碼時自動被呼叫。
__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'); // 賦初始值
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'); // 賦初始值
$person->name = 'Jams';
var_export($person);
輸出結果如下:
Person::__set_state(array( 'sex' => 'Male', 'name' => 'Jams', 'age' => 25, ))
14. __clone()
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'); // 賦初始值
$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) }
15. __autoload()
__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()方法呢?
/**
* 檔案 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引擎就會丟擲異常。
16. __debugInfo()
執行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及更高版本。
總結
上面介紹了16個PHP魔術方法,其中最常用的有__set()、__get()和__autoload()。如果你還有問題,可以從PHP官方網站上獲得幫助。
如果你年滿18週歲以上,又覺得學【PHP】太難?想嘗試其他程式語言,那麼我推薦你學Python,現有價值499元Python零基礎課程限時免費領取,限10個名額!
▲ 掃描二維碼-免費領取
點選“檢視原文”獲取更多
- 用“最好的語言”PHP,做一個機器學習資料集
- PHP開發中,如何做錯誤與異常處理呢 ?
- phper如何用Rust開發PHP擴充套件Liunx版【詳細教程】
- PHP 使用 CURL 詳解
- 小程式如何使用訂閱訊息(PHP程式碼 小程式js程式碼)
- MySQL提高效能,緩解資料庫壓力,你會做讀寫分離嗎?
- 深入理解 glibc malloc:記憶體分配器實現原理
- Laravel 成為最佳 PHP 框架的 14 個理由!
- 7 款顏值 yyds 的 Linux 作業系統 !
- 一文了解“最好程式語言”PHP 必知的 16 個程式設計法則!
- 終於有人把 "單點" 登入說清楚了!
- PHP程式執行Python指令碼(接收資料及傳參)
- 服務端 TCP 連線 TIME_WAIT 怎麼破?
- Shell 分析日誌檔案命令全面總結!
- 有些PHP程式設計師,不知道如何有效的除錯BUG
- 10個你可能不曾用過卻很有用的 Linux 命令
- PHP如何解決百萬級全站使用者訊息推送問題
- PHP Redis快取技術一覽
- Seata-php 入門與下半年展望
- 3 個PHP 知識總結:Memcache、快取和正則