Как переопределить функцию признака и вызвать ее из переопределенной функции?

Сценарий:

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