Skip to content

Stdlib: key()/current()/next()/prev()/reset()/end() — array internal pointer (ext/standard/array.c parity) #3312

Closed

Description

@PurHur

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

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 via JitArrayPointer.php)
  • ext/standard/Module.php registration
  • Do not confuse with ext/standard/JitArrayKey.php (array key listing helper)
  • test/compliance/cases/stdlib/array_pointer.phpt
  • php 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_pointer green

Links

Activity

  1. added
    enhancementNew feature or request
    phase-4:stdlibPhase 4 – stdlib for web apps
    area:vmVirtual machine
    implementation-readySpec complete: repro, php-src ref, done-when — safe for workers to claim
    on May 29, 2026
  2. PurHur commented on Jun 3, 2026

    @PurHur
    OwnerAuthor

    Implementation-ready expansion (2026-06-03)

    Probe

    ./script/docker-exec.sh -- bash -lc 'source script/php-env.sh && php bin/vm.php -r "
    foreach ([\"key\",\"current\",\"next\",\"prev\",\"reset\",\"end\",\"pos\",\"chop\"] as \$f)
      echo \$f, \" \", (int)function_exists(\$f), \"\\n\";
    "'

    All eight → 0 on VM. Host PHP in same image → 1 for all.

    Alias follow-up

    Register pos() / chop() when pointer/rtrim land — #4965.

    Bootstrap / M5

    Native hashtable cursor without Zend key() — #4967 (AOT self-host). VM work remains this issue.

    Repro (unchanged)

    <?php
    $a = ['a' => 1, 'b' => 2];
    var_dump(key($a), current($a));
    next($a);
    var_dump(key($a), current($a));
    reset($a);
    var_dump(key($a));

    Done when

  3. PurHur commented on Jun 16, 2026

    @PurHur
    OwnerAuthor

    Spec expansion / status audit (2026-06-16)

    Registration is done — all six builtins exist on VM:

    ./script/docker-exec.sh -- bash -lc 'source script/php-env.sh && php bin/vm.php -r "
    foreach ([\"key\",\"current\",\"next\",\"prev\",\"reset\",\"end\"] as \$f)
      echo \$f,\": \", function_exists(\$f)?\"yes\":\"no\", PHP_EOL;
    "'
    # all yes (Jun 2026)

    Remaining php-src-strict gap (do not close on function_exists alone)

    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:

    <?php
    enum E: int { case A = 1; case B = 2; }
    $a = [E::A, E::B];
    reset($a);
    var_export(current($a)); // Zend: E::A object; VM today: int(1)

    Updated done-when

    Worker: implement enum preservation under #8899; use this issue for pointer semantics matrix only. Close #3312 when both pointer API and enum repro are green.

  4. PurHur commented on Jun 19, 2026

    @PurHur
    OwnerAuthor

    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:

    ./script/docker-exec.sh -- bash -lc 'source script/php-env.sh
    php bin/vm.php -r "var_export(function_exists(\"key\")); echo \"\n\";
    \$a=[\"a\"=>1,\"b\"=>2]; reset(\$a); echo key(\$a),\"=\",current(\$a),\"\n\";
    next(\$a); echo key(\$a),\"=\",current(\$a),\"\n\";"'
    # true / a=1 / b=2

    Capability matrix marks all six VM/JIT/AOT yes.

    Remaining php-src gaps (worker scope)

    Gap Repro sketch php-src
    Enum-case keyed arrays [E::A => 1]; reset($a); key($a) must TypeError or preserve enum key per Zend ext/standard/array.c + zend_hash.c illegal offset
    Pointer pass-by-ref $a=[1,2]; $k=&$a; reset($k); key($a) must share internal pointer array.c ZEND_FETCH_REF
    Empty array key([]) → null; current([]) → false array.c
    Non-array operand key(null) → TypeError (php-src-strict) zend_parse_parameters

    Done when (updated)

    Do not re-implement basic registration — focus on edge parity + enum keys.

  5. PurHur commented on Jul 5, 2026

    @PurHur
    OwnerAuthor

    claim: cursor-agent-lane-a — starting this run (object key() private/protected mangling parity)

  6. PurHur commented on Jul 5, 2026

    @PurHur
    OwnerAuthor

    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 → ok
    • vendor/bin/phpunit --filter array_pointer_object → OK (4 tests)
    • ./script/ci-fast.sh --filter array_pointer → green

    Also covers the private-key portion of #16301 (values were already correct; names were unmangled).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:vmVirtual machineenhancementNew feature or requestimplementation-readySpec complete: repro, php-src ref, done-when — safe for workers to claimphase-4:stdlibPhase 4 – stdlib for web apps

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions