SPL Data Structures
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. The reference material consulted while preparing these pages is listed in the section bibliography. |
This page is written against PHP 8.5, checked on 2026-10-05. PHP’s array is an ordered hash map that serves almost every purpose. The SPL (Standard PHP Library, always enabled) adds classic data structures for the cases where an array has the wrong complexity or the wrong semantics: queues that dequeue in O(1), heaps that always return the smallest element, fixed-size integer-indexed arrays, and sets of objects.
Stacks and queues: SplDoublyLinkedList, SplStack, SplQueue
SplDoublyLinkedList is a doubly linked list with O(1) insertion and removal at both ends. SplStack (LIFO) and
SplQueue (FIFO) are subclasses with the matching iteration order. Unlike array_shift(), which renumbers the array
(O(n)), SplQueue::dequeue() is O(1).
<?php
declare(strict_types=1);
// Reservations are served first come, first served
$waitingList = new SplQueue();
$waitingList->enqueue('M-17');
$waitingList->enqueue('M-03');
$waitingList->enqueue('M-42');
echo $waitingList->dequeue(), ' is next; ', count($waitingList), " still waiting\n"; // M-17 is next; 2 still waiting
echo 'then ', $waitingList->bottom(), "\n"; // then M-03 (front of the queue)
// Undo history of catalogue edits: last in, first out
$undo = new SplStack();
$undo->push('add Kindred');
$undo->push('rename Solaris');
echo 'undo: ', $undo->pop(), ', top is now: ', $undo->top(), "\n"; // undo: rename Solaris, top is now: add Kindred
foreach ($undo as $action) { // iterates top to bottom for a stack
echo "- {$action}\n";
}
try {
(new SplQueue())->dequeue();
} catch (RuntimeException $e) {
echo get_class($e), ': ', $e->getMessage(), "\n"; // RuntimeException: Can't shift from an empty datastructure
}
setIteratorMode() chooses the direction (IT_MODE_FIFO/IT_MODE_LIFO) and whether iteration consumes elements
(IT_MODE_DELETE). Random access by index is O(n) on a linked list; use an array if you need it.
Source: SplQueue, SplStack and SplDoublyLinkedList
Heaps: SplMinHeap, SplMaxHeap and SplHeap
A heap keeps its elements partially ordered so that extract() always returns the smallest (SplMinHeap) or
largest (SplMaxHeap) in O(log n), and top() peeks at it in O(1). Extend the abstract SplHeap and implement
compare($a, $b) for custom ordering. Iterating a heap consumes it.
<?php
declare(strict_types=1);
/** Loans ordered by due date: the next one to fall due comes out first. */
final class DueDates extends SplHeap
{
protected function compare(mixed $a, mixed $b): int
{
return $b['due'] <=> $a['due']; // positive if $a should come out first
}
}
$due = new DueDates();
$due->insert(['isbn' => 'Kindred', 'due' => '2026-10-26']);
$due->insert(['isbn' => 'Solaris', 'due' => '2026-10-12']);
$due->insert(['isbn' => 'Dawn', 'due' => '2026-11-02']);
echo 'next due: ', $due->top()['isbn'], "\n"; // next due: Solaris
foreach ($due as $loan) { // consumes the heap
echo "{$loan['due']} {$loan['isbn']}\n";
}
var_dump($due->isEmpty()); // bool(true)
$fines = new SplMaxHeap();
foreach ([250, 75, 1000, 125] as $cents) {
$fines->insert($cents);
}
echo 'largest fine: ', $fines->extract(), "\n"; // largest fine: 1000
Source: SplHeap, SplMinHeap and SplMaxHeap
SplPriorityQueue
SplPriorityQueue is a max-heap of values with separate priorities: insert($value, $priority) and extract()
returns the value with the highest priority. Elements with equal priority come out in an unspecified order; add a
decreasing counter to the priority (an array priority such as [$priority, PHP_INT_MAX - $seq]) if FIFO order among
equals matters.
<?php
declare(strict_types=1);
$jobs = new SplPriorityQueue();
$seq = PHP_INT_MAX;
$jobs->insert('send newsletter', [1, $seq--]);
$jobs->insert('notify overdue loans', [5, $seq--]);
$jobs->insert('rebuild search index', [1, $seq--]);
$jobs->insert('password reset e-mail', [10, $seq--]);
$jobs->setExtractFlags(SplPriorityQueue::EXTR_DATA); // only the values (the default)
while (!$jobs->isEmpty()) {
echo $jobs->extract(), "\n";
}
password reset e-mail
notify overdue loans
send newsletter
rebuild search index
In a web application, a durable job queue (database, Redis, SQS) replaces this in-memory structure; see Queues and Jobs.
Source: SplPriorityQueue
SplFixedArray
SplFixedArray is an array with a fixed size and integer keys 0..n-1 only, stored as a plain C array. It uses
less memory than a PHP array of the same length and rejects out-of-range keys with an exception, at the cost of
flexibility (resizing with setSize() is explicit).
<?php
declare(strict_types=1);
$copiesPerShelf = new SplFixedArray(4); // shelves 0..3, initialised to null
$copiesPerShelf[0] = 12;
$copiesPerShelf[3] = 7;
echo $copiesPerShelf->getSize(), ' shelves, shelf 3 has ', $copiesPerShelf[3], "\n";
try {
$copiesPerShelf[4] = 1;
} catch (OutOfBoundsException $e) {
echo get_class($e), ": ", $e->getMessage(), "\n"; // OutOfBoundsException: Index invalid or out of range
}
$before = memory_get_usage();
$fixed = SplFixedArray::fromArray(range(1, 100_000));
$fixedBytes = memory_get_usage() - $before;
$before = memory_get_usage();
$plain = range(1, 100_000);
$plainBytes = memory_get_usage() - $before;
printf("SplFixedArray: %d KiB, array: %d KiB\n", $fixedBytes / 1024, $plainBytes / 1024);
Since PHP 7 packed arrays are already compact, so the saving is modest; reach for SplFixedArray when the fixed-size,
integer-indexed semantics are what you want, not only for memory.
Source: SplFixedArray
SplObjectStorage
SplObjectStorage is a map from objects to data, or a set of objects, using object identity. It holds strong
references (unlike WeakMap). Since PHP 8.5 its attach(), detach() and contains() methods are deprecated in
favour of the array-style offsetSet()/offsetUnset()/offsetExists(), i.e. $storage[$obj] = $data,
unset($storage[$obj]) and isset($storage[$obj]).
<?php
declare(strict_types=1);
final class Member { public function __construct(public string $id) {} }
$alice = new Member('M-17');
$bob = new Member('M-03');
$reminders = new SplObjectStorage();
$reminders[$alice] = ['Kindred']; // 8.5 style (instead of attach())
$reminders[$bob] = ['Solaris', 'Dawn'];
var_dump(isset($reminders[$alice])); // bool(true) (instead of contains())
unset($reminders[$bob]); // (instead of detach())
foreach ($reminders as $i => $member) { // iterates the objects; data via getInfo()
echo $member->id, ': ', implode(', ', $reminders->getInfo()), "\n"; // M-17: Kindred
}
// Set operations on object sets
$a = new SplObjectStorage();
$a[$alice] = null;
$b = new SplObjectStorage();
$b[$bob] = null;
$a->addAll($b);
echo count($a), "\n"; // 2
For a cache attached to objects that may disappear, use WeakMap instead (see
References and Memory Management).
spl_object_id() gives an integer id for an object, unique among live objects; spl_object_hash() is deprecated
upcoming in PHP 8.6.
Source: SplObjectStorage and PHP 8.5 deprecated features
ArrayObject
ArrayObject wraps an array in an object implementing ArrayAccess, IteratorAggregate and Countable, so it is
passed by handle instead of copied, and can be extended. It is mostly a compatibility tool today: a final class with
a typed private array, plus the interfaces you need, is clearer (see the Catalogue example in
Inheritance, Interfaces and Abstract
Classes). Since 8.5, wrapping an object (rather than an array) in ArrayObject or ArrayIterator is deprecated.
<?php
declare(strict_types=1);
$shelf = new ArrayObject(['Solaris', 'Kindred']);
$alias = $shelf; // same object, not a copy
$alias[] = 'Dawn';
echo count($shelf), "\n"; // 3
$shelf->asort();
echo implode(', ', $shelf->getArrayCopy()), "\n"; // Dawn, Kindred, Solaris (keys kept: 2, 1, 0)
Source: ArrayObject
When a plain array is better
| Need | Use | Why |
|---|---|---|
list, map, set of scalars |
|
fastest and most memory-efficient in PHP 7+; huge function library |
FIFO queue with many removals from the front |
|
|
always take the smallest/largest/most urgent |
|
O(log n) instead of re-sorting |
set or map keyed by objects |
|
arrays cannot use objects as keys |
fixed-size integer-indexed buffer |
|
enforced bounds, compact |
Beyond the SPL, the ds extension (PECL) offers Ds\Vector, Ds\Map, Ds\Set and others with consistent
semantics; it is not bundled with PHP.
Where older books and tutorials are outdated
-
SplObjectStorage::attach()/detach()/contains()(Beak ch. 10) are deprecated since 8.5. -
Memory claims: books written for PHP 5 show
SplFixedArrayusing a fraction of an array’s memory; since PHP 7’s packed arrays the difference is much smaller. -
ArrayObjectwith objects is deprecated since 8.5, andspl_object_hash()upcoming in 8.6.