444 votes

Comment surcharger une fonction de trait et l'appeler à partir de la fonction surchargée ?

Scénario :

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

Ce code ne fonctionne pas, et je ne trouve pas de moyen d'appeler une fonction de trait comme si elle était héritée. J'ai essayé d'appeler self::calc($v) , static::calc($v) , parent::calc($v) , A::calc($v) et les suivantes :

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

class MyClass {
    use A {
        calc as traitcalc;
    }

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

Rien ne fonctionne.

Existe-t-il un moyen de le faire fonctionner ou dois-je remplacer complètement la fonction trait qui est beaucoup plus complexe que celle-ci :)

771voto

ircmaxell Points 74865

Votre dernière était presque là :

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

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

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

Le trait n'est pas une classe. Vous ne pouvez pas accéder directement à ses membres. C'est essentiellement un copier-coller automatique...

36 votes

Juste pour clarifier - une fois que votre classe définit la même méthode, elle remplace automatiquement celle du trait. Le trait remplit la méthode comme @ircmaxell le mentionne lorsqu'elle est vide.

1 votes

Pour des raisons encore inconnues, cela a totalement perturbé un trait avec lequel je l'ai appliqué. Les autres méthodes utilisées par le trait peuvent ne pas fonctionner comme prévu lorsque vous faites cela.

3 votes

@PhillipWhelan Ce serait bien si vous pouviez ajouter plus d'informations sur ce qui "ne fonctionne pas comme prévu". Le fait d'écrire cela n'aide pas beaucoup à comprendre à quel type de comportement erroné il faut s'attendre, et ne nous assure pas qu'il ne s'agit pas d'une erreur temporaire de votre part. Peut-être y a-t-il une question de l'OS sur le problème dont vous parlez ? (Eventuellement) Merci.

22voto

Yehosef Points 3761

Si la classe implémente directement la méthode, elle n'utilisera pas la version traits. Peut-être que ce à quoi vous pensez est :

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

Comme les classes enfants n'implémentent pas directement la méthode, elles utiliseront d'abord celle du trait s'il y a lieu d'utiliser celle de la classe parent.

Si vous le souhaitez, le trait peut utiliser une méthode de la classe parente (en supposant que vous sachiez que la méthode s'y trouve), par ex.

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)

Vous pouvez également prévoir des moyens de contourner la méthode du trait, tout en y accédant, comme suit :

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

Vous pouvez voir son fonctionnement à http://sandbox.onlinephpfunctions.com/code/e53f6e8f9834aea5e038aec4766ac7e1c19cc2b5

13voto

Kartik V Points 437

Une approche alternative si vous êtes intéressés - avec une classe intermédiaire supplémentaire pour utiliser la méthode OOO normale. Cela simplifie l'utilisation avec parent::nom de la méthode

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

4voto

tarkhov Points 254

En utilisant un autre trait :

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

4voto

Gannet Points 351

Une autre variante : Définissez deux fonctions dans le trait, une fonction protégée qui exécute la tâche réelle et une fonction publique qui appelle à son tour la fonction protégée.

Cela évite aux classes de devoir utiliser l'instruction "use" si elles veulent remplacer la fonction, puisqu'elles peuvent toujours appeler la fonction protégée en interne.

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

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

class MyClass {
    use A;

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

class MyOtherClass {
    use A;
}

print (new MyClass())->calc(2); // will print 4

print (new MyOtherClass())->calc(2); // will print 3

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X