SPL Data Structures
What Are SPL Data Structures? In simple terms: PHP's array is a Swiss Army knife — it works as a list, a dictionary, a stack, and a queue. But some tasks have a better-suited tool: a stack for undo hi
What Are SPL Data Structures?
In simple terms: PHP's array is a Swiss Army knife — it works as a list, a dictionary, a stack, and a queue. But some tasks have a better-suited tool: a stack for undo history, a queue for jobs processed in order, a priority queue for urgent tasks first, or a heap for always finding the smallest value quickly. The Standard PHP Library (SPL) provides these structures as ready-made classes.
Technically: SPL (always available) includes data structure classes such as SplStack, SplQueue, SplDoublyLinkedList, SplPriorityQueue, SplMinHeap, SplMaxHeap, SplFixedArray, and SplObjectStorage. Each has well-defined behavior and performance characteristics, and they are objects — they can be passed around, type-hinted, iterated, and counted.
Overview
| Class | Behavior | Typical Use |
|---|---|---|
SplStack | Last in, first out (LIFO) | Undo history, navigation history, parsing |
SplQueue | First in, first out (FIFO) | Job queues, breadth-first processing |
SplPriorityQueue | Highest priority first | Task scheduling, support tickets |
SplMinHeap / SplMaxHeap | Always gives the smallest / largest element | Top-N problems, shortest delivery times |
SplFixedArray | Fixed size, integer indexes only | Large numeric datasets with lower memory use |
SplObjectStorage | Objects as keys (sets and maps) | Tracking objects, attaching data to objects |
SplStack: Last In, First Out
<?php
$undo = new SplStack();
$undo->push('Typed "Hello"');
$undo->push('Made text bold');
$undo->push('Deleted paragraph');
echo "Undo: " . $undo->pop() . "\n";
echo "Next undo would be: " . $undo->top() . "\n";
echo "Remaining actions: " . count($undo) . "\n";
foreach ($undo as $action) {
echo " $action\n";
}
Output
Undo: Deleted paragraph
Next undo would be: Made text bold
Remaining actions: 2
Made text bold
Typed "Hello"
push()adds to the top;pop()removes and returns the top element;top()reads it without removing.- Iterating a stack goes from top to bottom — the most recent action first.
- Calling
pop()ortop()on an empty stack throws aRuntimeException; checkisEmpty()first.
SplQueue: First In, First Out
<?php
$printJobs = new SplQueue();
$printJobs->enqueue('Invoice #501');
$printJobs->enqueue('Invoice #502');
$printJobs->enqueue('Packing slip #501');
while (!$printJobs->isEmpty()) {
echo "Printing " . $printJobs->dequeue() . "\n";
}
Output
Printing Invoice #501
Printing Invoice #502
Printing Packing slip #501
Jobs are processed in the order they arrived. dequeue() removes the element at the front in constant time; with an array, array_shift() must re-index the whole array on every call, which becomes slow for large queues.
SplPriorityQueue: Most Important First
<?php
$tickets = new SplPriorityQueue();
$tickets->insert('Password reset request', 10);
$tickets->insert('Website is down', 100);
$tickets->insert('Typo on About page', 1);
$tickets->insert('Payment failed for order 501', 50);
while (!$tickets->isEmpty()) {
echo $tickets->extract() . "\n";
}
Output
Website is down
Payment failed for order 501
Password reset request
Typo on About page
insert($value, $priority)adds an element with a priority;extract()always returns the element with the highest priority.- Internally, a heap keeps insertion and extraction fast (logarithmic time), even with many elements.
- The order of elements with equal priority is not guaranteed. If ties must be processed in arrival order, use a compound priority such as
[$priority, -$sequenceNumber].
Heaps: Always the Smallest or Largest
<?php
$deliveryMinutes = new SplMinHeap();
foreach ([45, 12, 30, 5, 60, 18] as $minutes) {
$deliveryMinutes->insert($minutes);
}
echo "Fastest three delivery partners: ";
for ($i = 0; $i < 3; $i++) {
echo $deliveryMinutes->extract() . ' min ';
}
echo "\n";
Output
Fastest three delivery partners: 5 min 12 min 18 min
SplMinHeap always extracts the smallest value; SplMaxHeap the largest. For complex elements, extend SplHeap and implement compare():
<?php
final class CheapestProductHeap extends SplHeap
{
protected function compare(mixed $a, mixed $b): int
{
return $b['price'] <=> $a['price']; // smaller price = higher in the heap
}
}
$heap = new CheapestProductHeap();
$heap->insert(['name' => 'Desk Lamp', 'price' => 1299]);
$heap->insert(['name' => 'Gel Pen', 'price' => 20]);
$heap->insert(['name' => 'Notebook', 'price' => 60]);
echo $heap->top()['name'] . "\n";
Output
Gel Pen
compare() returns a positive number if $a should come before $b. Heaps are ideal when you repeatedly need "the next smallest/largest" while elements keep arriving — fully sorting the data each time would be wasteful.
SplFixedArray: Fixed Size, Integer Indexes
<?php
$hourlyVisits = new SplFixedArray(24);
$hourlyVisits[9] = 120;
$hourlyVisits[13] = 340;
echo "Size: " . $hourlyVisits->getSize() . "\n";
echo "13:00 visits: " . $hourlyVisits[13] . "\n";
var_dump($hourlyVisits[0]);
try {
$hourlyVisits[24] = 5;
} catch (RuntimeException $e) {
echo get_class($e) . ": " . $e->getMessage() . "\n";
}
Output
Size: 24
13:00 visits: 340
NULL
RuntimeException: Index invalid or out of range
- The size is fixed at creation (it can be changed explicitly with
setSize()), and only integer indexes from 0 to size−1 are allowed. - Unset positions contain
null. - Because it stores values in a simple contiguous block, it uses noticeably less memory than an equivalent PHP array for large numeric datasets.
SplObjectStorage: Objects as Keys
Arrays cannot use objects as keys. SplObjectStorage can — making it a set of objects or a map from objects to data:
<?php
final class Customer
{
public function __construct(public string $name) {}
}
$asha = new Customer('Asha');
$bala = new Customer('Bala');
$loyaltyPoints = new SplObjectStorage();
$loyaltyPoints[$asha] = 120;
$loyaltyPoints[$bala] = 45;
$loyaltyPoints[$asha] += 30;
foreach ($loyaltyPoints as $customer) {
echo "{$customer->name}: {$loyaltyPoints[$customer]} points\n";
}
var_dump($loyaltyPoints->contains(new Customer('Asha')));
Output
Asha: 150 points
Bala: 45 points
bool(false)
- Objects are identified by identity: a different object with the same name is not contained.
- Iterating yields the objects; the attached data is read with
$storage[$object].
WeakMap (PHP 8.0+) is a related class: it also maps objects to data, but does not keep the objects alive — when an object is no longer used elsewhere, its entry disappears automatically. It is ideal for caches attached to objects.
Arrays or SPL?
| Use Arrays When | Use SPL Structures When |
|---|---|
| The data is small or medium-sized | Large queues where array_shift() would be slow |
You need array functions (array_map, array_filter, sorting) | You need priority ordering or a heap |
| Keys are strings or mixed | Keys are objects (SplObjectStorage, WeakMap) |
| The data is passed to JSON, templates, or databases | The structure's behavior (LIFO, FIFO) should be explicit in the code |
For most everyday code, arrays remain the simplest choice. Reach for SPL when a structure's specific behavior or performance matters.
Common Mistakes
Popping or Extracting From Empty Structures
Effect: RuntimeException.
Fix: Check isEmpty() or count() first.
Relying on Order for Equal Priorities
Effect: Items with the same priority come out in an unexpected order.
Fix: Add a tie-breaker to the priority.
Using array_shift() for Large Queues
Effect: Each call re-indexes the array, making processing slow.
Fix: Use SplQueue.
Expecting SplObjectStorage to Compare by Value
Effect: Equal-looking objects are treated as different keys.
Fix: Use the same instance, or key by an identifier in a regular array.
Best Practices
- Use
SplStackandSplQueueto express LIFO and FIFO behavior clearly. - Use
SplPriorityQueueand heaps for "most important / smallest first" processing. - Use
SplObjectStorageorWeakMapwhen objects must act as keys. - Use
SplFixedArrayfor large, fixed-size numeric datasets. - Default to arrays for ordinary data and switch to SPL when behavior or performance requires it.
Official reference: https\://www.php.net/manual/en/spl.datastructures.php