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.

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

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.

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.

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

array

fastest and most memory-efficient in PHP 7+; huge function library

FIFO queue with many removals from the front

SplQueue

array_shift() is O(n)

always take the smallest/largest/most urgent

SplMinHeap, SplMaxHeap, SplPriorityQueue

O(log n) instead of re-sorting

set or map keyed by objects

SplObjectStorage, or WeakMap for caches

arrays cannot use objects as keys

fixed-size integer-indexed buffer

SplFixedArray

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 SplFixedArray using a fraction of an array’s memory; since PHP 7’s packed arrays the difference is much smaller.

  • ArrayObject with objects is deprecated since 8.5, and spl_object_hash() upcoming in 8.6.