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

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


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


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

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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.