自 PHP 5.3.0 起,PHP 增長了一個叫作後期靜態綁定的功能,用於在繼承範圍內引用靜態調用的類。用於在繼承範圍內引用靜態調用的類,即在類的繼承過程當中,使用的類再也不是當前類,而是調用的類。php
下面介紹一個例子,具體原理和使用可參考 PHP手冊 中裏面詳細介紹後期靜態綁定。數組
Class A{ public static function create(){ $self = new self(); //所在位置的類的實例 $static = new static(); //調用類的實例 return array($self, $static); } } class B extends A{ } $arr = B::create(); var_dump($arr[0]); var_dump($arr[1]);
輸出:閉包
object(A)#3 (0) {} object(B)#4 (0) {}
與上述的輸出結果相同,在實例化對象中,static會根據運行時候調用的類來決定實例化的對象,而self是根據所在位置的類來決定實例化對象app
反射機制被多種語言普遍使用,主要用來動態獲取類、實例對象、方法等語言構建信息,經過API函數能夠實現對這些語言構建信息的動態獲取和動態操做等。下面展現一個使用反射的簡單例子。函數
Class ReflectionTest{ public function call(){ echo "hello world"."\r\n"; } } $ref = new ReflectionClass('ReflectionTest'); $inst = $ref->newInstanceArgs(); $inst->call();
上面的例子使用了ReflectionClass,使用ReflectionClass能夠獲取到類的基本信息,包含類的屬性、常量、方法、命名空間、以及對象的實例等等。例子中就是經過newInstanceArgs方法建立一個新的類實例。ReflectionClass類有如下方法:oop
ReflectionClass::__construct — 初始化 ReflectionClass 類 ReflectionClass::export — 導出一個類 ReflectionClass::getConstant — 獲取定義過的一個常量 ReflectionClass::getConstants — 獲取一組常量 ReflectionClass::getConstructor — 獲取類的構造函數 ReflectionClass::getDefaultProperties — 獲取默認屬性 ReflectionClass::getDocComment — 獲取文檔註釋 ReflectionClass::getEndLine — 獲取最後一行的行數 ReflectionClass::getExtension — 根據已定義的類獲取所在擴展的 ReflectionExtension 對象 ReflectionClass::getExtensionName — 獲取定義的類所在的擴展的名稱 ReflectionClass::getFileName — 獲取定義類的文件名 ReflectionClass::getInterfaceNames — 獲取接口(interface)名稱 ReflectionClass::getInterfaces — 獲取接口 ReflectionClass::getMethod — 獲取一個類方法的 ReflectionMethod。 ReflectionClass::getMethods — 獲取方法的數組 ReflectionClass::getModifiers — 獲取類的修飾符 ReflectionClass::getName — 獲取類名 ReflectionClass::getNamespaceName — 獲取命名空間的名稱 ReflectionClass::getParentClass — 獲取父類 ReflectionClass::getProperties — 獲取一組屬性 ReflectionClass::getProperty — 獲取類的一個屬性的 ReflectionProperty ReflectionClass::getReflectionConstant — Gets a ReflectionClassConstant for a class's constant ReflectionClass::getReflectionConstants — Gets class constants ReflectionClass::getShortName — 獲取短名 ReflectionClass::getStartLine — 獲取起始行號 ReflectionClass::getStaticProperties — 獲取靜態(static)屬性 ReflectionClass::getStaticPropertyValue — 獲取靜態(static)屬性的值 ReflectionClass::getTraitAliases — 返回 trait 別名的一個數組 ReflectionClass::getTraitNames — 返回這個類所使用 traits 的名稱的數組 ReflectionClass::getTraits — 返回這個類所使用的 traits 數組 ReflectionClass::hasConstant — 檢查常量是否已經定義 ReflectionClass::hasMethod — 檢查方法是否已定義 ReflectionClass::hasProperty — 檢查屬性是否已定義 ReflectionClass::implementsInterface — 接口的實現 ReflectionClass::inNamespace — 檢查是否位於命名空間中 ReflectionClass::isAbstract — 檢查類是不是抽象類(abstract) ReflectionClass::isAnonymous — 檢查類是不是匿名類 ReflectionClass::isCloneable — 返回了一個類是否可複製 ReflectionClass::isFinal — 檢查類是否聲明爲 final ReflectionClass::isInstance — 檢查類的實例 ReflectionClass::isInstantiable — 檢查類是否可實例化 ReflectionClass::isInterface — 檢查類是不是一個接口(interface) ReflectionClass::isInternal — 檢查類是否由擴展或核心在內部定義 ReflectionClass::isIterable — Check whether this class is iterable ReflectionClass::isIterateable — 檢查是否可迭代(iterateable) ReflectionClass::isSubclassOf — 檢查是否爲一個子類 ReflectionClass::isTrait — 返回了是否爲一個 trait ReflectionClass::isUserDefined — 檢查是否由用戶定義的 ReflectionClass::newInstance — 從指定的參數建立一個新的類實例 ReflectionClass::newInstanceArgs — 從給出的參數建立一個新的類實例。 ReflectionClass::newInstanceWithoutConstructor — 建立一個新的類實例而不調用它的構造函數 ReflectionClass::setStaticPropertyValue — 設置靜態屬性的值 ReflectionClass::__toString — 返回 ReflectionClass 對象字符串的表示形式。
相對於多繼承語言(如:C++),代碼複用問題對於單繼承語言(Ruby、PHP、JAVA/、C#)來講須要經過其餘方法解決,如Ruby中經過混入類解決。而PHP是經過Trait來實現代碼複用。this
<?php Class Base{ public function hello(){ echo "Method hello from Base"; } } trait A{ public function hello(){ echo "Method hello from A"."\r\n"; } public function hi(){ echo "Method Hi from A"."\r\n"; } abstract public function abstractFun(); static public function staticFun(){ echo "A Static Method"."\r\n"; } public function getValue(){ static $value; $value++; echo $value."\r\n"; } } trait B{ public function hello(){ parent::hello(); echo "Method hello from B"."\r\n"; } public function hi(){ echo "Method Hi from B"."\r\n"; } } trait AB{ use A, B{ B::hello insteadof A; A::hi insteadof B; } } Class Test extends Base{ use AB; private $value = "value in Test"."\r\n"; public function hi(){ echo "Method Hi from Test"."\r\n"; } public function abstractFun(){ echo $this->value; } } $obj = new Test(); $obj->hello(); $obj->hi(); $obj->staticFun(); $obj->abstractFun();
輸出:spa
<?php Class Base{ public function hello(){ echo "Method hello from Base"."\r\n"; } } trait A{ public function hello(){ echo "Method hello from A"."\r\n"; } public function hi(){ echo "Method Hi from A"."\r\n"; } abstract public function abstractMethod(); static public function staticMethod(){ echo "A Static Method"."\r\n"; } public function staticValue(){ static $value; $value++; echo $value."\r\n"; } } trait B{ public function hello(){ parent::hello(); echo "Method hello from B"."\r\n"; } public function hi(){ echo "Method Hi from B"."\r\n"; } } trait AB{ use A, B{ B::hello insteadof A;//解決名稱衝突 A::hi insteadof B; } } Class Test extends Base{ use AB; private $value = "value in Test"."\r\n"; public function hi(){ echo "Method Hi from Test"."\r\n"; } public function abstractMethod(){ echo $this->value; } } $obj = new Test(); $obj->hello(); //輸出traitB中的hello方法 $obj->hi(); //輸出Test中的方法 $obj->staticMethod();//trait可定義static方法 $obj->abstractMethod();//trait可定義abstract方法 $obj->staticValue();//1 $obj->staticValue();//2 static可以使用static變量//1
輸出:.net
Method hello from Base Method hello from B Method Hi from Test A Static Method value in Test 1 2
1.優先級:當前類優先級>trait>基類code
2.多個trait組合:經過逗號分隔,使用use關鍵字列出多個trait
3.衝突的解決:若是兩個trait都插入一個同名的方法,若沒明確解決衝突會產生一個致命錯誤。爲解決多個trait在同一個類中的命名衝突,需使用insteadof來指定使用衝突方法中的哪個;也能夠用as將其中一個衝突的方法以另外一個名稱引入。
將上面例子中的 trait AB中使用insteadof的兩句刪除,會出現如下報錯
Trait method hello has not been applied, because there are collisions with other trait methods on AB in D:XXXX.php on line 33
Fatal error: Trait method hello has not been applied, because there are collisions with other trait methods on AB in D:XXXX.php on line 33
4.trait的抽象方法:在trait中使用抽象成員,是的類中必須實現這個抽象方法
5.trait的靜態成員:在trait中能夠用靜態方法和靜態變量
6.trait中的屬性定義:在trait中一樣能夠定義屬性