一、PHP類與對象
1. 類
- 概念理解: 類是共享相同結構和行為的對象的集合,可以理解為特征的提取。例如將耳朵長、尾巴短、紅眼睛、吃胡蘿卜、蹦跳行走的動物特征抽象為"兔子"類。
- 代碼結構:
- 使用class關鍵字定義類
- 類名遵循大駝峰命名法
- 包含成員變量(屬性)和方法(行為)
- 成員組成:
- 屬性: 描述對象的特征,如$var1、$var2
- 方法: 定義對象的行為,如myfunc()函數
- 抽象特性: 類是特征的抽象集合,不是具體實例
2. 對象
- 實例關系: 對象是類的具體實例,如"兔子類"的具體實例可以是"小白兔"
- 創建方式: 使用new關鍵字實例化對象
- 實際案例:
- $baidu = new Site; 創建網站類的百度實例
- $kitty = new Cat; 創建貓類的Kitty實例
- $benz = new Car; 創建汽車類的奔馳實例
- 面向對象原則: PHP遵循"一切皆對象"的面向對象編程思想
二、代碼實例
- 類定義:
- 實例化過程:
- $obj = new ClassDemo(); 創建類實例
- $obj->echoString(); 調用對象方法
- 執行結果: 輸出成員變量$var的字符串值
- 關鍵語法:
- public 訪問修飾符
- $this 指代當前對象實例
- -> 對象操作符
三、知識小結
知識點 | 核心內容 | 考試重點/易混淆點 | 難度系數 |
PHP類與對象 | 類為共享結構和行為的對象集合(如Rabbit類抽象兔子特征);對象是類的實例(如new創建Baidu網站實例) | 類(抽象)vs對象(具體實例) | ?? |
PHP序列化與反序列化 | 序列化函數使用、數據格式轉換意義 | serialize()與unserialize()的安全風險 | ??? |
反序列化漏洞原理 | 惡意數據觸發對象注入,破壞程序邏輯 | 魔術方法(如__wakeup())的利用 | ???? |
CTF題目實戰分析 | 外部網站漏洞挖掘與利用方法 | 輸入點檢測、POP鏈構造 | ???? |
CMS漏洞案例(tCMS) | 實際漏洞復現與成因分析 | 未過濾用戶輸入導致對象注入 | ???? |
漏洞修復與防御 | 輸入驗證、禁用危險函數、使用安全庫 | __wakeup()方法的安全實現 | ??? |
一、PHP的magic函數
1. 函數作用
- 定義:魔術方法是以雙下劃線(__)開頭的特殊方法,當對對象執行特定操作時會覆蓋PHP的默認行為
- 數量:PHP中共有17個魔術方法,包括__construct()、__destruct()等
- 命名規范:PHP保留所有以__開頭的方法名稱,不建議自定義此類方法名
- 生命周期方法:
- __construct():對象創建時調用(通過new或反序列化)
- __destruct():對象銷毀時調用(如腳本執行結束)
- 類型轉換方法:
- __toString():對象被當作字符串使用時調用(如echo輸出或字符串拼接)
- 序列化相關:
- __sleep():對象被序列化前調用
- __wakeup():對象被反序列化后調用
- __serialize():PHP7.4新增,替代__sleep()
- __unserialize():PHP7.4新增,替代__wakeup()
- 訪問控制方法:
- __call():調用不可訪問方法時觸發
- __callStatic():靜態調用不可訪問方法時觸發
- __get():讀取不可訪問屬性時觸發
- __set():寫入不可訪問屬性時觸發
- __isset():對不可訪問屬性調用isset()或empty()時觸發
- __unset():對不可訪問屬性使用unset()時觸發
- 其他方法:
- __invoke():將對象作為函數調用時觸發
- __clone():對象克隆時調用
- __debugInfo():var_dump()對象時調用
- 訪問限制:除__construct()、__destruct()和__clone()外,所有魔術方法必須聲明為public
- 類型聲明:PHP8.0起,魔術方法的類型聲明必須與文檔描述一致
2. 例題:magic函數案例使用
- 實現原理:
- 自動觸發:魔術方法無需顯式調用,滿足條件時自動執行
- 生命周期控制:通過預定義方法實現對對象生命周期的統一管理
- 代碼復用:避免在每個對象中重復編寫相同邏輯
- 設計目的:
- 統一控制點:提供對象生命周期關鍵節點的標準控制接口
- 減少重復代碼:集中處理對象創建、使用和銷毀的通用邏輯
- 擴展性:允許開發者在不修改核心代碼的情況下擴展對象行為
- 使用場景:
- 資源管理:在__construct中初始化資源,在__destruct中釋放資源
- 調試跟蹤:通過魔術方法記錄對象操作日志
- 動態屬性:使用__get/__set實現動態屬性訪問
- 方法重載:通過__call實現方法重載功能
- 注意事項:
- 性能影響:魔術方法調用會增加額外開銷
- 命名沖突:避免自定義__開頭的方法名
- 版本兼容:注意不同PHP版本對魔術方法的支持差異
二、知識小結
知識點 | 核心內容 | 關鍵特性/易混淆點 | 應用場景 |
__construct() | 對象創建時自動調用 | 通過new或反序列化觸發 | 對象初始化、資源分配 |
__destruct() | 對象銷毀時自動調用 | 程序結束或對象顯式銷毀時觸發 | 資源釋放、清理操作 |
__toString() | 對象被當作字符串使用時調用 | echo輸出或字符串拼接時觸發 | 對象字符串化表示 |
__sleep() | 對象序列化前執行 | 與serialize()相關 | 選擇性序列化屬性 |
__wakeup() | 對象反序列化后執行 | 與unserialize()相關 | 反序列化后初始化 |
__call() | 訪問不可訪問方法時觸發 | 包含方法名和參數數組 | 動態方法處理 |
__callStatic() | 靜態上下文訪問不可訪問方法時觸發 | 靜態方法調用攔截 | 靜態方法重載 |
__get() | 讀取不可訪問屬性時觸發 | 屬性訪問攔截 | 動態屬性處理 |
__set() | 寫入不可訪問屬性時觸發 | 屬性賦值攔截 | 屬性訪問控制 |
__isset() | 對不可訪問屬性調用isset()或empty()時觸發 | 屬性存在性檢查攔截 | 動態屬性驗證 |
__unset() | 對不可訪問屬性調用unset()時觸發 | 屬性刪除攔截 | 屬性刪除控制 |
__invoke() | 對象被當作函數調用時觸發 | 使對象可調用 | 函數式編程接口 |
__clone() | 對象克隆時觸發 | 通過clone關鍵字調用 | 深拷貝控制 |
魔術方法特點 | 自動觸發機制 | 以雙下劃線__開頭 | 對象生命周期控制點 |
設計目的 | 統一對象行為控制 | 減少重復代碼 | 提供預置控制點 |
一、PHP序列化和反序列化
1. 序列化
1)序列化與反序列化概述
- 序列化: 將對象轉換成一個可存儲的字符串,便于存儲或傳遞PHP的值,同時不丟失其類型和結構。
- 反序列化: 將已序列化的字符串變回PHP的值。
- 魔術方法:
- _serialize(): 在序列化之前執行,返回一個代表對象序列化形式的關聯數組。
- _unserialize(): 在反序列化時執行,用于恢復對象的屬性。
- 注意:
- 如果類中同時定義了_serialize()和_sleep(),則只有_serialize()會被調用。
- 如果對象實現了Serializable接口,則接口的serialize()方法會被忽略,類中的serialize()方法會被調用。
2)serialize() 函數
- 功能: 產生一個可存儲的值的表示,返回一個包含表示value的字節流的字符串。
- 處理類型: 可處理除了resource之外的任何類型,包括包含指向其自身引用的數組。
- 對象序列化:
- 在序列化對象時,PHP會嘗試調用對象的_sleep()方法(如果存在_serialize()則忽略_sleep()),允許對象在被序列化之前做清除操作。
- 示例:
number=32;number = 32; number=32;
str = 'wuya';
bool=true;bool = true; bool=true;
null = NULL;
arr=array(′aa′=>1,′bbbb′=>9);arr = array('aa' => 1, 'bbbb' => 9); arr=array(′aa′=>1,′bbbb′=>9);
obj = new SerialType(data: 'somestr', pass: true);
var_dump(serialize(
number));//string(5)"i:32;"vardump(serialize(number)); // string(5) "i:32;" var_dump(serialize(number));//string(5)"i:32;"vard?ump(serialize(
str)); // string(11) "s:4:"wuya";"
var_dump(serialize(
bool));//string(4)"b:1;"vardump(serialize(bool)); // string(4) "b:1;" var_dump(serialize(bool));//string(4)"b:1;"vard?ump(serialize(
null)); // string(2) "N;"
var_dump(serialize(
arr));//string(34)"a:2:s:2:"aa";i:1;s:4:"bbbb";i:9;"vardump(serialize(arr)); // string(34) "a:2:{s:2:"aa";i:1;s:4:"bbbb";i:9;}" var_dump(serialize(arr));//string(34)"a:2:s:2:"aa";i:1;s:4:"bbbb";i:9;"vard?ump(serialize(
obj)); // string(75) "O:10:"SerialType":2:{s:4:"data";s:7:"somestr";s:16:"SerialTypepass";b:1;}"
###### 3)序列化結果解析 - **整數**: 以`i:`開頭,后跟整數值,如`i:32;`。 - **字符串**: 以`s:`開頭,后跟字符串長度和字符串內容,如`s:4:"wuya";`。 - **布爾值**: `b:1;`表示true,`b:0;`表示false。 - **空值**: 用`N;`表示。 - **數組**: 以`a:`開頭,后跟元素個數和元素內容,如`a:2:{s:2:"aa";i:1;s:4:"bbbb";i:9;}`。 - **對象**: 以`O:`開頭,后跟類名長度、類名、元素個數和元素內容,如`O:10:"SerialType":2: - **注意**: - 對象序列化時,私有成員變量名會拼接類名,如`s:16:"SerialTypepass";`。 - 常量不會被序列化。 ###### 4)反序列化 - **功能**: 將序列化的字符串恢復成PHP的值。 - **魔術方法**: - `_unserialize()`: 在反序列化時執行,用于恢復對象的屬性。 - 如果類中同時定義了`_unserialize()`和`_wakeup()`,則只有`_unserialize()`會生效。###### 5)序列化與反序列化的應用 - **存儲**: 可以將序列化后的字符串存儲在文件、數據庫、Redis等存儲系統中。 - **傳輸**: 便于在網絡中傳輸復雜的數據結構。##### 2. 其他序列化格式 <timestamp>574000</timestamp> ###### 1)序列化格式種類 - **json字符串**: 使用$json_encode$方法,常用于數據傳輸,如JAVA框架和消息中間件。 - **xml字符串**: 使用$wddx_serialize_value$方法,早年流行于web service數據交換。 - **二進制格式字節數組**: 也是一種序列化方式。 ###### 2)序列化示例 - **示例對象**: `JsonClass`類,包含字符串成員變量`word`和數組成員變量`prop`。 - **json序列化**: 使用`json_encode`方法,輸出為json字符串。 - **xml序列化**: 使用`wddx_serialize_value`方法,輸出為xml字符串。###### 3)序列化輸出 - **json輸出**: `{"word": "hello wuya", "prop": {"name": "wuya", "age": 31, "motto": "Apple keep doctor"}}` - **xml輸出**: 包含`wddxPacket`、`header`、`data`、`struct`等標簽,詳細描述了對象的結構和內容。###### 4)XML格式化 - **格式化工具**: 可使用在線XML格式化工具查看XML結構,如[BEJSON] ###### 5)序列化與反序列化 - **反序列化**: 使用相應的方法(如`json_decode`、`wddx_deserialize`)可將序列化的字符串還原為對象。###### 6)敏感字段處理 - **敏感字段**: 如密碼等,不需要序列化。 - **處理方法**: 重寫`_sleep`方法,返回一個數組,包含需要序列化的變量名,排除敏感字段。###### 7)_sleep方法示例 - **示例代碼**: `User`類重寫`_sleep`方法,排除`password`字段。 - **序列化結果**: 序列化字符串中不包含`password`字段。###### 8)序列化應用 - **應用場景**: 將對象保存為字符串,存儲在文件、數據庫或通過網絡傳輸,再在需要的地方還原使用。##### 3. 反序列化 <timestamp>851000</timestamp> ###### 1)反序列化例子 <timestamp>891000</timestamp>- **反序列化函數**: 使用 `unserialize()` 函數可以將已序列化的字符串還原成 PHP 的值或對象。 - **示例代碼**: ```php class User {public$username;public$nickname;private$password;public function __construct($username,$nickname,$password) { $this->username =$username; $this->nickname =$nickname; $this->password =$password;} } // 序列化對象 $user = new User("hackerwuya", "wuya", "password123"); $serializedUser = serialize($user); echo$serializedUser; // 反序列化對象 $unserializedUser = unserialize($serializedUser); var_dump($unserializedUser);
- 注意事項:
- 如果反序列化的字符串格式不正確,unserialize() 將返回 FALSE。
- 如果反序列化的對象類不存在,將得到一個 __PHP_Incomplete_Class 對象,并且無法調用未定義類的方法。
- Magic 方法:
- _sleep(): 在對象序列化之前調用,可以用于清理對象。
- _wakeup(): 在對象反序列化之后調用,可以用于重新初始化對象。
- 反序列化后的對象方法調用: 反序列化后的對象可以調用其定義的方法,如 echoString()。
- 篡改序列化字符串: 反序列化過程中可以篡改序列化字符串中的值,但需注意保持格式正確。
- 類不存在時的處理:
- 如果反序列化的類不存在,PHP 將返回一個 __PHP_Incomplete_Class 對象。
- 嘗試調用未定義類的方法將導致致命錯誤。
- 反序列化注意事項:
- 如果傳遞的字符串不可序列化,unserialize() 返回 FALSE。
- 如果對象類未定義,反序列化得到的對象是 __PHP_Incomplete_Class。
- 格式錯誤處理: 如果序列化字符串格式錯誤,unserialize() 將無法正確解析并返回 FALSE。
4. 序列化和反序列化的作用
1)序列化和反序列化的概念
- 序列化: 對象和字符串之間的轉換過程,將對象轉換為字符串,以便進行傳輸或存儲。
- 反序列化: 將序列化后的字符串轉換回對象的過程。
- 注意:
- 如果傳遞的字符串不可以序列化,則返回FALSE。
- 如果對象沒有預定義,反序列化得到的對象是
PHP_Incomplete_ClassPHP\_Incomplete\_ClassPHP_Incomplete_Class
。
2)序列化和反序列化的作用
- 傳輸對象: 序列化可以將對象轉換為字符串,方便在網絡中傳輸或存儲到文件中,然后在需要的地方進行反序列化,恢復成對象。
- 用作緩存: 序列化后的對象可以存儲到Cookie或Session中,作為緩存數據。在需要時,通過反序列化可以快速恢復對象狀態。
- 配合magic方法: 在反序列化時,可以自動觸發magic方法(如__wakeup()),進行一些初始化或清理工作。
3)序列化和反序列化與magic函數的關聯
- magic函數: 在PHP中,magic函數是指一些以雙下劃線(__)開頭的方法,它們在特定情況下會自動被調用。
- 關聯: 反序列化過程中,如果對象類中定義了__wakeup()等magic方法,這些方法會在反序列化時自動被調用,可以用于執行一些初始化操作或恢復對象狀態。
5. 反序列化與Magic函數
1)序列化和反序列化例題
- _unserialize()與_wakeup()的關系
- 關系: 如果類中同時定義了_unserialize()和_wakeup()兩個魔術方法,則只有_unserialize()方法會生效,_wakeup()方法會被忽略。
- 版本: 此特性自PHP7.4.0起可用。
- _serialize()與_sleep()的關系
- 關系: 如果類中同時定義了_serialize()和_sleep()兩個魔術方法,則只有_serialize()方法會被調用,_sleep()方法會被忽略。
- 接口: 如果對象實現了Serializable接口,接口的serialize()方法會被忽略,作為代替類中的_serialize()方法會被調用。
- _unserialize()的用途
- 用途: _unserialize()用于定義對象序列化友好的任意表示,數組的元素可能對應對象的屬性,但這并不是必須的。
- 觸發: 在反序列化時,如果存在_unserialize()魔術方法,則會自動調用此方法,并傳遞從_serialize()返回的恢復數組。
- 示例代碼
- 代碼示例:
this?>dsn=this->dsn =this?>dsn=
dsn;
this?>username=this->username =this?>username=
username;
this?>password=this->password =this?>password=
password;
this->connect(); } public function __serialize() { echo "_serialize\n"; } public function __unserialize(
data) {
echo "_unserialize\n";
}
public function __wakeup() {
echo "_wakeup\n";
}
}
- **測試**: 通過創建對象并調用unserialize()方法,觀察_serialize()、_unserialize()和_wakeup()的觸發情況。 ###### 2)版本影響與測試 - 版本差異 - **版本差異**: _unserialize()方法自PHP7.4.0起可用,在低于此版本的PHP中,反序列化時會調用_wakeup()方法。- **測試方法**: 通過在不同PHP版本下運行相同的測試代碼,觀察_unserialize()和_wakeup()的調用情況。 - 測試示例 - **測試代碼**: ```php $obj1 = new UnSerializeTest(); $obj2 = unserialize('0:15:"UnSerializeTest":1:{s:3:"var";s:15:"hello wuyaziabc";}'); $obj2->echoString();
- 結果分析: 在PHP7.4.0及以上版本,反序列化時會調用_unserialize()方法;在低于此版本的PHP中,會調用_wakeup()方法。
3)PHP版本管理
- PHP Study工具
- 工具介紹: PHP Study是一個本地的集成工具,可以方便地下載和安裝不同版本的PHP。
- 版本管理: 通過在PHP Study中添加不同版本的PHP,可以方便地在不同項目中使用不同版本的PHP進行開發和調試。
- 版本配置
- 配置方法: 在項目的配置中指定使用的PHP版本路徑,即可使用該版本的PHP進行運行和調試。
- 注意事項: 確保指定的PHP版本路徑正確,且該版本的PHP已正確安裝和配置。
二、知識小結
知識點 | 核心內容 | 考試重點/易混淆點 | 難度系數 |
PHP序列化和反序列化 | PHP對象存活于內存時可訪問屬性和方法,消亡后則不可訪問;序列化是將對象轉換成字符串形式以便存儲和傳輸,反序列化是將字符串恢復成對象。 | 序列化和反序列化的概念、方法、應用場景。 | ★★★ |
序列化方法 | 使用serialize()方法將對象轉換成字符串,可存儲于文件、數據庫、Redis等。 | serialize()方法的使用,不同數據類型序列化后的格式。 | ★★★★ |
反序列化方法 | 使用unserialize()方法將字符串恢復成對象。 | unserialize()方法的使用,反序列化后的對象狀態。 | ★★★ |
序列化格式解讀 | 整數、字符串、布爾值、空值、數組、對象的序列化格式。 | 各種數據類型序列化后的字符串格式,特別是對象和數組的復雜格式。 | ★★★★☆ |
序列化注意事項 | 對象序列化時不包括方法和常量,只包括類名、屬性和值。 | 方法和常量不被序列化,私有成員變量序列化時包含類名。 | ★★★ |
其他序列化格式 | JSON、XML、二進制等序列化格式。 | JSON和XML序列化的方法和應用場景。 | ★★ |
防止敏感信息序列化 | 重寫sleep方法,返回需要序列化的屬性數組,排除敏感信息。 | sleep方法的重寫,防止敏感信息被序列化。 | ★★★★ |
反序列化注意事項 | 反序列化時類不存在會報錯,但仍可返回對象;字符串格式錯誤會返回false。 | 類不存在時的反序列化結果,字符串格式錯誤的處理。 | ★★★★ |