Jak zastąpić funkcję cechy i wywołać ją z funkcji zastąpionej?

369

Scenariusz:

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

Ten kod nie działa i nie mogę znaleźć sposobu na wywołanie funkcji cechy, tak jak została odziedziczona. Próbowałem nazywając self::calc($v), static::calc($v), parent::calc($v), A::calc($v)i są następujące:

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

class MyClass {
    use A {
        calc as traitcalc;
    }

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

Nic nie działa.

Czy istnieje sposób, aby to działało, czy muszę całkowicie zastąpić funkcję cechy, która jest o wiele bardziej złożona niż to :)

Shu
źródło

Odpowiedzi:

640

Twój ostatni był już prawie na miejscu:

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

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

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

Ta cecha nie jest klasą. Nie możesz uzyskać dostępu do jego członków bezpośrednio. To po prostu automatyczne kopiowanie i wklejanie ...

ircmaxell
źródło
20
dla wyjaśnienia - gdy klasa zdefiniuje tę samą metodę, automatycznie zastępuje tę cechę. Cecha wypełnia metodę, jak wspomina @ircmaxell, gdy jest pusta.
Yehosef
2
@PhillipWhelan byłby miły, gdybyś mógł dodać więcej informacji o tym, co „nie działa zgodnie z oczekiwaniami”. Napisane w ten sposób niewiele pomaga zrozumieć, jakiego rodzaju złego zachowania można się spodziewać, i nie zapewnia nas, że nie jest to twój tymczasowy błąd. Może jest jakieś SO pytanie dotyczące problemu, o którym mówisz? (W końcu) Dzięki.
Kamafeather
1
Problem polega na tym, że wszystkie inne metody tej cechy nie są już uwzględnione.
malhal
2
Tylko w celach informacyjnych: Jeśli twoja funkcja cechy byłaby statyczna, możesz uzyskać do niej dostęp, wywołującA::calc(1)
velop
4
Jak wspomniał Phillip (tak myślę), jak zrobiłbyś to dla jednej metody cechy, jednocześnie włączając wszystkie inne metody tej samej cechy jak normalnie? Najlepiej bez wyraźnego odniesienia do każdej metody.
Gannet
14

Jeśli klasa implementuje metodę bezpośrednio, nie będzie używać wersji cech. Być może chodzi ci o:

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

Ponieważ klasy potomne nie implementują metody bezpośrednio, najpierw wykorzystają tę cechę, jeśli w przeciwnym razie skorzystają z cechy nadrzędnej.

Jeśli chcesz, cecha może użyć metody w klasie nadrzędnej (zakładając, że wiesz, że metoda tam będzie) np

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)

Możesz także podać sposoby zastąpienia, ale nadal uzyskujesz dostęp do metody cechy w następujący sposób:

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

Możesz zobaczyć, jak działa na http://sandbox.onlinephpfunctions.com/code/e53f6e8f9834aea5e038aec4766ac7e1c19cc2b5

Yehosef
źródło
8

Alternatywne podejście w razie zainteresowania - z dodatkową klasą pośrednią do korzystania z normalnego sposobu OOO. Upraszcza to korzystanie z 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.
źródło
6
Takie podejście pozwoli Ci uzyskać przewagę dzięki traits. Jak łączenie wielu cech w wielu klasach (np. Cecha A, B w klasie, cecha B, C, D w innej klasie, cecha A, C w innej klasie i tak dalej)
Ionuț Staicu
3
Nie, stosując to podejście nadal masz zalety posiadania cechy. Możesz użyć tej cechy w IntClass, ale możesz także użyć jej w wielu innych klasach, jeśli chcesz. Cecha będzie bezużyteczna, jeśli zostanie użyta tylko w IntClass. W takim przypadku lepiej byłoby umieścić metodę calc () bezpośrednio w tej klasie.
marcini,
To zupełnie by mi nie działało. ScreenablePerson::save()istnieje, Candidateużywa Validatingcechy i rozszerza ScreenablePerson, i wszystkie trzy klasy mają save().
Theodore R. Smith,
1

Używając innej cechy:

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
Tarkow
źródło