Skip to content

array_unshift() skips unvisited values in foreach by reference when the array has holes #25107

Open

Description

@JoaoMarcos44

Description

AI-assisted report. The explanation was prepared with AI assistance. The reproduction and outputs below were verified by local execution.

Calling array_unshift() during a foreach by reference can skip an unvisited element when deleted entries leave holes before the iterator position. This can silently omit work items.

<?php
$items = ['a', 'b', 'c', 'd'];
unset($items[0]);
$seen = [];
foreach ($items as &$item) {
    $seen[] = $item;
    if ($item === 'b') {
        array_unshift($items, 'x');
    }
}
unset($item);
echo json_encode($seen), "\n";

Actual output:

["b","d"]

Expected output:

["b","c","d"]

The PHP 7 foreach RFC and Zend/tests/foreach/foreach_016.phpt specify that array_unshift() preserves reference-iterator positions. This case also reproduces with string-keyed arrays. The rebuild compacts deleted slots, but the iterator adjustment only adds the number of prepended values to the old position.

Related PR #24475 optimizes the path without holes or active iterators; its current fallback retains the affected iterator adjustment.

PHP Version

Also reproduced on the lowest actively supported branch, PHP-8.4, at commit dfcc0433fcda7fb732cba8d7ee8c857d23f9a9e7, with debug and ZTS enabled.

PHP 8.4.28-dev (cli) (built: Oct 31 2026 18:38:26) (ZTS DEBUG)
Copyright (c) The PHP Group
Zend Engine v4.4.28-dev, Copyright (c) Zend Technologies
PHP 8.5.12 (cli) (built: Oct 28 2026 01:23:38) (NTS)
Copyright (c) The PHP Group
Built by https://github.com/docker-library/php
Zend Engine v4.5.12, Copyright (c) Zend Technologies
    with Zend OPcache v8.5.12, Copyright (c), by Zend Technologies

Also reproduced on PHP 8.7.0-dev at commit 248c5c45409667b5e69bba38a92c5cc9edd7582a.

Operating System

Debian GNU/Linux 12 (bookworm), Linux container.

Proposed fix and regression test: #25111.

Illustrated explanation and generation prompt.

Activity

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions