Table of Contents

PHP RFC: Iterable

Introduction

It is common for a function to accept or return either an array or an object implementing Traversable to be used with foreach. However, because array is a primitive type and Traversable is an interface, there currently is no way to use a type declaration on a parameter or return type to indicate that the value is iterable.

Proposal

This RFC proposes a new iterable pseduo-type. This type is analogous to callable, accepting multiple types instead of one single type.

iterable accepts any array or object implementing Traversable. Both of these types are iterable using foreach and can be used with yield from within a generator.

iterable can be used as a parameter type to indicate that a function requires a set of values, but does not care about the form of the value set (array, Iterator, Generator, etc.) since it will be used with foreach. If a value is not an array or instance of Traversable, a TypeError will be thrown.

function foo(iterable $iterable) {
    foreach ($iterable as $value) {
        // ...
    }
}

iterable can also be used as a return type to indicate a function will return an iterable value. If the returned value is not an array or instance of Traversable, a TypeError will be thrown.

function bar(): iterable {
    return [1, 2, 3];
}

Parameters declared as iterable may use null or an array as a default value.

function foo(iterable $iterable = []) {
    // ...
}

Functions declaring iterable as a return type may also be generators.

function gen(): iterable {
    yield 1;
    yield 2;
    yield 3;
}

Classes extending/implementing may broaden methods using array or Traversable as parameter types to iterable or narrow return types from iterable to array or Traversable. This behavior is the same as that proposed for all union types in the Union Types RFC.

interface Example {
    public function method(array $array): iterable;
}
 
class ExampleImplementation implements Example {
    public function method(iterable $iterable): array {
        // Parameter broadened and return narrowed.
    }
}

This proposal also adds a function is_iterable() that returns a boolean: true if a value is iterable and will be accepted by the iterable pseudo-type, false for other values.

var_dump(is_iterable([1, 2, 3])); // bool(true)
var_dump(is_iterable(new ArrayIterator([1, 2, 3]))); // bool(true)
var_dump(is_iterable((function () { yield 1; })())); // bool(true)
var_dump(is_iterable(1)); // bool(false)
var_dump(is_iterable(new stdClass())); // bool(false)

Object Iteration

PHP allows any object to be used with foreach. However, iterable does not accept any object, only those implementing Traversable. Values accepted by iterable should be designed for iteration, not any set of values (such as the public properties of an object or a string). There is already some precedent in the language for discriminating between values accepted by iterable and those that can be used with foreach. yield from only accepts arrays or objects implementing Traversable. iterable then represents the values usable with yield from.

Backward Incompatible Changes

iterable is implemented as reserved classname, therefore a class, interface, or trait named iterable cannot be declared.

Another function named is_iterable() cannot be declared in the root namespace.

Proposed PHP Version(s)

PHP 7.1

Future Scope

Union Types

If the union types RFC is accepted, then iterable will be a shortcut for array | Traversable. This is a common type requirement for functions, so having a shorter name for this union will be handy.

Voting

This proposal requires a 2/3 majority to be accepted.

Voting began on 2016-06-24 at 6:00 pm GMT and ended on 2016-07-02 at 11:59 pm GMT

Add iterable type to PHP 7.1
Real name Yes No
ajf (ajf)  
bwoebi (bwoebi)  
cmb (cmb)  
colinodell (colinodell)  
danack (danack)  
daverandom (daverandom)  
davey (davey)  
francois (francois)  
galvao (galvao)  
gooh (gooh)  
hywan (hywan)  
jwage (jwage)  
kalle (kalle)  
kguest (kguest)  
kinncj (kinncj)  
krakjoe (krakjoe)  
lbarnaud (lbarnaud)  
lcobucci (lcobucci)  
levim (levim)  
marcio (marcio)  
mariano (mariano)  
mbeccati (mbeccati)  
mrook (mrook)  
nikic (nikic)  
ocramius (ocramius)  
pauloelr (pauloelr)  
pmjones (pmjones)  
pollita (pollita)  
salathe (salathe)  
sammyk (sammyk)  
santiagolizardo (santiagolizardo)  
stas (stas)  
thekid (thekid)  
trowski (trowski)  
weierophinney (weierophinney)  
yunosh (yunosh)  
zimt (zimt)  
Final result: 35 2
This poll has been closed.

Patches and Tests

PR: #1941