本節講解的內容
對象的複製
對象的遍曆
對象的序列化和還原序列化
內建標準類的使用
traits的使用
類和對象的相關函數
PHP反射機制
前言
PHP的物件導向是一個重要的知識點,它的思想貫穿著我們開發的整個流程。在物件導向中還有一些知識點是需要我們去瞭解的,對象複製的特點以及對象的遍曆,對象的序列化和還原序列化,如果你想寫一個PHP的架構,那麼你對PHP的反射也是要掌握的。
對象的複製
當我們建立一個對象後,就會在記憶體中分配一個空間,對象名指向這個空間,前面我們講過對象的賦值,當一個對象把裡面的資料修改了,另一個對象的資料也會跟著變化,因為賦值是相當於把物件識別碼賦值了一份,而複製並不是這樣的。
<?phpclass Person{ public $name; public $age; public $sex; public function __construct($name,$age,$sex){ $this -> name = $name; $this -> age = $age; $this -> sex = $sex; } public function getName(){ return $this -> name; } public function getAge(){ return $this -> age; } public function getSex(){ return $this -> sex; }}$a = new Person('宋江','45','男');echo '<pre>';$b = clone $a;$b -> name = '武松';var_dump($a);var_dump($b);
結果
對象複製的文法:
$新對象 = clone $就對象;
從上面的結果中可以看出來,一個對象的資料變化,並不會影響到另外一個對象的資料。對象複製會產生一個全新的對象。可以理解如下:
在PHP的魔術方法中有一個魔術方法和PHP的複製有關的__clone(),在複製完成時,如果在類裡面定義了魔術方法__clone()方法,則新建立也就是複製產生的對象會調用這個__clone()方法,在這個方法中如果有需要可以對屬性做一些變動。
如果不想一個對象進行複製可以在內部把魔術方法__clone()方法定義成私人的,這時如果在進行複製,會提示
Call to private Peoson::__clone() from context ''
對象的遍曆
在PHP中對象也是可以遍曆的,對象的遍曆可以理解成把某個對象的屬性和值,遍曆顯示出來。遍曆使用到foreach這個迴圈體。基本文法:
foreach(對象名 as $key => $val){ //$key是屬性名稱,$val是屬性值}
<?php class Person{ public $name; public $age; private $sex; public function __construct($name,$age,$sex){ $this -> name = $name; $this -> age = $age; $this -> sex = $sex; }}$person = new Person('孫悟空',256,'男');foreach ($person as $key => $value) { echo $key . ' = ' . $value . '<br>';}.......結果........name = 孫悟空age = 256
對象的遍曆只能把屬性的修飾符是public的遍曆出來,protected和private不能再類外訪問,所以在類外進行對象的遍曆,用這兩個修飾符修飾的屬性是取不出來的,就像上面代碼的sex屬性一樣。
對象的序列化和還原序列化
在PHP中當程式執行完一個檔案,就會自動的釋放記憶體,我們在檔案中建立的對象,變數等,都會消失,如果我們在一個檔案建立了一個對象,在另外一個對象中想要使用這個對象,就可以使用到對象的序列化(serialize())和還原序列化(unserialize())。
對象的序列化和還原序列化可以理解成把對象轉換成字串儲存在一個檔案中,在用到對象時把檔案進行還原序列化得到對象,對象是不能直接儲存在檔案中的。
:
a.php檔案,建立一個對象並儲存:
<?php class Person{ public $name; public $age; private $sex; public function __construct($name,$age,$sex){ $this -> name = $name; $this -> age = $age; $this -> sex = $sex; }}$person = new Person('孫悟空',256,'男');//使用file_put_contents()函數把將一個字串寫入檔案 file_put_contents('D:person.txt', serialize($person));
上面的代碼file_put_contents()函數是把一個字串寫入到一個檔案中。
上面的代碼執行完可以看到在D盤有一個person.txt檔案,裡面是一個轉換成字串的對象。
b.php檔案,使用到a.php檔案中的對象person
<?php class Person{ public $name; public $age; private $sex; public function __construct($name,$age,$sex){ $this -> name = $name; $this -> age = $age; $this -> sex = $sex; } } //通過file_get_contents這個方法把一個檔案讀取到字串。 $str = file_get_contents('D:person.txt'); //進行還原序列化。 $person = unserialize($str); echo '<pre>'; var_dump($person); ......結果...... object(Person)#1 (3) { ["name"]=> string(9) "孫悟空" ["age"]=> int(256) ["sex":"Person":private]=> string(3) "男" }
在b.php中通過file_get_contents()這個方法把一個檔案讀取到字串,然後通過unserialize()還原序列化,但是這樣還原序列化得到的對象不是person對象,所以在檔案中把person類的聲明粘貼複製過來,自動轉換成person對象。
對象的序列化和還原序列化可以讓多個檔案分享權限設定一個對象。
PHP內建標準類
有時我們希望把一些資料,以對象的屬性的方式儲存,同時我們又不想定義一個類,可以考慮使用PHP內建標準類 stdClass,這是用系統提供的一個虛擬類,並不需要我們定義就可以直接使用。
<?php $person = new StdClass(); $person -> name = '孫悟空'; $person -> age = 524; $person -> sex = '男'; echo '<pre>'; var_dump($person); ......結果...... object(stdClass)#1 (3) { ["name"]=> string(9) "孫悟空" ["age"]=> int(524) ["sex"]=> string(3) "男" }
在上面代碼中我們可以看出來,並沒有定義stdClass這個類就能使用。
Traits的使用
Traits 是一種為類似 PHP 的單繼承語言而準備的代碼複用機制。Trait 為了減少單繼承語言的限制,使開發人員能夠自由地在不同階層內獨立的類中複用方法集。可以理解為在traits中定義一段代碼塊,可以在不同的類中使用。
圖解:
traits使用文法:
trait 自訂名稱{ //額外定義的代碼}在類中使用格式use 自訂的名稱;
代碼:
<?php //使用trait,自訂名稱 trait my_code{ public function minus($num1,$num2){ return $num1 - $num2; } } class A{ public function plus($num1,$num2){ return $num1 + $num2; } } class B extends A{ //使用trait定義的代碼塊 use my_code; } class C extends A{ use my_code; } class D extends A{ } $b = new B(); $c = new C(); $d = new D(); echo $b -> plus(1,2); echo '<br>'; echo $b -> minus(2,1); echo '<br>'; echo $d -> plus(1,2); echo '<br>'; echo $d -> minus(2,1); ......結果...... 3 1 3 Fatal error: Call to undefined method D::minus() in D:\mywamp\Apache24\htdocs\zendstudio\yunsuanfu\staits.php on line 36
在上面代碼中類D沒有使用trait代碼,所以在使用minus方法時,出現錯誤。
當我們在trait中寫的函數和父類的函數一樣時,以trait代碼為準,即trait代碼的優先順序高於繼承的類。
類與對象相關函數
在PHP中系統提供了一系列的函數來判斷類和對象的。從協助文檔中可以看到好多函數:
在這裡我們只對裡面的幾個函數進行瞭解。
<?php class Person{ public $name; public $age; private $sex; public function __construct($name,$age,$sex){ $this -> name = $name; $this -> age = $age; $this -> sex = $sex; } public function showInfo(){ echo '名字是:' . $this -> name . ',年齡是:' . $this -> age . ',性別是:' . $this -> sex . '<br>'; } } //判斷是否建立了對象,沒有建立返回true,建立返回false。 if(class_exists('Person')){ $person = new Person('唐僧',25,'男'); //返回對象的類名 echo '類名是: ' . get_class($person) . '<br>'; //判斷方法是否存在。 if(method_exists($person, 'showInfo')){ $person -> showInfo(); }else{ echo '該方法不存在'; } //判斷屬性是否存在 if(property_exists($person,'name')){ echo $person -> name; }else{ echo '屬性不存在'; } }else{ echo '類不存在'; } ......結果...... 類名是: Person 名字是:唐僧,年齡是:25,性別是:男 唐僧
使用類和對象函數,可以保證我們代碼的完整性,對出錯資訊進行及時的捕獲輸出。
PHP反射機制
在很多程式設計語言中都有反射這種概念,反射簡單理解就是通過類,擷取裡面的屬性,方法,甚至注釋也可以,不管屬性和方法的存取修飾詞是什麼類型都可以擷取到。
在PHP 5中具有完整的反射API,添加了對類、介面、函數、方法和擴充進行反向工程的能力。此外,反射 API 提供了方法來取出函數、類和方法中的文檔注釋。而我們在開發中一般是使用不到反射的,但是在某些情況下使用反射可以更好的處理問題,比如我們需要我們寫架構底層,擴充功能,管理大量的未知類。
定義一個類,通過反射建立對象和調用裡面的方法:
<?php class Dog{ public $name; protected $age; private $food; public function __construct($name, $age, $food){ $this->name = $name; $this->age = $age; $this->food = $food; } public function cry($sound){ echo '<br> ' . $this->name . ' 叫聲是.' . $sound; } } //使用反射完成對象的建立和方法調用 //1. 建立一個反射對象 $reflect_obj = new ReflectionClass('Dog'); //2. 通過反射對象建立Dog對象執行個體 $dog = $reflect_obj->newInstance('大黃狗', 4, '排骨'); echo '<pre>'; var_dump($dog); //3. 調用方法-使用代理方式. $reflect_method_cry = $reflect_obj->getMethod('cry'); echo '<pre>'; var_dump($reflect_method_cry); //4. 代理調用cry $reflect_method_cry->invoke($dog, '汪汪');
結果:
在上面代碼中,我們通過new建立了一個反射的對象,在反射對象裡面通過newInstance()方法得到類的對象。擷取裡面的方法可以使用反射對象的getMethod()方法,返回來的是一個方法對象ReflectionMethod類,通過裡面的invoke()方法執行該方法。這裡只是基本的介紹,可以尋找協助文檔瞭解更多的反射對象和方法。
反射實現TP控制器調度
需求:
有一個類IndexAction,其中的方法和存取控制修飾符是不確定的,1. 如果index 方法是public,可以執行 _before_index.2. 如果存在_before_index 方法,並且是public的,執行該方法3. 執行test方法4. 再判斷有沒有_after_index方法,並且是public的,執行該方法
代碼:
<?php class IndexAction{ public function index(){ echo 'index<br>'; } public function _before_index(){ echo '_before_index方法執行 <br>'; } public function test($data){ echo 'data : ' . $data . '<br>'; } public function _after_index(){ echo '_after_index方法執行<br>'; } } if(class_exists('IndexAction')){ //建立對象 $reflectionClass = new ReflectionClass('IndexAction'); //判斷index是否存在 if($reflectionClass -> hasMethod('index')){ //擷取index方法對象 $reflec_index_method = $reflectionClass -> getMethod('index'); //判斷修飾符是否是public if($reflec_index_method -> isPublic()){ //判斷是否有_before_index方法 if($reflectionClass -> hasMethod('_before_index')){ $reflec_before_method = $reflectionClass -> getMethod('_before_index'); //判斷是否是public if($reflec_before_method -> isPublic()){ $reflec_before_method -> invoke($reflectionClass -> newInstance()); //調用test()方法 $reflectionClass -> getMethod('test') -> invoke($reflectionClass -> newInstance(),'這是test的資料'); //判斷是否有_after_index方法 if($reflectionClass -> hasMethod('_after_index')){ $reflec_after_method = $reflectionClass -> getMethod('_after_index'); //判斷是否是public if($reflec_after_method -> isPublic()){ //執行_after_index方法 $reflec_after_method -> invoke($reflectionClass -> newInstance()); }else{ echo '_after_index不是public修飾的'; } }else{ echo '沒有_after_index方法'; } }else{ echo '_before_index修飾符不是public'; } }else{ echo '沒有_before_index方法'; } }else{ echo 'index方法不是public修飾'; } }else{ echo 'index方法不存在'; } }else{ echo '類名不存在'; } ......結果....... _before_index方法執行 data : 這是test的資料 _after_index方法執行
在上面的代碼中可以看到我們不停地在判斷類中有沒有某個方法,是不是public修飾,然後執行,我們可以利用封裝的思想,把一些共性的特徵抽取出來寫成一個函數。從而對我們的代碼進行最佳化。
最佳化的代碼:
<?php class IndexAction{ public function index(){ echo 'index<br>'; } public function _before_index(){ echo '_before_index方法執行 <br>'; } public function test($data){ echo 'data : ' . $data . '<br>'; } public function _after_index(){ echo '_after_index方法執行<br>'; } } if(class_exists('IndexAction')){ $reflectionClass = new ReflectionClass('IndexAction'); //建立IndexAction對象。 $indexAction = $reflectionClass -> newInstance(); execute($reflectionClass,$indexAction,'index'); execute($reflectionClass,$indexAction,'_after_index'); execute($reflectionClass,$indexAction,'test','test使用的資料'); execute($reflectionClass,$indexAction,'_after_index'); }else{ echo '沒有IndexAction方法'; } //封裝的函數 /** * [execute description]對反射的封裝。 * @param [type] $reflect_obj [description]反射對象 * @param [type] $worker [description]類對象 * @param [type] $name [description]方法的名字 * @param [type] $canshu [description]方法的參數 * @return boolean [description] */ function execute($reflect_obj,$indexAction,$name,$param = ''){ if($reflect_obj-> hasMethod($name)){ $method = $reflect_obj->getMethod($name); if($method->isPublic()){ $method->invoke($indexAction,$param); }else{ echo $name . '不是public'; } }else{ echo $name . '方法不存在'; } } ......結果..... index _after_index方法執行 data : test使用的資料 _after_index方法執行
可以看到進行功能的封裝,可以簡化我們的代碼,同時代碼看起來更加的清晰。
總結
PHP的物件導向的內容到這裡算是講完了,在開發中利用物件導向的思想進行開發是一定要掌握的技能。同時也要對物件導向進行深度的瞭解。
以上就是PHP,反射、對象序列化的內容,更多相關內容請關注topic.alibabacloud.com(www.php.cn)!