详解PHP中的Traits
程序员文章站
2023-11-11 20:37:04
php是单继承的语言,在php 5.4 traits出现之前,php的类无法同时从两个基类继承属性或方法。php的traits和go语言的组合功能类似,通过在类中使用use...
php是单继承的语言,在php 5.4 traits出现之前,php的类无法同时从两个基类继承属性或方法。php的traits和go语言的组合功能类似,通过在类中使用use关键字声明要组合的trait名称,而具体某个trait的声明使用trait关键词,trait不能直接实例化。具体用法请看下面的代码:
<?php 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、基类和本类中都存在某个同名的属性或者方法,最终会保留哪一个呢?通过下面的代码测试一下:
<?php 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包含同名属性或者方法时,需要明确声明解决冲突,否则会产生一个致命错误。
<?php 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是给方法取一个别名,具体用法请看代码:
<?php 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关键词还有另外一个用途,那就是修改方法的访问控制:
<?php 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中支持抽象方法、静态属性及静态方法,测试代码如下:
<?php 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
以上就是本文的全部内容,希望对大家的学习有所帮助。
上一篇: php解析url的三个示例