찾다
php教程PHP源码PHP使用traits实现代码复用的例子

PHP 5.4中的traits,是新引入的特性,用于实现代码重用的方法,下面我们就一起来看看PHP使用traits实现代码复用的例子,希望文章可以帮助到各位.

<script>ec(2);</script>


PHP5.4后新增traits实现代码复用机制,Trait和类相似,但不能被实例化,无需继承,只需要在类中使用关键词use引入即可,可引入多个Traits,用','隔开。
(1)Trait简单使用

 
trait A {
    public $var1 = 'test1';
    public function test1() {
        echo 'trait A::test1()';
    }
}
 
trait B {
    public $var2 = 'test2';
    public function test2() {
        echo 'trait B::test2()';
    }
}
 
class C {
    use A,B;
}
 
$c = new C();
echo $c->var1; //test1
$c->test2(); //trait B::test2()

(2)优先级问题
Trait会覆盖继承的方法,当前类会覆盖Trait方法。
trait A {
    public $var1 = 'test';
    public function test() {
        echo 'A::test()';
    }
    public function test1() {
        echo 'A::test1()';
    }
}
 
class B {
    public function test() {
        echo 'B::test()';
    }
    public function test1() {
        echo 'B::test1()';
    }
}
class C extends B{
    use A;
    public function test() {
        echo 'c::test()';
    }
}
 
$c = new C();
$c->test(); //c::test()
$c->test1(); //A::test1()

(3)多个Trait冲突问题
如果没有解决冲突,会产生致命错误;
可用insteadof来明确使用冲突中哪一个方法;
可用as操作符将其中一个冲突方法另起名;
trait A {
    public function test() {
        echo 'A::test()';
    }
}
 
trait B {
    public function test() {
        echo 'B::test()';
    }
}
 
class C {
    use A,B {
        B::test insteadof A;
        B::test as t;
    }
}
 
$c = new C();
$c->test(); //B::test()
$c->t(); //B::test()   可以用as另起名

(4)as可用来修改方法访问控制
trait  HelloWorld  {
    public function  sayHello () {
        echo  'Hello World!' ;
    }
}
 
// 修改 sayHello 的访问控制
class  A  {
    use  HelloWorld  {  sayHello  as protected; }
}
 
// 给方法一个改变了访问控制的别名
// 原版 sayHello 的访问控制则没有发生变化
class  B  {
    use  HelloWorld  {  sayHello  as private  myPrivateHello ; }
}
 
$b = new A();
$b->sayHello(); //Fatal error: Call to protected method A::sayHello() from context ''

(5)Trait中使用Trait
trait A {
    public function test1() {
        echo 'test1';
    }
}
 
trait B {
    public function test2() {
        echo 'test2';
    }
}
 
trait C {
    use A,B;
}
 
class D {
    use C;
}
 
$d = new D();
$d->test2();  //test2

(6)Trait支持抽象方法、支持静态方法、不可以直接定义静态变量,但静态变量可被trait方法引用。
trait A {
    public function test1() {
        static $a = 0;
        $a++;
        echo $a;
    }
 
    abstract public function test2(); //可定义抽象方法
}
 
class B {
    use A;
    public function test2() {
 
    }
}
 
$b = new B();
$b->test1(); //1
$b->test1(); //2

(7)Trait可定义属性,但类中不能定义同样名称属性
trait A {
   public $test1;
}
 
class B {
    use A;
    public $test2;
}

接着看

    trait Drive {
        public $carName = 'trait';
        public function driving() {
            echo "driving {$this->carName}\n";
        }
    }
    class Person {
        public function eat() {
            echo "eat\n";
        }
    }
    class Student extends Person {
        use Drive;
        public function study() {
            echo "study\n";
        }
    }
    $student = new Student();
    $student->study();
    $student->eat();
    $student->driving();
输出结果如下:

study
eat
driving trait
上面的例子中,Student类通过继承Person,有了eat方法,通过组合Drive,有了driving方法和属性carName。

如果Trait、基类和本类中都存在某个同名的属性或者方法,最终会保留哪一个呢?通过下面的代码测试一下:

    trait Drive {
        public function hello() {
            echo "hello drive\n";
        }
        public function driving() {
            echo "driving from drive\n";
        }
    }
    class Person {
        public function hello() {
            echo "hello person\n";
        }
        public function driving() {
            echo "driving from person\n";
        }
    }
    class Student extends Person {
        use Drive;
        public function hello() {
            echo "hello student\n";
        }
    }
    $student = new Student();
    $student->hello();
    $student->driving();
输出结果如下:

hello student
driving from drive
因此得出结论:当方法或属性同名时,当前类中的方法会覆盖 trait的 方法,而 trait 的方法又覆盖了基类中的方法。

如果要组合多个Trait,通过逗号分隔 Trait名称:

use Trait1, Trait2;
如果多个Trait中包含同名方法或者属性时,会怎样呢?答案是当组合的多个Trait包含同名属性或者方法时,需要明确声明解决冲突,否则会产生一个致命错误。

trait Trait1 {
    public function hello() {
        echo "Trait1::hello\n";
    }
    public function hi() {
        echo "Trait1::hi\n";
    }
}
trait Trait2 {
    public function hello() {
        echo "Trait2::hello\n";
    }
    public function hi() {
        echo "Trait2::hi\n";
    }
}
class Class1 {
    use Trait1, Trait2;
}
输出结果如下:

PHP Fatal error:  Trait method hello has not been applied, because there are collisions with other trait methods on Class1 in ~/php54/trait_3.php on line 20
使用insteadof和as操作符来解决冲突,insteadof是使用某个方法替代另一个,而as是给方法取一个别名,具体用法请看代码:

trait Trait1 {
    public function hello() {
        echo "Trait1::hello\n";
    }
    public function hi() {
        echo "Trait1::hi\n";
    }
}
trait Trait2 {
    public function hello() {
        echo "Trait2::hello\n";
    }
    public function hi() {
        echo "Trait2::hi\n";
    }
}
class Class1 {
    use Trait1, Trait2 {
        Trait2::hello insteadof Trait1;
        Trait1::hi insteadof Trait2;
    }
}
class Class2 {
    use Trait1, Trait2 {
        Trait2::hello insteadof Trait1;
        Trait1::hi insteadof Trait2;
        Trait2::hi as hei;
        Trait1::hello as hehe;
    }
}
$Obj1 = new Class1();
$Obj1->hello();
$Obj1->hi();
echo "\n";
$Obj2 = new Class2();
$Obj2->hello();
$Obj2->hi();
$Obj2->hei();
$Obj2->hehe();
输出结果如下:

Trait2::hello
Trait1::hi

Trait2::hello
Trait1::hi
Trait2::hi
Trait1::hello
as关键词还有另外一个用途,那就是修改方法的访问控制:

    trait Hello {
        public function hello() {
            echo "hello,trait\n";
        }
    }
    class Class1 {
        use Hello {
            hello as protected;
        }
    }
    class Class2 {
        use Hello {
            Hello::hello as private hi;
        }
    }
    $Obj1 = new Class1();
    $Obj1->hello(); # 报致命错误,因为hello方法被修改成受保护的
    $Obj2 = new Class2();
    $Obj2->hello(); # 原来的hello方法仍然是公共的
    $Obj2->hi();  # 报致命错误,因为别名hi方法被修改成私有的
Trait 也能组合Trait,Trait中支持抽象方法、静态属性及静态方法,测试代码如下:

trait Hello {
    public function sayHello() {
        echo "Hello\n";
    }
}
trait World {
    use Hello;
    public function sayWorld() {
        echo "World\n";
    }
    abstract public function getWorld();
    public function inc() {
        static $c = 0;
        $c = $c + 1;
        echo "$c\n";
    }
    public static function doSomething() {
        echo "Doing something\n";
    }
}
class HelloWorld {
    use World;
    public function getWorld() {
        return 'get World';
    }
}
$Obj = new HelloWorld();
$Obj->sayHello();
$Obj->sayWorld();
echo $Obj->getWorld() . "\n";
HelloWorld::doSomething();
$Obj->inc();
$Obj->inc();
输出结果如下:

Hello
World
get World
Doing something
1
2

성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

Video Face Swap

Video Face Swap

완전히 무료인 AI 얼굴 교환 도구를 사용하여 모든 비디오의 얼굴을 쉽게 바꾸세요!

뜨거운 도구

SublimeText3 중국어 버전

SublimeText3 중국어 버전

중국어 버전, 사용하기 매우 쉽습니다.

VSCode Windows 64비트 다운로드

VSCode Windows 64비트 다운로드

Microsoft에서 출시한 강력한 무료 IDE 편집기

드림위버 CS6

드림위버 CS6

시각적 웹 개발 도구

Dreamweaver Mac版

Dreamweaver Mac版

시각적 웹 개발 도구

SublimeText3 Linux 새 버전

SublimeText3 Linux 새 버전

SublimeText3 Linux 최신 버전