## traits 詳解
>[info] traits 知識并不是ThinkPHP教學的內容,但軟刪除的模型中應用了這項新技術~~
* * * * *
> 我們知道,PHP面向對象是單繼承的,無法同時從兩個或以上的基類繼承屬性或方法。從PHP 5.4起, Traits(構件類)技術出現,使多繼承成為現實!
* * * * *
>[warning] 注意:
1. Traits類的聲明使用trait關鍵詞,
2. 類中使用use關鍵字聲明要組合的Traits類名稱;
3. Traits類不能直接實例化
* * * * *
#### 實例演示:
~~~
<?php
//創建trait類:Test1
trait Test1
{
public function demo1()
{
return 'Test1::demo1()<br />';
}
}
//創建基類(父類):Father
class Father
{
public function demo1()
{
return 'Father::demo1<br />';
}
}
//創建自定義類:MyClass 是Father的派生類(子類)
class MyClass extends Father
{
use Test1;
public function demo1()
{
return 'MyClass::demo1<br />';
}
}
//創建自定義MyClass的對象
$obj = new MyClass;
//訪問$obj對象的demo1()方法
echo $obj -> demo1();
~~~
> 1. 現在我們已經有了三個類:trait類,基類Father和派生類MyClass;
> 2. 每個類中都有一個demo1( )方法;
> 3. $obj是MyClass實例化的對象,那么:`echo $obj -> demo1();`將會調用哪個類的demo1()方法呢?
* 讓我們運行一下吧:
~~~
MyClass::demo1
~~~
> 可見,實際調用的是MyClass類的demo1()方法。這符合繼承的原理,因為子復寫了父類同名方法。
> <font color="red">下面我們把MyClass類中的demo1()方法刪除</font>
* 修改后的代碼如下:
~~~
<?php
//創建trait類:Test1
trait Test1
{
public function demo1()
{
return 'Test1::demo1()<br />';
}
}
//創建基類(父類):Father
class Father
{
public function demo1()
{
return 'Father::demo1<br />';
}
}
//創建自定義類:MyClass 是Father的派生類(子類)
class MyClass extends Father
{
use Test1;
}
//創建自定義MyClass的對象
$obj = new MyClass;
//訪問$obj對象的demo1()方法
echo $obj -> demo1();
~~~
> 按照之前知識,如果調用的方法,在子類中沒有定義,則調用父類中同名方法,真的是這樣的嗎?讓我們測試一下吧:
* 這時運行結果如下:
~~~
Test1::demo1()
~~~
> 覺得奇怪嗎?為什么不是父類的輸出結果:`Father::demo1()`,而是:`Test1::demo1()`呢。這因為子類中,除了要繼承Father類,還要繼承trait類Test1,顯然,子類MyClass中的`use Test1` 的繼承優先級要高于原父類的Father,于是產生覆蓋效果。
> <span style="color:red;">trait類同名方法覆蓋了基類Father中的同名方法。</span>
#### 那么我們屏蔽掉:`use Test1;`,會調用父類同名方法嗎?
* 再次修改一下代碼:
~~~
<?php
//創建trait類:Test1
trait Test1
{
public function demo1()
{
return 'Test1::demo1()<br />';
}
}
//創建基類(父類):Father
class Father
{
public function demo1()
{
return 'Father::demo1<br />';
}
}
//創建自定義類:MyClass 是Father的派生類(子類)
class MyClass extends Father
{
}
//創建自定義MyClass的對象
$obj = new MyClass;
//訪問$obj對象的demo1()方法
echo $obj -> demo1();
~~~
> 現在子類MyClass中已經沒有use Test1;trait類Test已失效。
* 現在執行結果,如不出意料,應該是父類的demo1()方法的結果:
~~~
Father::demo1
~~~
>[info] 果然如此,調用是父類中的demo1()方法。
* * * * *
>[info] 現在我們思考另一個問題:如果有二個trait類,都是一個demo1方法,怎么辦?這是個很常見的問題,必須得到解決。
* 我們再次修改代碼,增加一個trait類Test2:
~~~
<?php
//創建trait類:Test1
trait Test1
{
public function demo1()
{
return 'Test1::demo1()<br />';
}
}
//創建trait類:Test2
trait Test2
{
public function demo1()
{
return 'Test2::demo1()<br />';
}
}
//創建基類(父類):Father
class Father
{
public function demo1()
{
return 'Father::demo1<br />';
}
}
//創建自定義類:MyClass 是Father的派生類(子類)
class MyClass extends Father
{
use Test1,Test2;
}
//創建自定義MyClass的對象
$obj = new MyClass;
//訪問$obj對象的demo1()方法
echo $obj -> demo1();
~~~
> 現在有二個trait類Test1和Test2,這二個類中都有demo1方法,`echo $obj -> demo1();`好糾結,應該調用哪個呢?
* 運行結果,肯定是錯誤的:
~~~
//大意是trait中的方法demo1 沒有提交運行,因為有重復的定義
Fatal error: Trait method demo1 has not been applied, because there are collisions with other trait methods on MyClass in /Users/zhupeter/Documents/web/test/index.php on line 31
~~~
>[info] 解決方案有二種:
1. 設置沖突時,調用哪一個方法:`insteadof;`
2. 給可能沖突的方法起一個別名:`as;`
~~~
<?php
//創建trait類:Test1
trait Test1
{
public function demo1()
{
return 'Test1::demo1()<br />';
}
}
//創建trait類:Test2
trait Test2
{
public function demo1()
{
return 'Test2::demo1()<br />';
}
}
//創建基類(父類):Father
class Father
{
public function demo1()
{
return 'Father::demo1<br />';
}
}
//創建自定義類:MyClass 是Father的派生類(子類)
class MyClass extends Father
{
use Test1,Test2{
//方案1:Test2的demo1方法與Test1的demo1方法沖突時,調用Test2的demo1方法;
Test2::demo1 insteadof Test1;
//Test2中的demo1方法可以使用方法別名td2調用
Test2::demo1 as td2;
}
}
//創建自定義MyClass的對象
$obj = new MyClass;
//訪問$obj對象的demo1()方法
//根據use中設置的調用規則,現在調用的應該是Test2類中的demo1方法
echo $obj -> demo1();
//第二套解決方案:給可能沖突的該當起別名來規避
//現在調用的仍然是Test2類中的demo1方法
echo $obj -> td2();
~~~
* 結過分析,運行結果也是可以預見的:
~~~
Test2::demo1()
Test2::demo1()
~~~
#### 這種特殊類的聲明,學會了嗎?親們~~
* * * * *
#### 總結:
>[danger] ThinkPHP5中的軟刪除等很多特殊操作,都是通過trait類來實現的,簡而言之,traits 提供了實現多繼承的一種機制。一定要掌握這種新技術。
- 前言[隨時更新]
- 開發環境
- 1.Mac環境
- 2.windows環境
- 模型對象
- 1.創建模型對象
- 2.模型初始化
- 數據對象
- 1.定義數據對象
- 2.創建數據對象
- 1.data方法
- 2.setAttr方法
- 3.__set方法
- 4.查詢數據對象
- 1.getData方法
- 2.getAttr方法
- 3.__get方法
- OOP難點總結
- 1.get_class( )實例講解
- 2.get_called_class( )實例講解
- 3.__call( )實例講解
- 3.__callStatic( )實例講解
- 4.call_user_func_array函數[重點]
- 5.普通方法與靜態方法
- 6.在Model源碼中的應用
- 7.new static 延遲靜態綁定
- PHP標準化規范
- 查詢數據
- 1.獲取單條:get靜態方法
- 2.獲取單條:對象查詢
- 3.獲取多條:all靜態方法
- 4.獲取多條:對象查詢
- 5.獲取字段值:value方法
- 6.獲取列值:column方法
- 7.動態查詢:getBy字段名
- 8.助手函數:model查詢
- 9.加載器:Loader類查詢
- 10.數據庫與模型查詢對比
- 新增數據
- 1.sava方法
- 2.savaAll方法
- 3.create靜態方法
- 4.insert靜態調用
- 更新數據
- 1.單條更新:save方法
- 2.批量更新:saveAll方法
- 3.靜態更新:update方法
- 4.查詢類Query直接更新
- 5. 閉包更新
- 刪除數據
- 1.刪除當前記錄:delete
- 2.靜態條件刪除:destory
- 獲取器
- 1.模型方法:set屬性Attr
- 修改器
- 1.set屬性Attr
- 時間戳
- 1.MySQL中日期類型復習
- 2.時間戳功能詳解
- 軟刪除[重點]
- 1.traits詳解[選學內容]
- 2.SoftDelet類源碼分析
- 3. delete實例刪除
- 4.destroy條件刪除
- 5.restore恢復數據
- 類型轉換
- 1. 規則設置
- 2. 實例演示
- 查詢范圍
- 1. 基本概念
- 2.實例演示