¿Cómo anular la función de rasgo y llamarla desde la función anulada?

370

Guión:

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

Este código no funciona, y no puedo encontrar una manera de llamar a una función de rasgo como si se heredara. Traté de llamar self::calc($v), static::calc($v), parent::calc($v), A::calc($v)y los siguientes:

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

class MyClass {
    use A {
        calc as traitcalc;
    }

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

Nada funciona.

¿Hay alguna manera de hacerlo funcionar o debo anular por completo la función de rasgo que es mucho más compleja que esto :)

Shu
fuente

Respuestas:

641

Tu último estaba casi allí:

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

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

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

El rasgo no es una clase. No puede acceder a sus miembros directamente. Básicamente es solo copiar y pegar automatizado ...

ircmaxell
fuente
20
solo para aclarar: una vez que su clase define el mismo método, anula automáticamente el rasgo. El rasgo completa el método como @ircmaxell menciona cuando está vacío.
Yehosef
2
@PhillipWhelan sería bueno si pudiera agregar más información sobre lo que "no funciona como se esperaba". Escrito así no ayuda mucho a entender qué tipo de comportamiento incorrecto esperar, y no nos asegura que esto no sea un error temporal de su parte. ¿Tal vez hay alguna pregunta SO sobre el tema del que está hablando? (Eventualmente) Gracias.
Kamafeather
1
El problema es que todos los otros métodos en el rasgo ya no están incluidos.
malhal
2
Solo como referencia: si su función de rasgo fuera estática, puede acceder a la función llamando aA::calc(1)
velop
44
Como Phillip mencionó (creo), ¿cómo haría esto para un método de un rasgo mientras aún incluye todos los demás métodos del mismo rasgo de lo normal? Preferiblemente sin hacer referencia explícita a cada método.
Gannet
14

Si la clase implementa el método directamente, no utilizará la versión de rasgos. Quizás lo que estás pensando es:

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

Debido a que las clases secundarias no implementan el método directamente, primero usarán el del rasgo si de lo contrario usan el de la clase principal.

Si lo desea, el rasgo puede usar el método en la clase padre (suponiendo que sepa que el método estaría allí), por ejemplo

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)

También puede proporcionar formas de anular, pero aún acceder al método de rasgos de la siguiente manera:

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

Puede verlo funcionar en http://sandbox.onlinephpfunctions.com/code/e53f6e8f9834aea5e038aec4766ac7e1c19cc2b5

Yehosef
fuente
8

Un enfoque alternativo si está interesado, con una clase intermedia adicional para usar la forma normal de OOO. Esto simplifica el uso 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);
    }
}
Kartik V
fuente
66
Este enfoque reducirá cualquier ventaja que tenga al usar traits. Como combinar múltiples rasgos en múltiples clases (p. Ej., Rasgo A, B en una clase, rasgo B, C, D en otra clase, rasgo A, C en otra clase, etc.)
Ionuț Staicu
3
No, con este enfoque aún tiene las ventajas de tener un rasgo. Puede usar este rasgo en IntClass, pero también puede usarlo en muchas otras clases si lo desea. El rasgo será inútil si se usó solo en IntClass. En ese caso, sería mejor colocar el método calc () directamente en esa clase.
marcini
Esto no funcionaría para mí. ScreenablePerson::save()existe, Candidateusa Validatingrasgo y se extiende ScreenablePerson, y las tres clases tienen save().
Theodore R. Smith
1

Usando otro rasgo:

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
fuente