Web lists-archives.com

Inheritance: annotations vs. type-hints




Simply stated: how do I know if the argument of a method is annotated or
type-hinted by simply reading the manual or the documentation generated
from the sources?

Note that annotations and type-hints are quite different from the PHP
point of view and mismatches could cause warnings or fatal errors at
parse time. The question above becomes even more current as PHP moves
in the land of the "strongly typed" languages. Both core libraries (DOM,
PDO, DateTime, etc.) and user code are involved in this issue.
To summarize the matching rules (see also the test code below):

Annotated method argument can be implemented|override by:
- annotated argument, or
- type-hinted argument (only since PHP 7.2)

Annotated return type can be implemented|override by:
- annotated return type, or
- type-hinted return type

Type-hinted method argument can be implemented|override by:
- same type-hint (parent class not allowed, see https://wiki.php.net/rfc/covariant-returns-and-contravariant-parameters)

Type-hinted return type can be implemented|override by:
- same type-hint (derived class not allowed, see https://wiki.php.net/rfc/covariant-returns-and-contravariant-parameters)

So, basically, if an interface or base class is fully annotated, we can
freely choose to annotate or type-hint our implementation or extended
class, possibly making analysis tools to complain here and there, but
making happy PHP at run-time.

Things go in a completely different way when interfaces and base classes
are fully type-hinted; in this case our implementation or extended class
MUST be fully type-hinted as well to prevent warnings and fatal parse
errors at run-time.

Then, for example, lets take this class:

class A {
    /**
     * @param int $x
     * @return int
     */
    function annotatedMethod($x){ return 1; }

    function typeHintedMethod(int $x): int { return 1; }
}

Note the first method is fully annotated with a DocBlock, while the
second one is type-hinted.  How to render the signature of these methods
in the documentation? Here is my proposal:

A::annotatedMethod(int $x): int;
A::typeHintedMethod(*int* $x): *int*;

Note that type-hinted types are enclosed between asterisks to make evident
these types MUST be indicated as type-hint in each extended class. Other
visual highligting style are possible too (slanted characters, boldface,
double-underline, etc.) but this solution is simpler as it can be
rendered by simply using ASCII characters only. The reverse is also
possible too, that is *T* may indicate an annotated type, while T may
indicate a type-hinted one, but since type-hints are by now quite rare
we may reduce the visual cluttering.


Now the test code illustrating annotations vs. type-hints compatibility:

<?php

class A {
	/**
	 * @param int $x
	 * @return int
	 */
	function annotatedMethod($x){ return 1; }
	
	function typeHintedMethod(int $x): int { return 1; }
}

class B1 extends A {
	/**
	 * @param int $x
	 * @return int
	 */
	function annotatedMethod($x){ return 1; }
	
	function typeHintedMethod(int $x): int { return 1; }
}

class B2 extends A {
	/**
	 * @return int
	 */
	function annotatedMethod(int $x){ return 1; }
	//PHP 7.1.9 Warning:  Declaration of B2::annotatedMethod(int $x) should be compatible with A::annotatedMethod($x) in ...
	//PHP 7.3.1 Warning:  Declaration of B2::annotatedMethod(int $x) should be compatible with A::annotatedMethod($x) in ...
}

class B3 extends A {
	/**
	 * @param int $x
	 */
	function annotatedMethod($x): int { return 1; }
}

class B4 extends A {
	/**
	 * @param int $x
	 */
	function typeHintedMethod($x): int { return 1; }
	//PHP 7.1.9 Warning:  Declaration of B4::typeHintedMethod($x): int should be compatible with A::typeHintedMethod(int $x): int in ...
	//PHP 7.3.1 OK due to the implementation of the "Parameter Type Widening" RFC https://wiki.php.net/rfc/parameter-no-type-variance since PHP 7.2
}

class B5 extends A {
	/**
	 * @return int
	 */
	function typeHintedMethod(int $x){ return 1; }
	//PHP 7.1.9 Fatal error:  Declaration of B5::typeHintedMethod(int $x) must be compatible with A::typeHintedMethod(int $x): int in ...
	//PHP 7.3.1 Fatal error:  Declaration of B5::typeHintedMethod(int $x) must be compatible with A::typeHintedMethod(int $x): int in ...
}
?>

Regards,
 ___
/_|_\  Umberto Salsi
\/_\/  www.icosaedro.it