Як перекрити функцію ознаки та викликати її з перекритої функції?


369

Сценарій:

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

Нічого не працює.

Чи є спосіб змусити його працювати чи потрібно повністю перекрити функцію, яка є набагато складнішою за цю :)

Відповіді:


640

Ваш останній був майже там:

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

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

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

Характер не клас. Ви не можете отримати доступ до його членів безпосередньо. Це в основному лише автоматизована копія та вставка ...


20
просто для уточнення - як тільки ваш клас визначає той самий метод, він автоматично переосмислює ознаки. Ця характеристика заповнює метод, як @ircmaxell згадує, коли він порожній.
Yehosef

2
@PhillipWhelan було б добре, якби ви могли додати більше інформації про те, що "працює не так, як очікувалося". Написане так, що це не дуже допомагає зрозуміти, якої неправильної поведінки очікувати, і не запевняє нас, що це не є тимчасовою помилкою вас. Можливо, є якесь питання щодо питання, про яке ви говорите? (Врешті-решт) Спасибі
Камафезер

1
Проблема в тому, що всі інші методи в ознаку більше не включаються.
мальхал

2
Для довідки: Якщо функція вашої функції буде статичною, ви можете отримати доступ до функції, зателефонувавшиA::calc(1)
velop

4
Як згадував Філліп (я думаю), як би ви це зробили для одного методу ознаки, в той же час включаючи всі інші методи тієї ж ознаки, що і звичайні? Переважно без явного посилання на кожен метод.
Gannet

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 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


8

Альтернативний підхід, якщо зацікавлений - з додатковим проміжним класом для використання звичайного способу OOO. Це спрощує використання з именем parent :: method

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 () безпосередньо в цьому класі.
marcini

Це абсолютно не буде працювати для мене. 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.