[php-src] PR #23945: ext/spl: Share arrays copy-on-write in ArrayObject and ArrayIterator

From: Date: Sun, 27 Sep 2026 15:02:56 +0000
Subject: [php-src] PR #23945: ext/spl: Share arrays copy-on-write in ArrayObject and ArrayIterator
Groups: php.git-pulls 
Request: Send a blank email to git-pulls+get-38856@lists.php.net to get a copy of this message
Pull Request: https://github.com/php/php-src/pull/23945 Author: nicolas-grekas ArrayObject and ArrayIterator duplicate the array they're given as soon as its refcount is above 1. That's the common case for return new \ArrayIterator($this->items); in getIterator(), so every foreach over such a collection copies it. getArrayCopy(), exchangeArray(), clone and (array) duplicate the storage too. This shares these arrays instead and separates the storage before any write, as the TODOs in spl_array.c suggested. Arrays holding references are still duplicated since zend_array_dup() unwraps references with refcount 1, and so are immutable arrays since they can't hold iterators. When the storage is separated during iteration, iterators move to the copy of their position that zend_array_dup() registers on the new table. Instructions per operation (callgrind, 100 elements, same with opcache): getIterator() + foreach -11%, new ArrayObject() -78%, getArrayCopy() -89%, clone -84%, exchangeArray() -88%, (array) -41%, foreach over an ArrayObject -0.4%. Walking Twig's node trees with foreach: -3%, with getIterator()->getArrayCopy(): -8%. This also fixes GH-17935, the array-backed cases of #22921, and a use-after-free when an error handler calls exchangeArray() while an element is being created. It overlaps with #20725, which fixes GH-17935 by separating at the write sites, and conflicts with #22921, which separates the storage on each access and would undo the sharing. /cc @alexandre-daubois @iliaal

« previous php.git-pulls (#38856) next »