Come sovrascrivere la funzione tratto e chiamarla dalla funzione ignorato?


370

Scenario:

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

Questo codice non funziona e non riesco a trovare un modo per chiamare una funzione tratto come se fosse ereditata. Ho provato a chiamare self::calc($v), static::calc($v), parent::calc($v), A::calc($v)e il seguente:

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

class MyClass {
    use A {
        calc as traitcalc;
    }

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

Niente funziona.

C'è un modo per farlo funzionare o devo scavalcare completamente la funzione tratto che è molto più complessa di così :)

Risposte:


641

Il tuo ultimo era quasi arrivato:

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

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

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

Il tratto non è una classe. Non puoi accedere direttamente ai suoi membri. Fondamentalmente è solo copia e incolla automatizzate ...


20
solo per chiarire - una volta che la tua classe definisce lo stesso metodo, sostituisce automaticamente quella del tratto. Il tratto riempie il metodo come menziona @ircmaxell quando è vuoto.
Yehosef,

2
@PhillipWhelan sarebbe bello se potessi aggiungere ulteriori informazioni su ciò che "non funziona come previsto". Scritto in questo modo non aiuta molto a capire che tipo di comportamento sbagliato aspettarsi, e non ci assicura che questo non sia un tuo errore temporaneo. Forse c'è qualche domanda SO sul problema di cui stai parlando? (Alla fine) Grazie.
Kamafeather,

1
Il problema è che tutti gli altri metodi nel tratto non sono più inclusi.
Malhal,

2
Solo per riferimento: se la tua funzione tratto fosse statica potresti accedere alla funzione chiamandoA::calc(1)
velop

4
Come ha detto Phillip (penso), come faresti per un metodo di un tratto pur includendo tutti gli altri metodi dello stesso tratto come al solito? Preferibilmente senza fare esplicito riferimento a ciascun metodo.
Gannet

14

Se la classe implementa direttamente il metodo, non utilizzerà la versione dei tratti. Forse quello a cui stai pensando è:

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

Poiché le classi figlio non implementano direttamente il metodo, useranno prima quella del tratto se altrimenti usano quella della classe genitore.

Se lo desideri, il tratto può usare il metodo nella classe genitore (supponendo che tu sappia che il metodo sarebbe lì) ad es

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)

Puoi anche fornire modi per sovrascrivere, ma comunque accedere al metodo tratto come segue:

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

Puoi vederlo funzionare su http://sandbox.onlinephpfunctions.com/code/e53f6e8f9834aea5e038aec4766ac7e1c19cc2b5


8

Un approccio alternativo se interessato - con una classe intermedia aggiuntiva per utilizzare il normale modo OOO. Questo semplifica l'utilizzo con 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
Questo approccio consentirà di ottenere qualsiasi vantaggio utilizzando traits. Come combinare più tratti in più classi (ad esempio tratto A, B in una classe, tratto B, C, D in un'altra classe, tratto A, C in un'altra classe e così via)
Ionuț Staicu

3
No, usando questo approccio hai ancora i vantaggi di avere un tratto. Puoi usare questa caratteristica in IntClass, ma puoi anche usarla in molte altre classi se vuoi. Il tratto sarà inutile, se utilizzato solo in IntClass. In tal caso, sarebbe meglio posizionare il metodo calc () direttamente in quella classe.
marcini,

Questo non avrebbe funzionato per me. ScreenablePerson::save()esiste, Candidateusa Validatingtratti e si estende ScreenablePersone tutte e tre le classi hanno save().
Theodore R. Smith,

1

Utilizzando un altro tratto:

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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.