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
Regression: next()/prev() on object leave current() NULL — Zend returns element (ext/standard/array.c) #25097
next() / prev() / end() on an object still advance key() (php-src accepts object HT), but current() (and often the return value of prev()) is NULL on VM/JIT while Zend returns the element at the new position.
Probed 2026-07-29 on Zend 8.2.32 vs bin/vm.php / bin/jit.php.
ext/standard/prev.php, next.php, current.php + ext/standard/JitArrayPointer.php / ArrayPointerJitHelper.php / ArrayPointerRuntime — after moving the object HT pointer, current() must load the value at key() (same as arrays)
Same shape with a declared class (class C { public $a=1; public $b=2; }) is green. Arrays are green. $c = current($o2) then echo is green. ObjectEntry pointer ops are fine when current() is not nested under var_export(..., true) in a concat.
Root cause (CFG)
php bin/print.php shows php-cfg hoist:
FuncCall current → result Var#8
ConstFetch true → Var#9
FuncCall var_export args[0]=Var#10 args[1]=Var#11 // dead temps, not #8/#9
slotForHoistedArrayPointerBuiltinCallArg / siblingFuncCallFeedsVarExportConsumer (#13829 / #13828) already cover arrayvar_export(current($a), true) (see InlineCallArgProducerSlotTest). Dynamic-property stdClass / (object)[...] after stmt-level next/prev still leaves arg #0 on a dead temp → var_export sees NULL while key() often still prints the advanced key.
Related existing coverage: test/repro/maintainer_gap_array_pointer_object.php (declared props, separate echoes — green), unit tests around #13828/#13829 (arrays only).
Next step
Extend InlineCallArgProducerSlotTest with the cast-stdClass concat repro above (expect 2).
Root cause found: releaseOutgoingCallArgTemps called ObjectLifetime::releaseDirectObject on by-ref SEND entries (slot null). For next($o2) that releases the CV's sole object ref → destroyForGc nulls stdClass properties → current() returns NULL while key() still shows the advanced pointer. Extra live refs (longer scripts) masked it (refCount 2→1). Fix: only releaseDirectObject when the entry has a by-value snapshot slot.
Root cause:releaseOutgoingCallArgTemps called ObjectLifetime::releaseDirectObject on by-ref SEND entries (no snapshot). next($stdClass) dropped the CV’s sole object ref → destroyForGc nulled properties → current() NULL while key() advanced.
phpunit --filter testVarExportCurrentAfterNextOnCastStdClass OK
phpunit --filter array_pointer_object_current_after_next OK (VM+JIT)
AOT build smoke tier 1 OK during ci-fast
Not covered: full ci-fast.sh end-to-end (master already red on duplicate spine SoapHttpTransport require + related footnote heal); hygiene commits on the PR also allowlisted the intentional max_memory_limit unlimited probe and synced init-sessionsweb flash clear
Activity
PurHur commentedon Jul 30, 2026
claim: PHP Compiler STD Lib Advanced — batch (issues #25063, #25097, #23785)
Hard stdlib cluster: highlight_string/file/show_source Zend HTML wire (#25063), next/prev/current on object HT (#25097), highlight Reflection names (#23785). Shipping sequential PRs; starting with #25063.
PurHur commentedon Jul 30, 2026
Continuing claimed batch after #25063 / #25128: implementing #25097 next — next/prev/current on object HT.
PurHur commentedon Jul 30, 2026
Handoff — blocked on CFG arg wiring (not ObjectEntry)
Claim: continuing advanced-stdlib batch after #25063 / #25128.
Stable repro (issue shape)
next_current=2 key='b'next_current=NULL key='b'(key OK, current NULL)Same shape with a declared class (
class C { public $a=1; public $b=2; }) is green. Arrays are green.$c = current($o2)then echo is green. ObjectEntry pointer ops are fine whencurrent()is not nested undervar_export(..., true)in a concat.Root cause (CFG)
php bin/print.phpshows php-cfg hoist:slotForHoistedArrayPointerBuiltinCallArg/siblingFuncCallFeedsVarExportConsumer(#13829 / #13828) already cover arrayvar_export(current($a), true)(seeInlineCallArgProducerSlotTest). Dynamic-property stdClass /(object)[...]after stmt-levelnext/prevstill leaves arg #0 on a dead temp →var_exportsees NULL whilekey()often still prints the advanced key.Related existing coverage:
test/repro/maintainer_gap_array_pointer_object.php(declared props, separate echoes — green), unit tests around #13828/#13829 (arrays only).Next step
InlineCallArgProducerSlotTestwith the cast-stdClass concat repro above (expect2).var_exportARG_SEND rewiring forcurrent/key/poswhen the operand is aCast_Object/ dynamic-prop object (same family as Regression: key() string-concat nested operand — returns true not key (ext/standard/array.c) #13829, not a newruntime/*.cpath).test/compliance/cases/stdlib/array_pointer_object_current_after_next.phpt.Not starting #23785 in this run until #25097 is green (batch rule).
PurHur commentedon Jul 30, 2026
claim: PHP Compiler STD Lib Advanced — continuing batch (#25063 done, #25097 next, then #23785)
Root cause found:
releaseOutgoingCallArgTempscalledObjectLifetime::releaseDirectObjecton by-ref SEND entries (slot null). Fornext($o2)that releases the CV's sole object ref →destroyForGcnulls stdClass properties →current()returns NULL whilekey()still shows the advanced pointer. Extra live refs (longer scripts) masked it (refCount 2→1). Fix: onlyreleaseDirectObjectwhen the entry has a by-value snapshot slot.Stdlib: do not releaseDirectObject by-ref call-arg CVs (#25097) (#25149)
PurHur commentedon Jul 30, 2026
Merged via #25149
Root cause:
releaseOutgoingCallArgTempscalledObjectLifetime::releaseDirectObjecton by-ref SEND entries (no snapshot).next($stdClass)dropped the CV’s sole object ref →destroyForGcnulled properties →current()NULL whilekey()advanced.Verification
prev_ret=2 current=2 key='b'/next_current=2 key='b'phpunit --filter testVarExportCurrentAfterNextOnCastStdClassOKphpunit --filter array_pointer_object_current_after_nextOK (VM+JIT)ci-fast.shend-to-end (master already red on duplicate spineSoapHttpTransportrequire + related footnote heal); hygiene commits on the PR also allowlisted the intentionalmax_memory_limitunlimited probe and synced init-sessionsweb flash clear