This is an old revision of the document!

PHP RFC: Covariant Returns and Contravariant Parameters


Currently PHP has mostly invariant parameter types and mostly invariant return types. This means that if a method of a super-type has a parameter or return type constraint of T then the corresponding parameter or return type constraint of method of the sub-type must also be T. However, PHP does special case a few things such as removing a type constraint on a parameter or adding one where one didn't exist for a return type; here is an example of both cases:

interface A {
  function m(B $z);
interface B extends A {
  // permitted
  function m($z): A;

However, PHP does not permit choosing less specific parameter types or more specific return types even though these substitutions are type-safe:

interface X {
  function m(Y $z): X;
interface Y extends X {
  // not permitted but type-safe
  function m(X $z): Y;

This RFC aims to support these additional cases.


When determining the compatibility of an method with its parent's the engine should now permit less specific parameter types and more specific return types as long as the new types still accept the types specified by the parents. In other words: a parameter type can be substituted for one of its super-types and a return type can substitute a sub-type.

This RFC will also fix Bug #76451: Aliases during inheritance type checks affected by opcache.

This RFC adds variance support to the object type. Variance on the object type was previously rejected when the object type was added, presumably on the grounds it should be added when variance is made more general, which is the purpose of this RFC.


Covariant return type with object:

interface Factory {
    function make(): object;
class UserFactory implements Factory {
    function make(): User;

Contravariant parameter type with iterable:

interface Concatable {
    function concat(Iterator $input); 
class Collection implements Concatable {
    // accepts all iterables, not just Iterator
    function concat(iterable $input) {/* . . . */}

Refining an iterator returned by IteratorAggregate::getIterator:

interface QueueIterator extends Iterator { /*...*/ }
interface Queue extends IteratorAggregate {
    function getIterator(): QueueIterator;
final class ArrayQueueIterator implements QueueIterator { /*...*/ }
final class ArrayQueue implements Queue {
    function getIterator(): ArrayQueueIterator {}

Auto-loading and Order of Definition Issues

Consider the following code, all of which is defined in the same file:

interface Collection extends Countable, IteratorAggregate {
  function getIterator(): Iterator;
class Vector implements Collection {
  function getIterator(): VectorIterator {
    /* . . . */
class VectorIterator implements Iterator {
  /* . . . */

All three types will need runtime variance checks because they extend or implement another type. To prevent issues with autoloading and order-of-definition issues, the implementation delays the variance verification until after the last consecutive type declaration. Here is what the relevant opcodes looked like from a preliminary implementation (output from phpdbg):

L1-13 {main}() /tmp/rfc_autoload.php - 0x7f6b49a89000 + 7 ops
 L2    #0     DECLARE_CLASS           "collection"
 L6    #1     DECLARE_CLASS           "vector"
 L10   #2     DECLARE_CLASS           "vectoriterator"
 L12   #3     VERIFY_VARIANCE         "collection"
 L12   #4     VERIFY_VARIANCE         "vector"
 L12   #5     VERIFY_VARIANCE         "vectoriterator"
 L13   #6     RETURN<-1>              1

Note that all 3 types are declared, then all 3 are verified in the same order they were written in.

At the moment the implementation will tolerate consecutive class and function definitions.

Backward Incompatible Changes

There are no intended incompatibilities. All incompatibilities should be reported and treated as bugs.

RFC Impact To Existing Extensions

TODO: New functions and macros should be introduced to make working with variant types in C easier.

Proposed PHP Versions

This RFC targets PHP 7.NEXT because it does not have any known compatibility issues.


This RFC requires two-thirds of voters to select “yes” for this RFC to pass.

Accept Covariant Returns and Contravariant Parameters?
Real name Yes No
ashnazg (ashnazg)  
bwoebi (bwoebi)  
carusogabriel (carusogabriel)  
colinodell (colinodell)  
cpriest (cpriest)  
danack (danack)  
derick (derick)  
dmitry (dmitry)  
dragoonis (dragoonis)  
emir (emir)  
galvao (galvao)  
gasolwu (gasolwu)  
girgias (girgias)  
guilhermeblanco (guilhermeblanco)  
hywan (hywan)  
jhdxr (jhdxr)  
jwage (jwage)  
kalle (kalle)  
kelunik (kelunik)  
kguest (kguest)  
laruence (laruence)  
lcobucci (lcobucci)  
leigh (leigh)  
levim (levim)  
lex (lex)  
malukenho (malukenho)  
marcio (marcio)  
mariano (mariano)  
mike (mike)  
nikic (nikic)  
ocramius (ocramius)  
petk (petk)  
pierrick (pierrick)  
pmmaga (pmmaga)  
salathe (salathe)  
sebastian (sebastian)  
thekid (thekid)  
theseer (theseer)  
trowski (trowski)  
yunosh (yunosh)  
Final result: 39 1
This poll has been closed.

Patches and Tests

A preliminary implementation can be found at https://github.com/php/php-src/compare/master...morrisonlevi:variance2. This is a preliminary patch with some obvious duplication of work, so performance impact has not been measured.

Tests are included in the implementation, but more tests are always welcome. Notably, some tests should be added to deal with multiple parents with differing but compatible signatures.

Future Scope

A future RFC may consider other super-types for existing types:

  • mixed: includes all types that exist or will ever exist including null; this matches our usage of mixed in our documentation.
  • scalar: includes bool, int, float, and string; this matches our is_scalar function.
  • numeric: this probably needs even more discussion because is_numeric does not deal exclusively with types: it also checks string values.


After the project is implemented, this section should contain

  1. the version(s) it was merged into
  2. a link to the git commit(s)
  3. a link to the PHP manual entry for the feature
  4. a link to the language specification section (if any)


Links to external references, discussions or RFCs.

rfc/covariant-returns-and-contravariant-parameters.1543259993.txt.gz · Last modified: 2018/11/26 19:19 by levim