Reputation: 9798
How can I launch constructor and destructor defined by trait along with the class' constructor and destructor as well. For example,
trait Audit
{
public function __construct()
{
parent::__construct(); // Doesn't work...
$this->_name = __CLASS__;
$this->AuditAction('Started');
}
public function __destruct()
{
parent::__destruct(); // Doesn't work...
$this->AuditAction('Ended');
echo $this->_log;
}
public function AuditAction($n)
{
$this->_log .= $this->GetCurrentTimeStamp() . ' ' . $this->_name . ": $n" . PHP_EOL;
}
private function GetCurrentTimeStamp()
{
return (new DateTime())->format('[Y-m-d H:i:s]');
}
private $_name, $_log = '';
}
class C
{
use Audit;
public function __construct()
{
}
public function __destruct()
{
}
}
$c = new C();
I should get couple of lines of text but I get none since class C's constructor is explicitly called instead. Is there a way to achieve this?
Upvotes: 9
Views: 6997
Reputation: 316969
The constructor and destructor of C will have precedence over the trait constructor and destructor when the class is composed:
An inherited member from a base class is overridden by a member inserted by a Trait. The precedence order is that members from the current class override Trait methods, which in return override inherited methods.
Source: http://php.net/traits
In other words, remove the empty constructor and destructor from C and the trait's constructor and destructor will be used. There is no way to make this work with both, C and the traits, constructor and destructor, because traits do not work like regular inheritance.
In general, I would advise against giving Traits their own constructors or destructors because you cannot instantiate Traits. You instantiate from a class having a Trait and that class should be in control. Consider adding an onCreate()
or onDestroy()
method to the trait instead and call these from the appropriate magic methods on C. You could achieve the same by aliasing the traits __construct in C, but I think this waters down semantics.
Upvotes: 7
Reputation: 31
Addition to Aeremdir's answer: inherited constructors from base class and multiple traits...
<?php
trait T1 {
public function __construct() {
$this->someval |= 0b0001;
echo "T1::__construct() done\n";
}
}
trait T2 {
public function __construct() {
$this->someval |= 0b0010;
echo "T2::__construct() done\n";
}
}
class C1 {
protected $someval;
public function __construct() {
$this->someval = 0b10000000;
echo "C1::__construct() done\n";
}
}
class C2 extends C1 {
use T1, T2 {
T1::__construct as private T1__construct;
T2::__construct as private T2__construct;
}
public function __construct() {
parent::__construct();
$this->T1__construct();
$this->T2__construct();
$this->someval |= 0b00100000;
echo "C2::__construct() done\n";
}
public function someval() {
$str = base_convert($this->someval, 10, 2);
$len = strlen($str);
if($len < 8)
$str = str_repeat('0', 8 - $len) . $str;
return '0b' . $str;
}
}
$v1 = new C2();
echo $v1->someval();
?>
under PHP 7.0.5 this code results in...
C1::__construct() done
T1::__construct() done
T2::__construct() done
C2::__construct() done
0b10100011
Upvotes: 3
Reputation: 6379
You have to define a custom name for the methods of the Trait
. Then you can call those method from your class.
As the methods of the class overwrite the methods of the Trait
you have to assign a different name to the methods of the Trait
:
class C
{
use Audit {
Audit::__construct as auditConstruct;
Audit::__destruct as auditDestruct;
}
public function __construct()
{
$this->auditConstruct();
}
public function __destruct()
{
$this->auditDestruct();
}
}
Upvotes: 3