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

370

Сценарий:

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);
    }
}

Ничего не работает

Есть ли способ заставить его работать или я должен полностью переопределить функцию trait, которая намного сложнее, чем эта :)

Shu
источник

Ответы:

641

Ваш последний был почти там:

trait A {
    function calc($v) {
        return $v+1;
    }
}

class MyClass {
    use A {
        calc as protected traitcalc;
    }

    function calc($v) {
        $v++;
        return $this->traitcalc($v);
    }
}

Черта не класс. Вы не можете получить доступ к его членам напрямую. Это в основном просто автоматическое копирование и вставка ...

ircmaxell
источник
20
просто чтобы уточнить - как только ваш класс определяет тот же метод, он автоматически переопределяет черту. Признак заполняет метод, поскольку @ircmaxell упоминает, когда он пуст.
Yehosef
2
@PhillipWhelan было бы неплохо, если бы вы могли добавить больше информации о том, что «работает не так, как ожидалось». Написанное таким образом, это не очень помогает понять, какого рода неправильное поведение ожидать, и не гарантирует нам, что это не временная ошибка с вашей стороны. Может быть, есть какой-то вопрос о проблеме, о которой вы говорите? (В конце концов) Спасибо.
Kamafeather
1
Проблема в том, что все остальные методы в черту больше не включены.
Малхал
2
Просто для справки: Если функция черты будет статичной вы могли бы получить доступ к функции по вызывающемуA::calc(1)
ворачивается
4
Как упоминал Филлип (я думаю), как бы вы сделали это для одного метода черты, в то же время включив все другие методы той же черты, что и в обычном режиме? Желательно без явной ссылки на каждый метод.
Ганнет
14

Если класс реализует метод напрямую, он не будет использовать версию признаков. Возможно, вы думаете о:

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 следующим образом:

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

Yehosef
источник
8

Альтернативный подход, если интересно - с дополнительным промежуточным классом использовать обычный способ ООО. Это упрощает использование с 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);
    }
}
Картик В
источник
6
Этот подход побрит любое ваше преимущество, используя traits. Как объединение нескольких черт в нескольких классах (например, черта A, B в классе, черта B, C, D в другом классе, черта A, C в другом классе и т. Д.)
Ionuț Staicu
3
Нет, используя этот подход, вы все еще обладаете преимуществами наличия черты. Вы можете использовать эту черту в IntClass, но вы также можете использовать ее во многих других классах, если хотите. Черта будет бесполезна, если она использовалась только в IntClass. В этом случае было бы лучше поместить метод calc () непосредственно в этот класс.
Марчини
Это совершенно не будет работать для меня. ScreenablePerson::save()существует, Candidateиспользует Validatingчерту и расширяет ScreenablePerson, и все три класса имеют save().
Теодор Р. Смит
1

Используя другую черту:

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
Тархов
источник