property metodo existe etiquetas descargar check php class inheritance superclass traits

metodo - php 7 manual



Característica de PHP: ¿existe una forma adecuada de garantizar que la clase que usa un rasgo extienda una superclase que contiene cierto método? (4)

Creo que hay un camino completamente sin rasgos:

class BaseClass { public function sayHello() { echo ''Hello ''; } } class SayWorld { protected $parent = null; function __construct(BaseClass $base) { $this->parent = $base; } public function sayHelloWorld() { $this->parent->sayHello(); echo ''World!''; } } class MyHelloWorld extends Base { protected $SayWorld = null; function __construct() { $this->SayWorld = new SayWorld($this); } public function __call ( string $name , array $arguments ) { if(method_exists($this->SayWorld, $name)) { $this->SayWorld->$name(); } } }

Ejemplo # 2 del manual de PHP http://php.net/manual/en/language.oop5.traits.php estados

<?php class Base { public function sayHello() { echo ''Hello ''; } } trait SayWorld { public function sayHello() { parent::sayHello(); echo ''World!''; } } class MyHelloWorld extends Base { use SayWorld; } $o = new MyHelloWorld(); $o->sayHello(); ?>

Este es el código correcto, pero no es seguro usar parent:: en ese contexto. Digamos que escribí mi propia clase ''hola mundo'' que no hereda ninguna otra clase:

<?php class MyOwnHelloWorld { use SayWorld; } ?>

Este código no producirá ningún error hasta que llame al método sayHello() . Esto es malo.

Por otro lado, si el rasgo necesita usar un determinado método , puedo escribir este método como abstracto , y esto es bueno ya que garantiza que el rasgo se use correctamente en el momento de la compilación. Pero esto no se aplica a las clases para padres:

<?php trait SayWorld { public function sayHelloWorld() { $this->sayHello(); echo ''World!''; } public abstract function sayHello(); // compile-time safety }

Entonces, mi pregunta es: ¿hay una manera de asegurar (en tiempo de compilación, no en tiempo de ejecución) que la clase que usa un rasgo determinado tendrá el método parent::sayHello() ?


La etapa de compilación de PHP simplemente crea un código de bytes. Todo lo demás se realiza en tiempo de ejecución, incluida la toma de decisiones polimórficas. Así, el código como abajo compila:

class A {} class B extends A { public function __construct() { parent::__construct(); } }

pero explota cuando se run :

$b = new B;

Por lo tanto, estrictamente no puede tener un control parent en tiempo de compilación. Lo mejor que puedes hacer es postergar esto hasta el tiempo de ejecución tan pronto como sea posible. Como han demostrado otras respuestas, es posible hacer esto dentro de su método de rasgo con instanceof . Personalmente prefiero usar sugerencias de tipo cuando sé que un método de rasgo necesita un contrato en particular.

Todo esto se reduce al propósito fundamental de los rasgos: compilar y pegar en tiempo. Eso es. Rasgos no saben nada de los contratos. No se sabe nada de polimorfismo. Simplemente proporcionan un mecanismo para su reutilización. Cuando se combinan con interfaces, son bastante potentes, aunque algo verbosas.

De hecho, la primera discusión académica sobre los rasgos deja al descubierto la idea de que los rasgos deben ser "puros" en el sentido de que no tienen conocimiento del objeto que los rodea:

Un rasgo es esencialmente un grupo de métodos puros que sirve como un bloque de construcción para las clases y es una unidad primitiva de reutilización de código. En este modelo, las clases se componen de un conjunto de rasgos al especificar el código de pegamento que conecta los rasgos entre sí y accede al estado necesario.

"Si los rasgos no fueran malos, serían divertidos" resume muy bien los puntos, las trampas y las opciones. No comparto el vitriolo del autor para los rasgos: los uso cuando tiene sentido y siempre en pareja con una interface . Que el ejemplo en la documentación de PHP fomente el mal comportamiento es desafortunado.


No no hay. De hecho, este ejemplo es muy malo, ya que el propósito de introducir rasgos era introducir la misma funcionalidad en muchas clases sin depender de la herencia, y usar el parent no solo requiere que la clase tenga padre, sino que también debe tener un método específico.

En una nota lateral, parent llamadas de los parent no se verifican en el momento de la compilación, puede definir una clase simple que no extienda nada con las llamadas de los padres en sus métodos, y funcionará hasta que se llame a uno de estos métodos.


Puede verificar si $ esto extiende una clase específica o implementa una interfaz específica:

interface SayHelloInterface { public function sayHello(); } trait SayWorldTrait { public function sayHello() { if (!in_array(''SayHello'', class_parents($this))) { throw new /LogicException(''SayWorldTrait may be used only in classes that extends SayHello.''); } if (!$this instanceof SayHelloInterface) { throw new /LogicException(''SayWorldTrait may be used only in classes that implements SayHelloInterface.''); } parent::sayHello(); echo ''World!''; } } class SayHello { public function sayHello() { echo ''Hello ''; } } class First extends SayHello { use SayWorldTrait; } class Second implements SayHelloInterface { use SayWorldTrait; } try { $test = new First(); $test->sayHello(); // throws logic exception because the First class does not implements SayHelloInterface } catch(/Exception $e) { echo $e->getMessage(); } try { $test = new Second(); $test->sayHello(); // throws logic exception because the Second class does not extends SayHello } catch(/Exception $e) { echo $e->getMessage(); }