PHP 實現(xiàn)代碼復(fù)用的一個方法 traits新特性
來源:易賢網(wǎng) 閱讀:977 次 日期:2015-03-13 10:01:30
溫馨提示:易賢網(wǎng)小編為您整理了“PHP 實現(xiàn)代碼復(fù)用的一個方法 traits新特性”,方便廣大網(wǎng)友查閱!

這篇文章主要介紹了PHP 實現(xiàn)代碼復(fù)用的一個方法,traits的新特性的相關(guān)資料,需要的朋友可以參考下

在閱讀yii2源碼的時候接觸到了trait,就學(xué)習(xí)了一下,寫下博客記錄一下。

自 PHP 5.4.0 起,PHP 實現(xiàn)了代碼復(fù)用的一個方法,稱為 traits。

Traits 是一種為類似 PHP 的單繼承語言而準備的代碼復(fù)用機制。Trait 為了減少單繼承語言的限制,使開發(fā)人員能夠自由地在不同層次結(jié)構(gòu)內(nèi)獨立的類中復(fù)用方法集。Traits 和類組合的語義是定義了一種方式來減少復(fù)雜性,避免傳統(tǒng)多繼承和混入類(Mixin)相關(guān)的典型問題。

Trait 和一個類相似,但僅僅旨在用細粒度和一致的方式來組合功能。Trait 不能通過它自身來實例化。它為傳統(tǒng)繼承增加了水平特性的組合;也就是說,應(yīng)用類的成員不需要繼承。

Trait 示例

代碼如下:

<?php

trait ezcReflectionReturnInfo {

function getReturnType() { /*1*/ }

function getReturnDescription() { /*2*/ }

}

class ezcReflectionMethod extends ReflectionMethod {

use ezcReflectionReturnInfo;

/* ... */

}

class ezcReflectionFunction extends ReflectionFunction {

use ezcReflectionReturnInfo;

/* ... */

}

?>

優(yōu)先級

從基類繼承的成員被 trait 插入的成員所覆蓋。優(yōu)先順序是來自當(dāng)前類的成員覆蓋了 trait 的方法,而 trait 則覆蓋了被繼承的方法。

優(yōu)先順序示例

代碼如下:

<?php

class Base {

public function sayHello() {

echo 'Hello ';

}

}

trait SayWorld {

public function sayHello() {

parent::sayHello();

echo 'World!';

}

}

class MyHelloWorld extends Base {

use SayWorld;

}

$o = new MyHelloWorld();

$o->sayHello();

?>

以上例程會輸出:Hello World!

從基類繼承的成員被插入的 SayWorld Trait 中的 sayHello 方法所覆蓋。其行為 MyHelloWorld 類中定義的方法一致。優(yōu)先順序是當(dāng)前類中的方法會覆蓋 trait 方法,而 trait 方法又覆蓋了基類中的方法。

另一個優(yōu)先級順序的例子

代碼如下:

<?php

trait HelloWorld {

public function sayHello() {

echo 'Hello World!';

}

}

class TheWorldIsNotEnough {

use HelloWorld;

public function sayHello() {

echo 'Hello Universe!';

}

}

$o = new TheWorldIsNotEnough();

$o->sayHello();

?>

以上例程會輸出:Hello Universe!

多個 trait

通過逗號分隔,在 use 聲明列出多個 trait,可以都插入到一個類中。

多個 trait 的用法的例子

代碼如下:

<?php

trait Hello {

public function sayHello() {

echo 'Hello ';

}

}

trait World {

public function sayWorld() {

echo 'World';

}

}

class MyHelloWorld {

use Hello, World;

public function sayExclamationMark() {

echo '!';

}

}

$o = new MyHelloWorld();

$o->sayHello();

$o->sayWorld();

$o->sayExclamationMark();

?>

以上例程會輸出:Hello World!

沖突的解決

如果兩個 trait 都插入了一個同名的方法,如果沒有明確解決沖突將會產(chǎn)生一個致命錯誤。

為了解決多個 trait 在同一個類中的命名沖突,需要使用 insteadof 操作符來明確指定使用沖突方法中的哪一個。

以上方式僅允許排除掉其它方法,as 操作符可以將其中一個沖突的方法以另一個名稱來引入。

沖突解決的例子

代碼如下:

<?php

trait A {

public function smallTalk() {

echo 'a';

}

public function bigTalk() {

echo 'A';

}

}

trait B {

public function smallTalk() {

echo 'b';

}

public function bigTalk() {

echo 'B';

}

}

class Talker {

use A, B {

B::smallTalk insteadof A;

A::bigTalk insteadof B;

}

}

class Aliased_Talker {

use A, B {

B::smallTalk insteadof A;

A::bigTalk insteadof B;

B::bigTalk as talk;

}

}

?>

在本例中 Talker 使用了 trait A 和 B。由于 A 和 B 有沖突的方法,其定義了使用 trait B 中的 smallTalk 以及 trait A 中的 bigTalk。

Aliased_Talker 使用了 as 操作符來定義了 talk 來作為 B 的 bigTalk 的別名。

修改方法的訪問控制

使用 as 語法還可以用來調(diào)整方法的訪問控制。

修改方法的訪問控制的例子

代碼如下:

<?php

trait HelloWorld {

public function sayHello() {

echo 'Hello World!';

}

}

// 修改 sayHello 的訪問控制

class MyClass1 {

use HelloWorld { sayHello as protected; }

}

// 給方法一個改變了訪問控制的別名

// 原版 sayHello 的訪問控制則沒有發(fā)生變化

class MyClass2 {

use HelloWorld { sayHello as private myPrivateHello; }

}

?>

從 trait 來組成 trait

正如類能夠使用 trait 一樣,其它 trait 也能夠使用 trait。在 trait 定義時通過使用一個或多個 trait,它能夠組合其它 trait 中的部分或全部成員。

從 trait 來組成 trait的例子

代碼如下:

<?php

trait Hello {

public function sayHello() {

echo 'Hello ';

}

}

trait World {

public function sayWorld() {

echo 'World!';

}

}

trait HelloWorld {

use Hello, World;

}

class MyHelloWorld {

use HelloWorld;

}

$o = new MyHelloWorld();

$o->sayHello();

$o->sayWorld();

?>

以上例程會輸出:Hello World!

Trait 的抽象成員

為了對使用的類施加強制要求,trait 支持抽象方法的使用。

表示通過抽象方法來進行強制要求的例子

代碼如下:

<?php

trait Hello {

public function sayHelloWorld() {

echo 'Hello'.$this->getWorld();

}

abstract public function getWorld();

}

class MyHelloWorld {

private $world;

use Hello;

public function getWorld() {

return $this->world;

}

public function setWorld($val) {

$this->world = $val;

}

}

?>

Trait 的靜態(tài)成員

Traits 可以被靜態(tài)成員靜態(tài)方法定義。

靜態(tài)變量的例子

代碼如下:

<?php

trait Counter {

public function inc() {

static $c = 0;

$c = $c + 1;

echo "$c\n";

}

}

class C1 {

use Counter;

}

class C2 {

use Counter;

}

$o = new C1(); $o->inc(); // echo 1

$p = new C2(); $p->inc(); // echo 1

?>

靜態(tài)方法的例子

代碼如下:

<?php

trait StaticExample {

public static function doSomething() {

return 'Doing something';

}

}

class Example {

use StaticExample;

}

Example::doSomething();

?>

靜態(tài)變量和靜態(tài)方法的例子

代碼如下:

<?php

trait Counter {

public static $c = 0;

public static function inc() {

self::$c = self::$c + 1;

echo self::$c . "\n";

}

}

class C1 {

use Counter;

}

class C2 {

use Counter;

}

C1::inc(); // echo 1

C2::inc(); // echo 1

?>

屬性

Trait 同樣可以定義屬性。

定義屬性的例子

代碼如下:

<?php

trait PropertiesTrait {

public $x = 1;

}

class PropertiesExample {

use PropertiesTrait;

}

$example = new PropertiesExample;

$example->x;

?>

如果 trait 定義了一個屬性,那類將不能定義同樣名稱的屬性,否則會產(chǎn)生一個錯誤。如果該屬性在類中的定義與在 trait 中的定義兼容(同樣的可見性和初始值)則錯誤的級別是 E_STRICT,否則是一個致命錯誤。

沖突的例子

代碼如下:

<?php

trait PropertiesTrait {

public $same = true;

public $different = false;

}

class PropertiesExample {

use PropertiesTrait;

public $same = true; // Strict Standards

public $different = true; // 致命錯誤

}

?>

Use的不同

不同use的例子

代碼如下:

<?php

namespace Foo\Bar;

use Foo\Test; // means \Foo\Test - the initial \ is optional

?>

<?php

namespace Foo\Bar;

class SomeClass {

use Foo\Test; // means \Foo\Bar\Foo\Test

}

?>

第一個use是用于 namespace 的 use Foo\Test,找到的是 \Foo\Test,第二個 use 是使用一個trait,找到的是\Foo\Bar\Foo\Test。

__CLASS__和__TRAIT__

__CLASS__ 返回 use trait 的 class name,__TRAIT__返回 trait name

示例如下

代碼如下:

<?php

trait TestTrait {

public function testMethod() {

echo "Class: " . __CLASS__ . PHP_EOL;

echo "Trait: " . __TRAIT__ . PHP_EOL;

}

}

class BaseClass {

use TestTrait;

}

class TestClass extends BaseClass {

}

$t = new TestClass();

$t->testMethod();

//Class: BaseClass

//Trait: TestTrait

Trait單例

實例如下

代碼如下:

<?php

trait singleton {

/**

* private construct, generally defined by using class

*/

//private function __construct() {}

public static function getInstance() {

static $_instance = NULL;

$class = __CLASS__;

return $_instance ?: $_instance = new $class;

}

public function __clone() {

trigger_error('Cloning '.__CLASS__.' is not allowed.',E_USER_ERROR);

}

public function __wakeup() {

trigger_error('Unserializing '.__CLASS__.' is not allowed.',E_USER_ERROR);

}

}

/**

* Example Usage

*/

class foo {

use singleton;

private function __construct() {

$this->name = 'foo';

}

}

class bar {

use singleton;

private function __construct() {

$this->name = 'bar';

}

}

$foo = foo::getInstance();

echo $foo->name;

$bar = bar::getInstance();

echo $bar->name;

調(diào)用trait方法

雖然不很明顯,但是如果Trait的方法可以被定義為在普通類的靜態(tài)方法,就可以被調(diào)用

實例如下

代碼如下:

<?php

trait Foo {

function bar() {

return 'baz';

}

}

echo Foo::bar(),"\\n";

?>

小伙伴們對于traits的新特性是否熟悉了呢,希望本文能對大家有所幫助。

更多信息請查看IT技術(shù)專欄

更多信息請查看網(wǎng)絡(luò)編程
易賢網(wǎng)手機網(wǎng)站地址:PHP 實現(xiàn)代碼復(fù)用的一個方法 traits新特性

2025國考·省考課程試聽報名

  • 報班類型
  • 姓名
  • 手機號
  • 驗證碼
關(guān)于我們 | 聯(lián)系我們 | 人才招聘 | 網(wǎng)站聲明 | 網(wǎng)站幫助 | 非正式的簡要咨詢 | 簡要咨詢須知 | 新媒體/短視頻平臺 | 手機站點 | 投訴建議
工業(yè)和信息化部備案號:滇ICP備2023014141號-1 云南省教育廳備案號:云教ICP備0901021 滇公網(wǎng)安備53010202001879號 人力資源服務(wù)許可證:(云)人服證字(2023)第0102001523號
聯(lián)系電話:0871-65099533/13759567129 獲取招聘考試信息及咨詢關(guān)注公眾號:hfpxwx
咨詢QQ:1093837350(9:00—18:00)版權(quán)所有:易賢網(wǎng)