Repository navigation
Description
Category
stdlib
Problem
PHP’s array internal pointer builtins let code walk arrays without foreach. All six are absent from ext/standard/Module.php; function_exists('key') is false on VM (verified 2026-06-01).
Zend maintains a cursor per array value; reset()/end() move it, current()/key() read position, next()/prev() advance. Legacy code and php-src tests rely on this API.
Implementation hint: lib/VM/HashTable.php already has a private $internalPointer field and advance logic used by iterateKeyed() — expose Zend-compatible reset/end/next/prev/current/key on the array zval, not only internal foreach.
php-src reference
ext/standard/array.c—PHP_FUNCTION(key),current,next,prev,reset,endZend/zend_hash.c— internal pointer (HT_IDX,HT_USED) onHashTable
Repro (today)
<?php
$a = ['a' => 1, 'b' => 2, 'c' => 3];
reset($a);
echo key($a), '=', current($a), "\n";
next($a);
echo key($a), '=', current($a), "\n";
end($a);
echo key($a), '=', current($a), "\n";
prev($a);
echo key($a), '=', current($a), "\n";./script/docker-exec.sh -- bash -lc 'source script/php-env.sh && php bin/vm.php repro.php'Zend PHP: a=1, b=2, c=3, b=2
This compiler: Call to undefined function reset() (first missing call)
Edge-case matrix (must match Zend)
| Call | Empty [] |
After reset on ['a'=>1] |
|---|---|---|
key() |
null |
'a' |
current() |
false |
1 |
next() |
false |
advances |
prev() |
false |
— |
reset() |
false |
1 (return value = current) |
end() |
false |
last value |
Pass-by-value: function f($a) { next($a); } must not move caller’s pointer (separate zval / COW).
Scope
lib/VM/HashTable.php— public pointer API:resetInternal(),endInternal(),nextInternal(),prevInternal(),getInternalKey(),getInternalCurrent()ext/standard/—key.php,current.php,next.php,prev.php,reset.php,end.php(VM + optional JIT viaJitArrayPointer.php)ext/standard/Module.phpregistration- Do not confuse with
ext/standard/JitArrayKey.php(array key listing helper) test/compliance/cases/stdlib/array_pointer.phptphp script/capability-matrix.php
Done when
- All six functions registered; repro output matches Zend on packed + string-key arrays
- Edge-case matrix above verified
- Passing by value does not mutate caller’s array pointer
-
./script/ci-fast.sh --filter array_pointergreen
Links
- Pairs with
foreach/ Language: foreach over Traversable / Iterator objects (Zend parity) #3234 (object iterators) · Epic: Self-host critical path — compiler compiles itself (M3→M5) #1492
Activity
PurHur commentedon Jun 3, 2026
Implementation-ready expansion (2026-06-03)
Probe
All eight → 0 on VM. Host PHP in same image → 1 for all.
Alias follow-up
Register
pos()/chop()when pointer/rtrimland — #4965.Bootstrap / M5
Native hashtable cursor without Zend
key()— #4967 (AOT self-host). VM work remains this issue.Repro (unchanged)
Done when
posalias (Stdlib: chop() / pos() — legacy aliases of rtrim() / current() (ext/standard/string.c, array.c) #4965) match Zend on VMtest/compliance/cases/stdlib/array_pointer.phptin ci-fastPurHur commentedon Jun 16, 2026
Spec expansion / status audit (2026-06-16)
Registration is done — all six builtins exist on VM:
Remaining php-src-strict gap (do not close on
function_existsalone)Enum case arrays must not materialize backing scalars when walking the internal pointer — tracked in #8899 (
current/prev/next/end/reset).Repro extension for this issue's done-when:
Updated done-when
function_existstrue for all six (complete)./script/ci-fast.sh --filter array_pointergreenWorker: implement enum preservation under #8899; use this issue for pointer semantics matrix only. Close #3312 when both pointer API and enum repro are green.
PurHur commentedon Jun 19, 2026
Spec refresh (2026-06-19) — partially landed, narrow remaining scope
Status: Core six builtins are registered and working since #4967 (
ext/standard/key.php,current.php,next.php,prev.php,reset_.php,end_.php). Verified on master:Capability matrix marks all six VM/JIT/AOT yes.
Remaining php-src gaps (worker scope)
[E::A => 1]; reset($a); key($a)must TypeError or preserve enum key per Zendext/standard/array.c+zend_hash.cillegal offset$a=[1,2]; $k=&$a; reset($k); key($a)must share internal pointerarray.cZEND_FETCH_REFkey([])→null;current([])→falsearray.ckey(null)→TypeError(php-src-strict)zend_parse_parametersDone when (updated)
./script/docker-exec.shtest/compliance/cases/stdlib/array_pointer.phptcovers ref-alias + empty + TypeError./script/ci-fast.sh --filter array_pointergreenDo not re-implement basic registration — focus on edge parity + enum keys.
PurHur commentedon Jul 5, 2026
claim: cursor-agent-lane-a — starting this run (object key() private/protected mangling parity)
PurHur commentedon Jul 5, 2026
Merged via #16307 — object
key()now returns Zend-mangled private/protected property names.Verification (Docker):
php bin/vm.php test/repro/maintainer_gap_array_pointer_object_mangled.php→okvendor/bin/phpunit --filter array_pointer_object→ OK (4 tests)./script/ci-fast.sh --filter array_pointer→ greenAlso covers the private-key portion of #16301 (values were already correct; names were unmangled).
Stdlib: key() on object returns mangled private/protected names (#3312)…
chore: drop 24 more stale maintainer_gap repros (wave30 batch 2, #16660…