Traits

In PHP gibt es eine Methode der Wiederverwendung von Code, die Traits genannt wird.

Traits sind ein Mechanismus zur Wiederverwendung von Code, der in Programmiersprachen mit einfacher Vererbung wie PHP verwendet wird. Ein Trait kann verwendet werden, um den Beschränkungen der einfachen Vererbung auszuweichen, indem er erlaubt, dass Mengen von Methoden frei in mehreren unabhängigen Klassen, die in verschiedenen Klassenhierarchien stecken, wiederzuverwenden. Die Semantik der Kombination von Traits und Klassen ist so definiert, dass die Komplexität reduziert wird und die üblichen Probleme vermeidet, die mit Mehrfachvererbung und Mixins in Verbindung gebracht werden.

Ein Trait hat Ähnlichkeit mit einer Klasse, ist aber nur dafür vorgesehen, Funktionalität in einer feingranularen und konsistenten Art und Weise zu bündeln. Es ist nicht möglich einen Trait alleinstehend zu instantiieren. Es handelt sich um einen Zusatz zur traditionellen Vererbung und erlaubt horizontale Komposition von Verhaltensweisen, d. h. die Verwendung von Klassenmethoden ohne Vererbung vorauszusetzen.

Beispiel #1 Trait-Beispiel

<?php

trait TraitA {
public function
sayHello() {
echo
'Hello';
}
}

trait
TraitB {
public function
sayWorld() {
echo
'World';
}
}

class
MyHelloWorld
{
use
TraitA, TraitB; // Eine Klasse kann mehrere Traits verwenden

public function sayHelloWorld() {
$this->sayHello();
echo
' ';
$this->sayWorld();
echo
"!\n";
}
}

$myHelloWorld = new MyHelloWorld();
$myHelloWorld->sayHelloWorld();

?>

Das oben gezeigte Beispiel erzeugt folgende Ausgabe:

Hello World!

Rangfolge

Methoden der aktuellen Klasse überschreiben Methoden, welche von Traits definiert wurden. Jene wiederum überschreiben von Elternklassen geerbte Methoden.

Beispiel #2 Rangfolge-Beispiel

Die Methode sayHello der Basisklasse wird durch die gleichnamige Methode aus dem Trait überschrieben.

<?php
class Base {
public function
sayHello() {
echo
'Hallo ';
}
}

trait
SayWorld {
public function
sayHello() {
parent::sayHello();
echo
'Welt!';
}
}

class
MyHelloWorld extends Base {
use
SayWorld;
}

$o = new MyHelloWorld();
$o->sayHello();
?>

Das oben gezeigte Beispiel erzeugt folgende Ausgabe:

Hallo Welt!

Beispiel #3 Weiteres Beispiel für Rangfolge

<?php
trait HelloWorld {
public function
sayHello() {
echo
'Hallo Welt!';
}
}

class
TheWorldIsNotEnough {
use
HelloWorld;
public function
sayHello() {
echo
'Hallo Universum!';
}
}

$o = new TheWorldIsNotEnough();
$o->sayHello();
?>

Das oben gezeigte Beispiel erzeugt folgende Ausgabe:

Hallo Universum!

Mehrere Traits

Mehrere Traits können durch eine kommaseparierte Auflistung im use-Statement zu einer Klasse hinzugefügt werden.

Beispiel #4 Verwendung mehrerer Traits

<?php
trait Hello {
public function
sayHello() {
echo
'Hallo ';
}
}

trait
World {
public function
sayWorld() {
echo
' Welt';
}
}

class
MyHelloWorld {
use
Hello, World;
public function
sayExclamationMark() {
echo
'!';
}
}

$o = new MyHelloWorld();
$o->sayHello();
$o->sayWorld();
$o->sayExclamationMark();
?>

Das oben gezeigte Beispiel erzeugt folgende Ausgabe:

Hallo Welt!

Konfliktauflösung

Falls zwei Traits eine Methode mit gleichem Namen einfügen, so wird ein Fatal Error erzeugt, wenn der Konflikt nicht explizit aufgelöst wird.

Um einen Namenskonflikt zwischen Traits in der gleichen Klasse aufzulösen muss der insteadof-Operator verwendet werden, um genau eine der Methoden des Konflikts auszuwählen.

Da dies es nur erlaubt Methoden auszuschließen kann der as-Operator verwendet werden, um einen Alias zu einer der Methoden hinzuzufügen. Es ist zu beachten, dass der as Operator die Methode nicht umbenennt, und auch keine andere Methode beeinflusst.

Beispiel #5 Konfliktauflösung

In diesem Beispiel verwendet die Klasse Talker die Traits A und B. Nachdem A und B sich widersprechende Methoden besitzen definiert die Klasse, dass sie die Variante von smallTalk aus dem Trait B und die Variante von bigTalk des Traits A verwenden möchte.

Die Klasse Aliased_Talker verwendet den as-Operator, damit sie außerdem die Implementierung der bigTalk-Methode von B unter dem Alias talk verwenden kann.

<?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;
}
}
?>

Veränderung der Sichtbarkeit von Methoden

Mit der as-Syntax ist es ebenso möglich, die Sichtbarkeit einer Methode in der darstellenden Klasse zu verändern.

Beispiel #6 Veränderung der Sichtbarkeit von Methoden

<?php
trait HelloWorld {
public function
sayHello() {
echo
'Hallo Welt!';
}
}

// Ändern der Sichtbarkeit von sayHello
class MyClass1 {
use
HelloWorld { sayHello as protected; }
}

// Alias der Methode mit geänderter Sichtbarkeit
// Die Sichtbarkeit von sayHello bleibt unverändert
class MyClass2 {
use
HelloWorld { sayHello as private myPrivateHello; }
}
?>

Trait-Zusammensetzung aus Traits

Ebenso wie Klassen können auch Traits andere Traits verwenden. Indem man einen oder mehrere Traits in einer Traitdefinition verwendet kann man Traits entweder teilweise oder vollständig aus Methoden, welche in anderen Traits definiert sind, zusammensetzen.

Beispiel #7 Trait-Zusammensetzung aus Traits

<?php
trait Hello {
public function
sayHello() {
echo
'Hallo ';
}
}

trait
World {
public function
sayWorld() {
echo
'Welt!';
}
}

trait
HelloWorld {
use
Hello, World;
}

class
MyHelloWorld {
use
HelloWorld;
}

$o = new MyHelloWorld();
$o->sayHello();
$o->sayWorld();
?>

Das oben gezeigte Beispiel erzeugt folgende Ausgabe:

Hallo Welt!

Abstrakte Traitmember

Traits unterstützen auch die Verwendung von abstrakten Methoden, um darstellenden Klassen Anforderungen vorzugeben. Es werden öffentliche, geschützte und private Methoden unterstützt. Vor PHP 8.0.0 wurden nur öffentliche und geschützte abstrakte Methoden unterstützt.

Achtung

Seit PHP 8.0.0 muss die Signatur einer konkreten Methode den Regeln zur Signaturkompatibilität entsprechen; zuvor durfte ihre Signatur davon abweichen.

Beispiel #8 Ausdrücken von Anforderungen durch abstrakte Methoden

<?php
trait Hello {
public function
sayHelloWorld() {
echo
'Hallo'.$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;
}
}
?>

Statische Traitmember

Traits unterstützen sowohl statische Variablen, statische Methoden und statische Eignschaften.

Hinweis:

Seit PHP 8.1.0 sind der Aufruf einer statischen Methode oder der Zugriff auf eine statische Eigenschaft direkt in einem Trait veraltet. Auf statische Methoden und Eigenschaften sollte nur in einer Klasse zugegriffen werden, die den Trait verwendet.

Beispiel #9 Statische Variablen

<?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();
$p = new C2();
$p->inc();

?>

Das oben gezeigte Beispiel erzeugt folgende Ausgabe:

1
1

Beispiel #10 Statische Methoden

<?php

trait StaticExample
{
public static function
doSomething()
{
return
'Tue etwas';
}
}

class
Example
{
use
StaticExample;
}

echo
Example::doSomething();

?>

Das oben gezeigte Beispiel erzeugt folgende Ausgabe:

Doing something

Beispiel #11 Statische Eigenschaften

Achtung

Vor PHP 8.3.0 wurden statische Eigenschaften, die in einem Trait definiert waren, von allen Klassen in derselben Vererbungshierarchie, die diesen Trait verwendeten, gemeinsam genutzt. Seit PHP 8.3.0 wird eine statische Eigenschaft, die von einer Kindklasse über einen Trait verwendet wird, als von der in der Elternklasse definierte Eigenschaft unterschieden betrachtet.

<?php

trait T
{
public static
$counter = 1;
}

class
A
{
use
T;

public static function
incrementCounter()
{
static::
$counter++;
}
}

class
B extends A
{
use
T;
}

A::incrementCounter();

echo
A::$counter, "\n";
echo
B::$counter, "\n";

?>

Das oben gezeigte Beispiel erzeugt mit PHP 8.3 folgende Ausgabe:

2
1

Eigenschaften

Traits können ebenfalls Eigenschaften definieren.

Beispiel #12 Eigenschaften definieren

<?php

trait PropertiesTrait
{
public
$x = 1;
}

class
PropertiesExample
{
use
PropertiesTrait;
}

$example = new PropertiesExample();
$example->x;

?>

Definiert ein Trait eine Eigenschaft, dann kann eine Klasse keine Eigenschaft mit dem selben Namen definieren, es sei denn, sie ist kompatibel (selbe Sichtbarkeit und Typ, readonly-Modifikator und Initialwert); andernfalls wird ein fataler Fehler auftreten.

Beispiel #13 Konfliktauflösung

<?php
trait PropertiesTrait {
public
$same = true;
public
$different1 = false;
public
bool $different2;
public
bool $different3;
}

class
PropertiesExample {
use
PropertiesTrait;
public
$same = true;
public
$different1 = true; // Fatal error
public string $different2; // Fatal error
readonly protected bool $different3; // Fatal error
}
?>

Konstanten

Seit PHP 8.2.0 können Traits auch Konstanten definieren.

Beispiel #14 Definieren von Konstanten

<?php
trait ConstantsTrait {
public const
FLAG_MUTABLE = 1;
final public const
FLAG_IMMUTABLE = 5;
}

class
ConstantsExample {
use
ConstantsTrait;
}

$example = new ConstantsExample;
echo
$example::FLAG_MUTABLE;
?>

Das oben gezeigte Beispiel erzeugt folgende Ausgabe:

1

Wenn ein Trait eine Konstante definiert, kann eine Klasse keine Konstante mit demselben Namen definieren, es sei denn, sie ist kompatibel (dieselbe Sichtbarkeit, derselbe Initialwert und dieselbe Finalität), andernfalls wird ein fataler Fehler ausgegeben.

Beispiel #15 Konfliktauflösung

<?php
trait ConstantsTrait {
public const
FLAG_MUTABLE = 1;
final public const
FLAG_IMMUTABLE = 5;
}

class
ConstantsExample {
use
ConstantsTrait;
public const
FLAG_IMMUTABLE = 5; // Fatal error
}
?>

Finale Methoden

Seit PHP 8.3.0 kann der Modifikator final mit dem Operator as auf Methoden angewendet werden, die aus Traits importiert wurden. Dies kann verwendet werden, um zu verhindern, dass Kindklassen die Methode überschreiben. Die Klasse, die den Trait verwendet, kann die Methode jedoch weiterhin überschreiben.

Beispiel #16 Definieren einer Methode aus einem Trait als final

<?php

trait CommonTrait
{
public function
method()
{
echo
'Hello';
}
}

class
FinalExampleA
{
use
CommonTrait {
CommonTrait::method as final; // Das "final" verhindert, dass Kindklassen
// die Methode überschreiben.
}
}

class
FinalExampleB extends FinalExampleA
{
public function
method() {}
}

?>

Das oben gezeigte Beispiel erzeugt eine ähnliche Ausgabe wie:

Fatal error: Cannot override final method FinalExampleA::method() in ...
add a note

User Contributed Notes 25 notes

up
656
Safak Ozpinar / safakozpinar at gmail
13 years ago
Unlike inheritance; if a trait has static properties, each class using that trait has independent instances of those properties.Example using parent class:<?phpclass TestClass {    public static $_bar;}class Foo1 extends TestClass { }class Foo2 extends TestClass { }Foo1::$_bar = 'Hello';Foo2::$_bar = 'World';echo Foo1::$_bar . ' ' . Foo2::$_bar; // Prints: World World?>Example using trait:<?phptrait TestTrait {    public static $_bar;}class Foo1 {    use TestTrait;}class Foo2 {    use TestTrait;}Foo1::$_bar = 'Hello';Foo2::$_bar = 'World';echo Foo1::$_bar . ' ' . Foo2::$_bar; // Prints: Hello World?>
up
454
greywire at gmail dot com
13 years ago
The best way to understand what traits are and how to use them is to look at them for what they essentially are:  language assisted copy and paste.If you can copy and paste the code from one class to another (and we've all done this, even though we try not to because its code duplication) then you have a candidate for a trait.
up
248
Stefan W
11 years ago
Note that the "use" operator for traits (inside a class) and the "use" operator for namespaces (outside the class) resolve names differently. "use" for namespaces always sees its arguments as absolute (starting at the global namespace):<?phpnamespace Foo\Bar;use Foo\Test;  // means \Foo\Test - the initial \ is optional?>On the other hand, "use" for traits respects the current namespace:<?phpnamespace Foo\Bar;class SomeClass {    use Foo\Test;   // means \Foo\Bar\Foo\Test}?>Together with "use" for closures, there are now three different "use" operators. They all mean different things and behave differently.
up
11
JustAddingSomeAdditionalUseCase
2 years ago
I have not seen this specific use case:"Wanting to preserve action of parent class method, the trait one calling ::parent & also the child class mehod action".// Child class.use SuperTrait {  initialize as initializeOr;}public function initialize(array &$element) {  ...  $this->initializeOr($element);}// Trait.public function initialize(array &$element) {  ...  parent::initialize($element);}// Parent class.public function initialize(array &$element) {  ...}
up
105
t8 at AT pobox dot com
13 years ago
Another difference with traits vs inheritance is that methods defined in traits can access methods and properties of the class they're used in, including private ones.For example:<?phptrait MyTrait{  protected function accessVar()  {    return $this->var;  }}class TraitUser{  use MyTrait;  private $var = 'var';  public function getVar()  {    return $this->accessVar();  }}$t = new TraitUser();echo $t->getVar(); // -> 'var'                                                                                                                                                                                                                          ?>
up
99
chris dot rutledge at gmail dot com
13 years ago
It may be worth noting here that the magic constant __CLASS__ becomes even more magical - __CLASS__ will return the name of the class in which the trait is being used.for example<?phptrait sayWhere {    public function whereAmI() {        echo __CLASS__;    }}class Hello {    use sayWHere;}class World {    use sayWHere;}$a = new Hello;$a->whereAmI(); //Hello$b = new World;$b->whereAmI(); //World?>The magic constant __TRAIT__ will giev you the name of the trait
up
62
qeremy (!) gmail
10 years ago
Keep in mind; "final" keyword is useless in traits when directly using them, unlike extending classes / abstract classes.<?phptrait Foo {    final public function hello($s) { print "$s, hello!"; }}class Bar {    use Foo;    // Overwrite, no error    final public function hello($s) { print "hello, $s!"; }}abstract class Foo {    final public function hello($s) { print "$s, hello!"; }}class Bar extends Foo {    // Fatal error: Cannot override final method Foo::hello() in ..    final public function hello($s) { print "hello, $s!"; }}?>But this way will finalize trait methods as expected;<?phptrait FooTrait {    final public function hello($s) { print "$s, hello!"; }}abstract class Foo {    use FooTrait;}class Bar extends Foo {    // Fatal error: Cannot override final method Foo::hello() in ..    final public function hello($s) { print "hello, $s!"; }}?>
up
11
yeu_ym at yahoo dot com
6 years ago
Here is an example how to work with visiblity and conflicts.<?phptrait A{    private function smallTalk()    {        echo 'a';    }    private function bigTalk()    {        echo 'A';    }}trait B{    private function smallTalk()    {        echo 'b';    }    private function bigTalk()    {        echo 'B';    }}trait C{    public function smallTalk()    {        echo 'c';    }    public function bigTalk()    {        echo 'C';    }}class Talker{    use A, B, C {        //visibility for methods that will be involved in conflict resolution        B::smallTalk as public;        A::bigTalk as public;        //conflict resolution        B::smallTalk insteadof A, C;        A::bigTalk insteadof B, C;        //aliases with visibility change        B::bigTalk as public Btalk;        A::smallTalk as public asmalltalk;                //aliases only, methods already defined as public        C::bigTalk as Ctalk;        C::smallTalk as cmallstalk;    }}(new Talker)->bigTalk();//A(new Talker)->Btalk();//B(new Talker)->Ctalk();//C(new Talker)->asmalltalk();//a(new Talker)->smallTalk();//b(new Talker)->cmallstalk();//c
up
32
canufrank
8 years ago
A number of the notes make incorrect assertions about trait behaviour because they do not extend the class.So, while "Unlike inheritance; if a trait has static properties, each class using that trait has independent instances of those properties.Example using parent class:<?phpclass TestClass {    public static $_bar;}class Foo1 extends TestClass { }class Foo2 extends TestClass { }Foo1::$_bar = 'Hello';Foo2::$_bar = 'World';echo Foo1::$_bar . ' ' . Foo2::$_bar; // Prints: World World?>Example using trait:<?phptrait TestTrait {    public static $_bar;}class Foo1 {    use TestTrait;}class Foo2 {    use TestTrait;}Foo1::$_bar = 'Hello';Foo2::$_bar = 'World';echo Foo1::$_bar . ' ' . Foo2::$_bar; // Prints: Hello World?>"shows a correct example, simply adding<?phprequire_once('above');class Foo3 extends Foo2 {}Foo3::$_bar = 'news';echo Foo1::$_bar . ' ' . Foo2::$_bar . ' ' . Foo3::$_bar; // Prints: Hello news newsI think the best conceptual model of an incorporated trait is an advanced insertion of text, or as someone put it "language assisted copy and paste." If Foo1 and Foo2 were defined with $_bar, you would not expect them to share the instance. Similarly, you would expect Foo3 to share with Foo2, and it does.Viewing this way explains away a lot of  the 'quirks' that are observed above with final, or subsequently declared private vars,
up
15
rawsrc
7 years ago
About the (Safak Ozpinar / safakozpinar at gmail)'s great note, you can still have the same behavior than inheritance using trait with this approach : <?phptrait TestTrait {    public static $_bar;}class FooBar {    use TestTrait;}class Foo1 extends FooBar {}class Foo2 extends FooBar {}Foo1::$_bar = 'Hello';Foo2::$_bar = 'World';echo Foo1::$_bar . ' ' . Foo2::$_bar; // Prints: World World
up
10
balbuf
9 years ago
(It's already been said, but for the sake of searching on the word "relative"...)The "use" keyword to import a trait into a class will resolve relative to the current namespace and therefore should include a leading slash to represent a full path, whereas "use" at the namespace level is always absolute.
up
18
marko at newvibrations dot net
8 years ago
As already noted, static properties and methods in trait could be accessed directly using trait. Since trait is language assisted c/p, you should be aware that static property from trait will be initialized to the value trait property had in the time of class declaration. Example:<?phptrait Beer {    protected static $type = 'Light';    public static function printed(){        echo static::$type.PHP_EOL;    }    public static function setType($type){        static::$type = $type;    }}class Ale {    use Beer;}Beer::setType("Dark");class Lager {    use Beer;}Beer::setType("Amber");header("Content-type: text/plain");Beer::printed();  // Prints: AmberAle::printed();   // Prints: LightLager::printed(); // Prints: Dark?>
up
12
qschuler at neosyne dot com
11 years ago
Note that you can omit a method's inclusion by excluding it from one trait in favor of the other and doing the exact same thing in the reverse way.<?phptrait A {    public function sayHello()    {        echo 'Hello from A';    }    public function sayWorld()    {        echo 'World from A';    }}trait B {    public function sayHello()    {        echo 'Hello from B';    }    public function sayWorld()    {        echo 'World from B';    }}class Talker {    use A, B {        A::sayHello insteadof B;        A::sayWorld insteadof B;        B::sayWorld insteadof A;    }}$talker = new Talker();$talker->sayHello();$talker->sayWorld();?>The method sayHello is imported, but the method sayWorld is simply excluded.
up
18
Edward
13 years ago
The difference between Traits and multiple inheritance is in the inheritance part.   A trait is not inherited from, but rather included or mixed-in, thus becoming part of "this class".   Traits also provide a more controlled means of resolving conflicts that inevitably arise when using multiple inheritance in the few languages that support them (C++).  Most modern languages are going the approach of a "traits" or "mixin" style system as opposed to multiple-inheritance, largely due to the ability to control ambiguities if a method is declared in multiple "mixed-in" classes.Also, one can not "inherit" static member functions in multiple-inheritance.
up
44
ryan at derokorian dot com
13 years ago
Simple singleton trait.<?phptrait 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;
up
6
katrinaelaine6 at gmail dot com
7 years ago
Adding to "atorich at gmail dot com":The behavior of the magic constant __CLASS__ when used in traits is as expected if you understand traits and late static binding (http://php.net/manual/en/language.oop5.late-static-bindings.php).<?php$format = 'Class: %-13s | get_class(): %-13s | get_called_class(): %-13s%s';trait TestTrait {    public function testMethod() {        global $format;        printf($format, __CLASS__, get_class(), get_called_class(), PHP_EOL);    }        public static function testStatic() {        global $format;        printf($format, __CLASS__, get_class(), get_called_class(), PHP_EOL);    }}trait DuplicateTrait {    public function duplMethod() {        global $format;        printf($format, __CLASS__, get_class(), get_called_class(), PHP_EOL);    }        public static function duplStatic() {        global $format;        printf($format, __CLASS__, get_class(), get_called_class(), PHP_EOL);    }}abstract class AbstractClass {        use DuplicateTrait;        public function absMethod() {        global $format;        printf($format, __CLASS__, get_class(), get_called_class(), PHP_EOL);    }        public static function absStatic() {        global $format;        printf($format, __CLASS__, get_class(), get_called_class(), PHP_EOL);    }}class BaseClass extends AbstractClass {    use TestTrait;}class TestClass extends BaseClass { }$t = new TestClass();$t->testMethod();TestClass::testStatic();$t->absMethod();TestClass::absStatic();$t->duplMethod();TestClass::duplStatic();?>Will output:Class: BaseClass     | get_class(): BaseClass     | get_called_class(): TestClass    Class: BaseClass     | get_class(): BaseClass     | get_called_class(): TestClass    Class: AbstractClass | get_class(): AbstractClass | get_called_class(): TestClass    Class: AbstractClass | get_class(): AbstractClass | get_called_class(): TestClass    Class: AbstractClass | get_class(): AbstractClass | get_called_class(): TestClass    Class: AbstractClass | get_class(): AbstractClass | get_called_class(): TestClassSince Traits are considered literal "copying/pasting" of code, it's clear how the methods defined in DuplicateTrait give the same results as the methods defined in AbstractClass.
up
15
D. Marti
12 years ago
Traits are useful for strategies, when you want the same data to be handled (filtered, sorted, etc) differently.For example, you have a list of products that you want to filter out based on some criteria (brands, specs, whatever), or sorted by different means (price, label, whatever). You can create a sorting trait that contains different functions for different sorting types (numeric, string, date, etc). You can then use this trait not only in your product class (as given in the example), but also in other classes that need similar strategies (to apply a numeric sort to some data, etc).<?phptrait SortStrategy {    private $sort_field = null;    private function string_asc($item1, $item2) {        return strnatcmp($item1[$this->sort_field], $item2[$this->sort_field]);    }    private function string_desc($item1, $item2) {        return strnatcmp($item2[$this->sort_field], $item1[$this->sort_field]);    }    private function num_asc($item1, $item2) {        if ($item1[$this->sort_field] == $item2[$this->sort_field]) return 0;        return ($item1[$this->sort_field] < $item2[$this->sort_field] ? -1 : 1 );    }    private function num_desc($item1, $item2) {        if ($item1[$this->sort_field] == $item2[$this->sort_field]) return 0;        return ($item1[$this->sort_field] > $item2[$this->sort_field] ? -1 : 1 );    }    private function date_asc($item1, $item2) {        $date1 = intval(str_replace('-', '', $item1[$this->sort_field]));        $date2 = intval(str_replace('-', '', $item2[$this->sort_field]));        if ($date1 == $date2) return 0;        return ($date1 < $date2 ? -1 : 1 );    }    private function date_desc($item1, $item2) {        $date1 = intval(str_replace('-', '', $item1[$this->sort_field]));        $date2 = intval(str_replace('-', '', $item2[$this->sort_field]));        if ($date1 == $date2) return 0;        return ($date1 > $date2 ? -1 : 1 );    }}class Product {    public $data = array();        use SortStrategy;        public function get() {        // do something to get the data, for this ex. I just included an array        $this->data = array(            101222 => array('label' => 'Awesome product', 'price' => 10.50, 'date_added' => '2012-02-01'),            101232 => array('label' => 'Not so awesome product', 'price' => 5.20, 'date_added' => '2012-03-20'),            101241 => array('label' => 'Pretty neat product', 'price' => 9.65, 'date_added' => '2012-04-15'),            101256 => array('label' => 'Freakishly cool product', 'price' => 12.55, 'date_added' => '2012-01-11'),            101219 => array('label' => 'Meh product', 'price' => 3.69, 'date_added' => '2012-06-11'),        );    }        public function sort_by($by = 'price', $type = 'asc') {        if (!preg_match('/^(asc|desc)$/', $type)) $type = 'asc';        switch ($by) {            case 'name':                $this->sort_field = 'label';                uasort($this->data, array('Product', 'string_'.$type));            break;            case 'date':                $this->sort_field = 'date_added';                uasort($this->data, array('Product', 'date_'.$type));            break;            default:                $this->sort_field = 'price';                uasort($this->data, array('Product', 'num_'.$type));        }    }}$product = new Product();$product->get();$product->sort_by('name');echo '<pre>'.print_r($product->data, true).'</pre>';?>
up
4
bscheshirwork at gmail dot com
7 years ago
https://3v4l.org/mFuQE1. no deprecate if same-class-named method get from trait2. replace same-named method ba to aa in Ctrait ATrait {    public function a(){        return 'Aa';    }}trait BTrait {    public function a(){        return 'Ba';    }}class C {    use ATrait{        a as aa;    }    use BTrait{        a as ba;    }        public function a() {        return static::aa() . static::ba();    }}$o = new C;echo $o->a(), "\n";class D {    use ATrait{        ATrait::a as aa;    }    use BTrait{        BTrait::a as ba;    }        public function a() {        return static::aa() . static::ba();    }}$o = new D;echo $o->a(), "\n";class E {    use ATrait{        ATrait::a as aa;        ATrait::a insteadof BTrait;    }    use BTrait{        BTrait::a as ba;    }        public function e() {        return static::aa() . static::ba();    }}$o = new E;echo $o->e(), "\n";class F {    use ATrait{        a as aa;    }    use BTrait{        a as ba;    }        public function f() {        return static::aa() . static::ba();    }}$o = new F;echo $o->f(), "\n";AaAa AaBa Deprecated: Methods with the same name as their class will not be constructors in a future version of PHP; E has a deprecated constructor in /in/mFuQE on line 48 AaBa Fatal error: Trait method a has not been applied, because there are collisions with other trait methods on F in /in/mFuQE on line 65
up
13
Kristof
11 years ago
don't forget you can create complex (embedded) traits as well<?phptrait Name {  // ...}trait Address {  // ...}trait Telephone {  // ...}trait Contact {  use Name, Address, Telephone;}class Customer {  use Contact;}class Invoce {  use Contact;}?>
up
5
Carlos Alberto Bertholdo Carucce
9 years ago
If you want to resolve name conflicts and also change the visibility of a trait method, you'll need to declare both in the same line:trait testTrait{        public function test(){        echo 'trait test';    }    }class myClass{        use testTrait {        testTrait::test as private testTraitF;    }        public function test(){        echo 'class test';        echo '<br/>';        $this->testTraitF();    }    }$obj = new myClass(); $obj->test(); //prints both 'trait test' and 'class test'$obj->testTraitF(); //The method is not accessible (Fatal error: Call to private method myClass::testTraitF() )
up
4
Oddant
12 years ago
I think it's obvious to notice that using 'use' followed by the traits name must be seen as just copying/pasting lines of code into the place where they are used.
up
1
guidobelluomo at gmail dot com
4 years ago
If you override a method which was defined by a trait, calling the parent method will also call the trait's override. Therefore if you need to derive from a class which has a trait, you can extend the class without losing the trait's functionality:<?phptrait ExampleTrait{    public function output()    {        parent::output();        echo "bar<br>";    }}class Foo{    public function output()    {        echo "foo<br>";    }}class FooBar extends Foo{    use ExampleTrait;}class FooBarBaz extends FooBar{    use ExampleTrait;    public function output()    {        parent::output();        echo "baz";    }}(new FooBarBaz())->output();?>Output:foobarbaz
up
3
84td84 at gmail dot com
10 years ago
A note to 'Beispiel #9 Statische Variablen'. A trait can also have a static property:trait Counter {    static $trvar=1;    public static function stfunc() {        echo "Hello world!"    }}class C1 {    use Counter;}print "\nTRVAR: " . C1::$trvar . "\n";   //prints 1$obj = new C1();C1::stfunc();   //prints  Hello world!$obj->stfunc();   //prints Hello world!A static property (trvar) can only be accessed using the classname (C1).But a static function (stfunc) can be accessed using the classname or the instance ($obj).
up
2
cody at codysnider dot com
8 years ago
/*DocBlocks pertaining to the class or trait will NOT be carried over when applying the trait.Results trying a couple variations on classes with and without DocBlocks that use a trait with a DocBlock*/<?php/** * @Entity */trait Foo{    protected $foo;}/** * @HasLifecycleCallbacks */class Bar{    use \Foo;        protected $bar;}class MoreBar{    use \Foo;        protected $moreBar;}$w = new \ReflectionClass('\Bar');echo $w->getName() . ":\r\n";echo $w->getDocComment() . "\r\n\r\n";$x = new \ReflectionClass('\MoreBar');echo $x->getName() . ":\r\n";echo $x->getDocComment() . "\r\n\r\n";$barObj = new \Bar();$y = new \ReflectionClass($barObj);echo $y->getName() . ":\r\n";echo $y->getDocComment() . "\r\n\r\n";foreach($y->getTraits() as $traitObj) {    echo $y->getName() . " ";    echo $traitObj->getName() . ":\r\n";    echo $traitObj->getDocComment() . "\r\n";}$moreBarObj = new \MoreBar();$z = new \ReflectionClass($moreBarObj);echo $z->getName() . " ";echo $z->getDocComment() . "\r\n\r\n";foreach($z->getTraits() as $traitObj) {    echo $z->getName() . " ";    echo $traitObj->getName() . ":\r\n";    echo $traitObj->getDocComment() . "\r\n";}
up
4
artur at webprojektant dot pl
12 years ago
Trait can not have the same name as class because it will  show: Fatal error: Cannot redeclare class
To Top