Как переопределить функцию признака и вызвать ее из переопределенной функции?
Сценарий:
trait A {
function calc($v) {
return $v+1;
}
}
class MyClass {
use A;
function calc($v) {
$v++;
return A::calc($v);
}
}
print (new MyClass())->calc(2); // should print 4
Этот код не работает, и я не могу найти способ вызвать функцию признака, такую как унаследован. Я пробовал называть self::calc($v)
, static::calc($v)
, parent::calc($v)
, A::calc($v)
и следующее:
trait A {
function calc($v) {
return $v+1;
}
}
class MyClass {
use A {
calc as traitcalc;
}
function calc($v) {
$v++;
return traitcalc($v);
}
}
Ничего не работает.
Есть ли способ заставить его работать, или я должен полностью переопределить функцию признака, которая намного сложнее, чем эта:)
Ответы
Ответ 1
Ваш последний был почти там:
trait A {
function calc($v) {
return $v+1;
}
}
class MyClass {
use A {
calc as protected traitcalc;
}
function calc($v) {
$v++;
return $this->traitcalc($v);
}
}
Эта черта не является классом. Вы не можете напрямую обращаться к своим членам. Это в основном просто автоматическая копия и вставка...
Ответ 2
Если класс реализует метод напрямую, он не будет использовать версию признаков. Возможно, вы думаете о том, что:
trait A {
function calc($v) {
return $v+1;
}
}
class MyClass {
function calc($v) {
return $v+2;
}
}
class MyChildClass extends MyClass{
}
class MyTraitChildClass extends MyClass{
use A;
}
print (new MyChildClass())->calc(2); // will print 4
print (new MyTraitChildClass())->calc(2); // will print 3
Поскольку дочерние классы не реализуют метод напрямую, они сначала будут использовать свойство этого признака, если в противном случае использовать родительский класс.
Если вы хотите, черта может использовать метод в родительском классе (если вы знаете, что метод будет там), например
trait A {
function calc($v) {
return parent::calc($v*3);
}
}
// .... other code from above
print (new MyTraitChildClass())->calc(2); // will print 8 (2*3 + 2)
Вы также можете указать способы переопределения, но по-прежнему обращаться к методу признаков следующим образом:
trait A {
function trait_calc($v) {
return $v*3;
}
}
class MyClass {
function calc($v) {
return $v+2;
}
}
class MyTraitChildClass extends MyClass{
use A {
A::trait_calc as calc;
}
}
class MySecondTraitChildClass extends MyClass{
use A {
A::trait_calc as calc;
}
public function calc($v) {
return $this->trait_calc($v)+.5;
}
}
print (new MyTraitChildClass())->calc(2); // will print 6
echo "\n";
print (new MySecondTraitChildClass())->calc(2); // will print 6.5
Вы можете видеть, что он работает на http://sandbox.onlinephpfunctions.com/code/e53f6e8f9834aea5e038aec4766ac7e1c19cc2b5
Ответ 3
Альтернативный подход, если он заинтересован, - с дополнительным промежуточным классом, чтобы использовать обычный способ OOO. Это упрощает использование с помощью parent:: methodname
trait A {
function calc($v) {
return $v+1;
}
}
// an intermediate class that just uses the trait
class IntClass {
use A;
}
// an extended class from IntClass
class MyClass extends IntClass {
function calc($v) {
$v++;
return parent::calc($v);
}
}
Ответ 4
Использование другого признака:
trait ATrait {
function calc($v) {
return $v+1;
}
}
class A {
use ATrait;
}
trait BTrait {
function calc($v) {
$v++;
return parent::calc($v);
}
}
class B extends A {
use BTrait;
}
print (new B())->calc(2); // should print 4