trowski

Aaron Piotrowski

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.

Patches and Tests

PR: #1941

Votes

An option needs 2/3 votes to win

Add iterable type to PHP 7.1 (94.6% approved)
User Vote
ajf Yes
bwoebi Yes
cmb Yes
colinodell Yes
danack Yes
daverandom Yes
davey Yes
francois Yes
galvao Yes
gooh Yes
hywan Yes
jwage Yes
kalle Yes
kguest Yes
kinncj Yes
krakjoe Yes
lbarnaud Yes
lcobucci Yes
levim Yes
marcio No
mariano Yes
mbeccati Yes
mrook Yes
nikic Yes
ocramius Yes
pauloelr Yes
pmjones Yes
pollita Yes
salathe Yes
sammyk Yes
santiagolizardo Yes
stas No
thekid Yes
trowski Yes
weierophinney Yes
yunosh Yes
zimt Yes