You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Class with property hooks causes ReflectionClass->isIterable() to return true on PHP8.4 #20217
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
Thanks for the report. This is due to ReflectionClass::isIterable() being somewhat inaccurate. It checks for ce->get_iterator, rather than checking whether the Traversable interface is implemented, like is_iterable() does. /cc @DanielEScherzer
Activity
iluuu1994 commentedon Oct 19, 2025
Thanks for the report. This is due to
ReflectionClass::isIterable()being somewhat inaccurate. It checks force->get_iterator, rather than checking whether theTraversableinterface is implemented, likeis_iterable()does. /cc @DanielEScherzerMerge branch 'PHP-8.5'