Narrowing return type on inheritance for abstract

2019-02-28 20:04发布

问题:

Assume we have the following inheritance chain in PHP

abstract class Entity {}
abstract class RealEntity extends Entity {}
abstract class PseudoEntity extends Entity {}

and a bunch of other classes that mirror the same inheritance chain

abstract class EntitySerializer {
  /**
   * @return Entity
   */
  abstract public function getEntity();
}

abstract class RealEntitySerializer extends EntitySerializer {
  /**
   * @return RealEntity
   */
  abstract public function getEntity();
}


abstract class PseudoEntitySerializer extends EntitySerializer {
  /**
   * @return PseudoEntity
   */
  abstract public function getEntity();
}

PHP complains that the abstract method getEntity must either be properly implemented (and loose the abstract keyword) or must not be re-declared. I can understand why PHP complains, because despite the PHPdoc comments the method signature is identical to the signature of the parent method in EntitySerializer.

However, I want somehow to make clear that child classes that extend RealEntitySerializer or PseudoEntitySerializer must not return an instance of an arbitrary Entity but narrow the return type to RealEntity or PseudoEntity resp.

Especially, If I omit re-definition of the method and its corresponding PHPdoc from the intermediate classes in order to make PHP happy, my IDE correctly assumes that RealEntitySerializer::getEntity and PseudoEntitySerializer::getEntity is allowed to return an arbitrary instance of Entity. In consequence, my IDE complains that I call undefined methods if I call methods that are particular to one of the intermediate classes on an object that was returned by RealEntitySerializer::getEntity or PseudoEntitySerializer::getEntity resp.

How, do I achieve both goals? Code that is (a) interpreted by PHP without an error and and that is (b) properly documented.

回答1:

You want the PSR-5: PHPDoc @method tag.

Syntax

@method [return type] [name]([type] [parameter], [...]) [description]

Examples

/**
 * @method string getString()
 * @method void setInteger(int $integer)
 * @method setString(int $integer)
 */
class Child extends Parent
{
    <...>
}


abstract class EntitySerializer {
  /**
   * @return Entity
   */
  abstract public function getEntity();
}

/**
 * @method RealEntity getEntity()
 */
abstract class RealEntitySerializer extends EntitySerializer {}

/**
 * @method PseudoEntity getEntity()
 */
abstract class PseudoEntitySerializer extends EntitySerializer {}