Home >Backend Development >PHP Tutorial >How to implement new traits of code reuse in PHP
I came into contact with traits while reading the yii2 source code, so I studied it and wrote a blog to record it.
Since PHP 5.4.0, PHP has implemented a method of code reuse called traits.
Traits is a code reuse mechanism prepared for single inheritance languages like PHP. Traits are designed to reduce the constraints of single-inheritance languages and allow developers to freely reuse method sets in independent classes within different hierarchies. The semantics of traits and class composition define a way to reduce complexity and avoid the typical problems associated with traditional multiple inheritance and mixins.
Trait is similar to a class, but is only designed to combine functionality in a fine-grained and consistent way. Trait cannot be instantiated by itself. It adds a combination of horizontal features to traditional inheritance; that is, members of application classes do not need to be inherited.
Trait example
The code is as follows:
<?php trait ezcReflectionReturnInfo { function getReturnType() { /*1*/ } function getReturnDescription() { /*2*/ } } class ezcReflectionMethod extends ReflectionMethod { use ezcReflectionReturnInfo; /* ... */ } class ezcReflectionFunction extends ReflectionFunction { use ezcReflectionReturnInfo; /* ... */ } ?>
Priority
Members inherited from the base class are inserted by trait covered by members. The order of precedence is that members from the current class override the trait's methods, and the trait overrides the inherited methods.
Priority order example
The code is as follows:
<?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(); ?>
The above routine will output: Hello World!
SayWorld where members inherited from the base class are inserted Covered by the sayHello method in Trait. Its behavior is consistent with the methods defined in the MyHelloWorld class. The order of precedence is that methods in the current class override trait methods, which in turn override methods in the base class.
Another example of priority order
The code is as follows:
<?php trait HelloWorld { public function sayHello() { echo 'Hello World!'; } } class TheWorldIsNotEnough { use HelloWorld; public function sayHello() { echo 'Hello Universe!'; } } $o = new TheWorldIsNotEnough(); $o->sayHello(); ?>
The above routine will output: Hello Universe!
Multiple traits
are separated by commas and multiple traits are listed in the use statement. They can all be inserted into a class.
Examples of usage of multiple traits
The code is as follows:
<?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(); ?>
The above routine will output: Hello World!
Conflict resolution
If two traits insert a method with the same name, a fatal error will occur if the conflict is not resolved explicitly.
In order to resolve the naming conflict of multiple traits in the same class, you need to use the insteadof operator to explicitly specify which of the conflicting methods to use.
The above method only allows to exclude other methods. The as operator can introduce one of the conflicting methods under another name.
Example of conflict resolution
The code is as follows:
<?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; } } ?>
In this example Talker uses traits A and B. Since A and B have conflicting methods, they define using smallTalk from trait B and bigTalk from trait A.
Aliased_Talker
Use the as operator to define talk as an alias of B's bigTalk.
Modify the access control of the method
Using the as syntax can also be used to adjust the access control of the method.
An example of modifying the access control of a method
The code is as follows:
<?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; } } ?>
Composed from trait to trait
Just like Just as classes can use traits, other traits can also use traits. By using one or more traits when a trait is defined, it can combine some or all members of other traits.
Example of composing trait from trait
The code is as follows:
<?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(); ?>
The above routine will output: Hello World!
Abstract of Trait Members
In order to enforce requirements on the classes used, traits support the use of abstract methods.
Indicates an example of mandatory requirements through abstract methods
The code is as follows:
<?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; } } ?>
Static member of Trait
Traits can be defined by static members and static methods.
Example of static variables
The code is as follows:
<?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 ?>
Example of static method
The code is as follows:
<?php trait StaticExample { public static function doSomething() { return 'Doing something'; } } class Example { use StaticExample; } Example::doSomething(); ?>
Examples of static variables and static methods
The code is as follows:
<?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 ?>
Attributes
Trait can also define attributes.
Example of defining attributes
The code is as follows:
<?php trait PropertiesTrait { public $x = 1; } class PropertiesExample { use PropertiesTrait; } $example = new PropertiesExample; $example->x; ?>
If a trait defines an attribute, that class will not be able to define an attribute with the same name, otherwise a mistake. If the property's definition in the class is compatible with its definition in the trait (same visibility and initial value) then the error level is E_STRICT, otherwise it is a fatal error.
Conflict examples
The code is as follows:
<?php trait PropertiesTrait { public $same = true; public $different = false; } class PropertiesExample { use PropertiesTrait; public $same = true; // Strict Standards public $different = true; // 致命错误 } ?>
Differences in Use
Examples of different uses
The code is as follows:
<?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 } ?>
The first use is use Foo\Test for namespace, and what is found is \Foo\Test. The second use is to use a trait. Found \Foo\Bar\Foo\Test
.
__CLASS__
and __TRAIT__
__CLASS__
Returns the class name of the use trait, __TRAIT__ returns the trait name
Example The code is as follows
The code is as follows:
<?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 singleton
The example is as follows
The code is as follows:
<?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;
Call trait method
Although it is not obvious, if the trait method can be defined as a static method in a normal class, it can be called
实例如下
代码如下:
<?php trait Foo { function bar() { return 'baz'; } } echo Foo::bar(),"\\n"; ?>
相关学习推荐:PHP编程从入门到精通
The above is the detailed content of How to implement new traits of code reuse in PHP. For more information, please follow other related articles on the PHP Chinese website!