企业🤖AI Agent构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
[TOC] * * * * * ## 1 Trait的意义 **将不同类中相同代码组织到同一个trait中,避免代码重复编写。** 以tp5的model扩展实现为例 thinkphp/library/trait/model下的Adv.php 这个**trait中定义模型扩展中通用方法**。 由在/thinkphp/library/think/model/目录下的 Adv.php,Mongo.php中**分别引入** **避免了**在Adv.php与Mongo.php中**编写相同的代码** trait在**PHP5.4中引入**。 与类相似,使用细粒度和一致的方式组织功能代码 **不能通过自身实例化,需要在类中使用use引入使用** 与多继承与混入类(Mixin)的功能相似 为传统类的单继承增加了**代码组合的另一种方法** * * * * * ## 2 Trait的简单使用 ~~~ <?php trait ezcReflectionReturnInfo { function getReturnType () { /*1*/ } function getReturnDescription () { /*2*/ } } class ezcReflectionMethod extends ReflectionMethod { use ezcReflectionReturnInfo ; /* ... */ } class ezcReflectionFunction extends ReflectionFunction { use ezcReflectionReturnInfo ; /* ... */ } ?> ~~~ 使用**trait关键字**定义了**重复代码内容** 在需要引入trait的类中使用**关键字use**引入了对应的trait代码 为两个类都添加了getReturnType(),getReturnDescription()方法 ## 3 Trait的内部组织 >[info] 属性 trait中可以定义类中需要的属性。 ~~~ <?php trait PropertiesTrait { public $x = 1 ; } class PropertiesExample { use PropertiesTrait ; } $example = new PropertiesExample ; $example -> x ; ?> ~~~ 类PropertiesExample使用了trait的$x属性。 **类中不能定义trait已定义的属性**,否则产生错误 如果类中属性与trait的属性具有相同的可见性和初始值,错误级别是E_SRTICT,否则是指明错误 ~~~ <?php trait PropertiesTrait { public $same = true ; public $different = false ; } class PropertiesExample { use PropertiesTrait ; public $same = true ; // Strict Standards public $different = true ; // 致命错误 } ?> ~~~ >[info] 方法 通常使用trait**定义类中需要重复使用的方法代码**。如简单使用中的例子 >[info] 修饰符 trait中的属性和方法可以使用 **public,static,abstract等修饰符修饰,** 设置属性和方法的**访问控制**。 静态变量的使用 ~~~ <?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 ?> ~~~ 静态方法的使用 ~~~ <?php trait StaticExample { public static function doSomething () { return 'Doing something' ; } } class Example { use StaticExample ; } Example :: doSomething (); ?> ~~~ 抽象方法的使用 ~~~ <?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 ; } } ?> ~~~ >[info] 组合 在trait中可以使用use引入其他的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 (); ?> ~~~ ## 4 Trait的高级使用 >[info] 覆盖优先级 类的成员覆盖引入的trait的成员 trait的成员覆盖继承类的成员 ~~~ <?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 (); ?> ~~~ 从基类继承的成员被插入的 SayWorld Trait 中的 MyHelloWorld 方法所覆盖。 其行为 MyHelloWorld 类中定义的方法一致。 优先顺序是当前类中的方法会覆盖 trait 方法,而 trait 方法又覆盖了基类中的方法。 ~~~ <?php trait HelloWorld { public function sayHello () { echo 'Hello World!' ; } } class TheWorldIsNotEnough { use HelloWorld ; public function sayHello () { echo 'Hello Universe!' ; } } $o = new TheWorldIsNotEnough (); $o -> sayHello (); ?> ~~~ >[info] 引入多个trait 通过逗号在use声明中可以引入多个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 (); ?> ~~~ >[info] 多个trait冲突 如果两个 trait 都插入了一个同名的方法,如果没有明确解决冲突将会产生一个致命错误。 为了解决多个 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 的别名。 >[info] as修改访问控制 使用 as 语法还可以用来调整方法的访问控制。 ~~~ <?php trait HelloWorld { public function sayHello () { echo 'Hello World!' ; } } // 修改 sayHello 的访问控制 class MyClass1 { use HelloWorld { sayHello as protected; } } // 给方法一个改变了访问控制的别名 // 原版 sayHello 的访问控制则没有发生变化 class MyClass2 { use HelloWorld { sayHello as private myPrivateHello ; } } ?> ~~~ ## 5 tp5中的trait 使用T()或者Loader::import()可以导入对应trait ~~~ thinkphp\library\think\Controller.php: namespace think; T('controller/Jump'); class Controller { use \traits\controller\Jump; // 视图类实例 protected $view = null; ... } ~~~ ~~~ thinkphp\library\think\model\Adv.php: namespace think\model; \think\Loader::import('model/Adv', TRAIT_PATH, EXT); \think\Loader::import('model/Transaction', TRAIT_PATH, EXT); class Adv extends \think\Model { use \traits\model\Adv; use \traits\model\Transaction; } ~~~ ~~~ thinkphp\library\think\model\Mongo.php: namespace think\model; use think\Lang; use think\Loader; \think\Loader::import('modle/Adv', TRAIT_PATH, EXT); /** * MongoModel模型类 * 实现了ODM和ActiveRecords模式 */ class Mongo extends \think\Model { use \traits\model\Adv; ... } ~~~