-
Notifications
You must be signed in to change notification settings - Fork 8k
Description
Description
The following code:
<?php
class ClassWithPhp84PropertyHooks
{
private string $backing = 'default';
public string $name {
get => $this->backing;
set => $this->backing = strtoupper($value);
}
}
$refClass = new ReflectionClass(ClassWithPhp84PropertyHooks::class);
var_dump($refClass->isIterable());
Resulted in this output:
true
But I expected this output instead:
false
Copilot analysis:
ReflectionClass::isIterable()
returns true because the engine considers the class iterable when its internal class entry has a non‑NULL get_iterator handler — and classes that use the new property hooks end up with such a handler set. isIterable() checks ce->get_iterator || instanceof Traversable, so the presence of the get_iterator pointer makes it return true even if the class does not implement Traversable.
What the code does (evidence)
The ReflectionClass::isIterable implementation (ext/reflection/php_reflection.c) returns false for interfaces/traits/abstracts, otherwise it returns: RETURN_BOOL(ce->get_iterator || instanceof_function(ce, zend_ce_traversable)); i.e. it is true when either the class has a get_iterator handler or it implements Traversable.
Why property hooks flip that bit
The property-hook implementation in the engine installs/customizes object handlers so that property access and enumeration can be handled by the new hook code. As part of that support the class entry ends up with a get_iterator handler (so the engine can enumerate properties through the new hooks). Because isIterable() only looks at the get_iterator pointer (or Traversable), Reflection reports the class as iterable.
Practical consequence
ReflectionClass::isIterable() returning true in this case is expected engine behaviour. It does not necessarily mean the class implements Traversable/IteratorAggregate; it means the Zend class entry provides an iterator function for foreach/iteration. You can confirm with reflection checks, e.g.: $r = new ReflectionClass(ClassWithPhp84PropertyHooks::class); var_dump($r->isIterable(), $r->implementsInterface(Traversable::class)); // likely: true, false
References
PHP Version
PHP 8.4.13 (cli) (built: Sep 23 2025 14:14:32) (NTS)
Copyright (c) The PHP Group
Built by Homebrew
Zend Engine v4.4.13, Copyright (c) Zend Technologies
with Xdebug v3.4.1, Copyright (c) 2002-2025, by Derick Rethans
with Zend OPcache v8.4.13, Copyright (c), by Zend Technologies
Operating System
MacOS