Using AS on a __construct method (and maybe other magic methods) is really, really bad. The problem is that is doesn't throw any errors, at least in 5.4.0. It just sporadically resets the connection. And when I say "sporadically," I mean that arbitrary changes in the preceding code can cause the browser connection to reset or not reset *consistently*, so that subsequent page refreshes will continue to hang, crash, or display perfectly in the same fashion as the first load of the page after a change in the preceding code, but the slightest change in the code can change this state. (I believe it is related to precise memory usage.)
I've spent a good part of the day chasing down this one, and weeping every time commenting or even moving a completely arbitrary section of code would cause the connection to reset. It was just by luck that I decided to comment the
"__construct as primitiveObjectConstruct"
line and then the crashes went away entirely.
My parent trait constructor was very simple, so my fix this time was to copy the functionality into the child __construct. I'm not sure how I'll approach a more complicated parent trait constructor.
Traits
Seit PHP 5.4.0 gibt es in PHP eine Methode der Wiederverwendung von Code, die Traits genannt wird.
Traits ist ein Mechanismus zur Wiederverwendung von Code, der in Programmiersprachen mit einfacher Vererbung wie PHP verwendet wird. Ein Trait kann verwendet werden die Beschränkungen der einfachen Vererbung aufzuweichen 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 ezcReflectionReturnInfo {
function getReturnType() { /*1*/ }
function getReturnDescription() { /*2*/ }
}
class ezcReflectionMethod extends ReflectionMethod {
use ezcReflectionReturnInfo;
/* ... */
}
class ezcReflectionFunction extends ReflectionFunction {
use ezcReflectionReturnInfo;
/* ... */
}
?>
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 eine der Methoden des Konflikts wieder unter einem anderen Namen hinzuzufügen.
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 { doHelloWorld as private sayHello }
}
?>
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 aufzudrängen.
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
Innerhalb von Traits kann auf statische Variablen zugegriffen werden, aber diese können nicht in Traits definiert werden. Statische Methoden können hingegen auch durch Traits für die darstellende Klasse definiert werden.
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(); // echo 1
$p = new C2(); $p->inc(); // echo 1
?>
Beispiel #10 Statische Methoden
<?php
trait StaticExample {
public static function doSomething() {
return 'Tue etwas';
}
}
class Example {
use StaticExample;
}
Example::doSomething();
?>
Attribute
Traits können ebenfalls Attribute definieren.
Beispiel #11 Attribute definieren
<?php
trait PropertiesTrait {
public $x = 1;
}
class PropertiesExample {
use PropertiesTrait;
}
$example = new PropertiesExample;
$example->x;
?>
Ein Attribut, welches durch einen Trait definiert wurde, kann nicht von der
Klasse erneut definiert werden. Wenn die Attribute kompatibel sind, d.h. die selbe
Sichtbarkeit und den selben Standardwert besitzen, wird ein E_STRICT
Fehler geworfen. Anderfalls wird ein Fatal Error geworfen.
Beispiel #12 Konfliktauflösung
<?php
trait PropertiesTrait {
public $same = true;
public $different = false;
}
class PropertiesExample {
use PropertiesTrait;
public $same = true; // Strict Standards
public $different = true; // Fatal error
}
?>
Simple singleton trait.
<?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;
Traits can not implement interfaces.
(should be obvious, but tested is tested)
Unlike inheritance; if a trait has static properties, each class using that trait has independent instances of those properties.
Example using parent class:
<?php
class 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:
<?php
trait 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
?>
A (somewhat) practical example of trait usage.
Without traits:
<?php
class Controller {
/* Controller-specific methods defined here. */
}
class AdminController extends Controller {
/* Controller-specific methods inherited from Controller. */
/* Admin-specific methods defined here. */
}
class CrudController extends Controller {
/* Controller-specific methods inherited from Controller. */
/* CRUD-specific methods defined here. */
}
class AdminCrudController extends CrudController {
/* Controller-specific methods inherited from Controller. */
/* CRUD-specific methods inherited from CrudController. */
/* (!!!) Admin-specific methods copied and pasted from AdminController. */
}
?>
With traits:
<?php
class Controller {
/* Controller-specific methods defined here. */
}
class AdminController extends Controller {
/* Controller-specific methods inherited from Controller. */
/* Admin-specific methods defined here. */
}
trait CrudControllerTrait {
/* CRUD-specific methods defined here. */
}
class AdminCrudController extends AdminController {
use CrudControllerTrait;
/* Controller-specific methods inherited from Controller. */
/* Admin-specific methods inherited from AdminController. */
/* CRUD-specific methods defined by CrudControllerTrait. */
}
?>
<?php
trait first_trait
{
function first_function()
{
echo "From First Trait";
}
}
trait second_trait
{
function first_function()
{
echo "From Second Trait";
}
}
class first_class
{
use first_trait, second_trait
{
// This class will now call the method
// first function from first_trait only
first_trait::first_function insteadof second_trait;
// first_function of second_traits can be
// accessed with second_function
second_trait::first_function as second_function;
}
}
$obj = new first_class();
// Output: From First Trait
$obj->first_function();
// Output: From Second Trait
$obj->second_function();
?>
the iranian php programmer
writer: farhad zand
farhad.peb@gmail.com
php_engineer_bk@yahoo.com
The magic method __call works as expected using traits.
<?php
trait Call_Helper{
public function __call($name, $args){
return count($args);
}
}
class Foo{
use Call_Helper;
}
$foo = new Foo();
echo $foo->go(1,2,3,4); // echoes 4
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.
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.
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
<?php
trait 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
