// trait Pprint { public function whoAmI() { return get_class($this) . ': ' . (string) $this; } } class Human { use Pprint; // , use protected $_name = 'unknown'; public function __construct($name) { $this->_name = $name; } public function __toString() { return (string) $this->_name; } } $a = new Human('Nikita'); echo $a->whoAmI(), PHP_EOL; //=> Human: Nikita
Pprint
type has been added to the Human
class.use
constructs within the class definition. use
can be specified anywhere in the class.{...}
) after use
you can:Trait::method as myMethod
- the method
from Trait will be additionally available, like myMethod
);TraitA::method insteadof TraitB
- the TraitA
method will be used instead of the TraitB
method of the same name);Trait::publicMethod as protected
), you can immediately rename it ( Trait::publicMethod as protected _myProtectedMethod
).trait
and may include other types, by specifying them in the use
keyword. The syntax and capabilities are similar to use
in a class. trait Pprint { public function whoAmI() { return get_class($this) . ': ' . (string) $this; } } trait Namer { // use Pprint; public function getMyName() { return $this->whoAmI(); } public function getMyLastName() { return 'Unknown =('; } public function getMyNickname() { return preg_replace('/[^az]+/i', '_', strtolower($this->getMyName())); } } trait SuperNamer { public function getMyLastName() { return 'Ask me'; } } class Human { use SuperNamer; use Namer { SuperNamer::getMyLastName insteadof Namer; Namer::getMyNickname as protected _getMyLogin; } protected $_name = 'unknown'; public function __construct($name) { $this->_name = $name; } public function __toString() { return (string) $this->_name; } public function getLogin() { return $this->_getMyLogin(); } } $a = new Human('Nikita'); echo join(', ', get_class_methods($a)), PHP_EOL; //__construct, __toString, getLogin, getMyLastName, //getMyName, getMyNickname, whoAmI echo $a->getMyName(), PHP_EOL; //Human: Nikita echo $a->getMyLastName(), PHP_EOL; //Ask me echo $a->getLogin(), PHP_EOL; //human_nikita echo $a->getMyNickname(), PHP_EOL; //human_nikita
use
seems to be associated with the type around which it is described, but it is not. The rules in the block are global and can be declared anywhere. // use SuperNamer, Namer, Singleton, SomeOther { SuperNamer::getMyLastName insteadof Namer; SomeOther::getSomething as private; } // use Namer; use Singleton; use SuperNamer { SuperNamer::getMyLastName insteadof Namer; } use SomeOther { SomeOther::getSomething as private; }
getMyNickname
remained in the list, and _getMyLogin
just its alias with reduced access. You can eliminate the original method at all, but more on that below in the section of magic. if ($isWin) { trait A { /* … */} } else { trait A { /* … */} }
trait WithId { protected $_id = null; public function getId() { return $this->_id; } public function setId($id) { $this->_id = $id; } }
_id
property in the type conflict with that used in the class or its descendants, the properties of the types write with the prefixes: trait WithId { protected $_WithId_id = null; protected $_WithId_checked = false; //... public function getId() { return $this->_WithId_id; } public function setId($id) { $this->_WithId_id = $id; } }
whoAmI
method into the Human
class, respectively, all calls to parent
, self
, $this
will work as well as the call in the class methods. The exception will be some magic constants, for example inside whoAmI
__METHOD__ === 'Pprint :: whoAmI'.$this->_name
, instead of calling __toString
. However, it is worth thinking a few times before doing this, since on complex implementations this will cause quite a bit of confusion. I would recommend always using clear methods, if necessary, even describe them in the interface and "force" classes to implement it. trait A { public function abc() {} } trait B { public function abc() {} } class C { use A, B; } //Fatal error: Trait method abc has not been applied, //because there are collisions with other trait methods //on C in %FILE% on line %line%
To the rescue comes insteadof
, through which you will need to resolve all conflicts. trait A { public function abc() {} } trait B { public function abc() {} } class C { use A, B; public function abc() {} } //OK
Sometime later, by transferring the abc
method to the parent class, we get a strange error on the collision of type methods, which can be confusing. So, it is better to resolve conflicts in advance. ( On the other hand, if the type and class methods in the code are the same, something is probably wrong. ) trait WithId { protected $_id = false; //protected $_var = 'a'; public function getId() { return $this->_id; } //... } trait WithId2 { protected $_id = null; //protected $_var = null; //... } class A { use WithId, WithId2; } class B { use WithId2, WithId; } class C { use WithId; protected $_id = '0'; } // $a = new A(); var_dump($a->getId()); //NULL $b = new B(); var_dump($b->getId()); //false $c = new C(); var_dump($c->getId()); //false (!) // $_var // WithId and WithId2 define the same property ($_var) // in the composition of A. However, the definition differs // and is considered incompatible. Class was composed // in %FILE% on line %LINE%
A
, getId
was NULL, and in class B
, false. In this case, class properties are considered lower than the type property (with methods equal to the opposite) and in C
instead of the expected '0', we get false. var_dump(null == false); //true var_dump('0' == false); //true var_dump('a' == null); //false
<?php trait Slug { public function error() { echo $this->a; //5 } public function someMethod() { $this->error(); } public function testExc() { throw new Exception('Test'); //16 } } class Brain { use Slug; public function plurk() { $this->testExc(); //25 } } error_reporting(E_ALL); $b = new Brain(); $b->someMethod(); //Notice: Undefined property: Brain::$a in %FILE% on line 5 try { $b->plurk(); //35 } catch(Exception $e) { echo $e; } // exception 'Exception' with message 'Test' in %FILE%:16 // Stack trace: // #0 %FILE%(25): Brain->testExc() // #1 %FILE%(35): Brain->plurk() // #2 {main}
trait A { public function a() {} public function b() {} } trait B { public function d() { $this->e(); } public function e() {} } class C { use A { // A::b insteadof A; A::b as c; } use B { // B::e insteadof B; } } echo join(", ", get_class_methods('C')), PHP_EOL; //a, c, d
$c = new C(); $c->d(); //Fatal error: Call to undefined method C::e()
trait Namer { public function getName() { return 'Name'; } } trait Namer2 { public function getName() { return 'Name2'; } } trait Supernamer { use Namer, Namer2 { Namer::getName insteadof Namer; Namer::getName as protected _Namer_getName_; Namer2::getName insteadof Namer2; Namer2::getName as protected _Namer2_getName_; } public function getName() { return $this->_Namer_getName_() . $this->_Namer2_getName_(); } }
trait Singleton { static public function getInstance() { $class = get_called_class(); // static:: if (!Storage::hasInstance($class)) { $new = new static(); Storage::setInstance($class, $new); } return Storage::getInstance($class); } }
static
when declaring a variable. These variables should retain their value when the method is called, but apparently the structure for storing these variables is initialized at each place where the method is used. The result is such a scheme: trait Singleton { static public function getInstance() { static $instance = null; if ($instance === null) { $instance = new static(); } return $instance; } } class MyClass { use Singleton; } class MyExtClass extends MyClass {} echo get_class(MyClass::getInstance()), PHP_EOL; //MyClass echo get_class(MyExtClass::getInstance()), PHP_EOL; //MyExtClass
Source: https://habr.com/ru/post/130000/
All Articles