iterable 是 array|traversable 的编译时别名,非具体类型;它接受数组及任意 traversable 对象(如 arrayobject、generator),而 array 仅接受原生数组,故 arrayobject、生成器等可传给 iterable 却不被 array 接受。

iterable 不是具体类型,而是 array|Traversable 的编译时别名;它不表示“某种数组”,而是一种契约:只要能被 foreach 消费,就满足要求。
iterable 能接受哪些值,array 不能?
关键区别在于运行时兼容范围:
-
iterable可以是array,也可以是任意实现了Iterator、IteratorAggregate或其他Traversable子接口的对象(比如ArrayObject、SimpleXMLIterator、自定义分页器) -
array类型声明只接受 PHP 原生数组,传入ArrayObject会直接报TypeError - 生成器函数返回
iterable是合法的,但不能声明返回array—— 因为生成器不是数组,它延迟产出值
示例:
function accepts_iterable(iterable $i): void { foreach ($i as $v) { echo $v; } }
function accepts_array(array $a): void { foreach ($a as $v) { echo $v; } }
$gen = function() { yield 'a'; yield 'b'; };
$obj = new ArrayObject(['x', 'y']);
accepts_iterable($gen()); // ✅ OK
accepts_iterable($obj); // ✅ OK
accepts_array($obj); // ❌ TypeError: Argument #1 must be of type array
声明参数为 iterable 时,为什么有时反而更严格?
表面看 iterable 更宽泛,但在类型继承和方法重写中,它可能触发更严格的协变/逆变检查:
- 父类方法参数声明为
iterable,子类重写时可缩小为array或具体Traversable子类(如ArrayObject) - 但若父类参数是
array,子类不能扩大为iterable—— 这会破坏调用方对“一定是数组”的预期 - 错误常出现在 IDE 提示或静态分析工具(如 PHPStan)中,而非运行时报错
典型报错信息:Parameter of method Child::foo() has type iterable, but parent method Parent::foo() has type array
用 iterable 替代 array 会影响性能或反射吗?
几乎无运行时开销,但要注意两处隐性差异:
- 类型反射中,
ReflectionParameter::getType()对iterable返回ReflectionNamedType,名字是"iterable";但在联合类型中(如iterable|int),它会被展开为array|Traversable|int - 错误消息里不再显示
iterable,而是array|Traversable—— 这是 PHP 编译期替换的结果,调试时容易困惑 - 没有额外内存或迭代开销:PHP 内部对
iterable的处理和直接写array|Traversable完全一致
什么时候该选 iterable,而不是 array?
核心判断依据是「你是否真的依赖数组的特定行为」:
- 只做遍历(
foreach)、不调用count()、array_keys()、[]下标访问 → 用iterable - 需要
is_array()判断、修改键值、使用array_map()等数组专用函数 → 必须用array - 想让 API 更开放(比如允许用户传生成器做流式处理),又不想放弃类型安全 →
iterable是明确信号
最容易被忽略的一点:iterable 允许空值(?iterable),但 array 本身不能为 null,除非显式写成 ?array —— 这个问号语义差异在接口设计中常被漏掉。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











