Wie überschreibe ich die Trait-Funktion und rufe sie von der überschriebenen Funktion aus auf?

370

Szenario:

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

Dieser Code funktioniert nicht und ich kann keine Möglichkeit finden, eine Merkmalsfunktion aufzurufen, wie sie geerbt wurde. Ich habe versucht Aufruf self::calc($v), static::calc($v), parent::calc($v), A::calc($v)und die folgenden:

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

class MyClass {
    use A {
        calc as traitcalc;
    }

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

Nichts funktioniert.

Gibt es eine Möglichkeit, es zum Laufen zu bringen, oder muss ich die Trait-Funktion, die viel komplexer als diese ist, vollständig überschreiben :)

Shu
quelle

Antworten:

641

Dein letzter war fast da:

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

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

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

Das Merkmal ist keine Klasse. Sie können nicht direkt auf seine Mitglieder zugreifen. Es ist im Grunde nur automatisiertes Kopieren und Einfügen ...

ircmaxell
quelle
20
Nur zur Verdeutlichung: Sobald Ihre Klasse dieselbe Methode definiert, überschreibt sie automatisch die Merkmale. Das Merkmal füllt die Methode aus, wie @ircmaxell erwähnt, wenn sie leer ist.
Yehosef
2
@PhillipWhelan wäre schön, wenn Sie weitere Informationen darüber hinzufügen könnten, was "nicht wie erwartet funktioniert". So geschrieben hilft es nicht viel zu verstehen, welche Art von falschem Verhalten zu erwarten ist, und versichert uns nicht, dass dies kein vorübergehender Fehler von Ihnen ist. Vielleicht gibt es eine SO-Frage zu dem Thema, über das Sie sprechen? (Irgendwann) Danke.
Kamafeather
1
Das Problem ist, dass alle anderen Methoden im Merkmal nicht mehr enthalten sind.
Malhal
2
Nur als Referenz: Wenn Ihre Merkmalsfunktion statisch wäre, können Sie auf die Funktion zugreifen, indem SieA::calc(1)
velop
4
Wie würden Sie dies, wie Phillip erwähnte (glaube ich), für eine Methode eines Merkmals tun, während Sie alle anderen Methoden desselben Merkmals wie normal einbeziehen? Am besten ohne explizite Bezugnahme auf jede Methode.
Gannet
14

Wenn die Klasse die Methode direkt implementiert, wird die Merkmalsversion nicht verwendet. Vielleicht denken Sie an:

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

Da die untergeordneten Klassen die Methode nicht direkt implementieren, verwenden sie zuerst die des Merkmals, wenn andernfalls die der übergeordneten Klasse verwendet wird.

Wenn Sie möchten, kann das Merkmal eine Methode in der übergeordneten Klasse verwenden (vorausgesetzt, Sie wissen, dass die Methode vorhanden ist), z

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)

Sie können auch Möglichkeiten zum Überschreiben bereitstellen, aber dennoch wie folgt auf die Trait-Methode zugreifen:

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

Sie können es unter http://sandbox.onlinephpfunctions.com/code/e53f6e8f9834aea5e038aec4766ac7e1c19cc2b5 sehen

Yehosef
quelle
8

Ein alternativer Ansatz bei Interesse - mit einer zusätzlichen Zwischenklasse, um den normalen OOO-Weg zu verwenden. Dies vereinfacht die Verwendung mit 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);
    }
}
Kartik V.
quelle
6
Dieser Ansatz spart jeden Vorteil, den Sie durch die Verwendung von traits haben. Wie das Kombinieren mehrerer Merkmale in mehreren Klassen (z. B. Merkmal A, B in einer Klasse, Merkmal B, C, D in einer anderen Klasse, Merkmal A, C in einer anderen Klasse usw.)
Ionuț Staicu
3
Nein, mit diesem Ansatz haben Sie immer noch die Vorteile eines Merkmals. Sie können dieses Merkmal in IntClass verwenden, aber Sie können es auch in vielen anderen Klassen verwenden, wenn Sie möchten. Das Merkmal ist nutzlos, wenn es nur in IntClass verwendet wurde. In diesem Fall ist es besser, die Methode calc () direkt in diese Klasse einzufügen.
Marcini
Das würde bei mir überhaupt nicht funktionieren. ScreenablePerson::save()existiert, Candidateverwendet ValidatingMerkmal und erweitert ScreenablePerson, und alle drei Klassen haben save().
Theodore R. Smith
1

Verwenden eines anderen Merkmals:

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