Skip to Content
Course content

125: SPL Data Structures: SplStack, SplQueue, ArrayObject

Click on the "Edit" button in the top corner of the screen to edit your slide content.

For most of your career in PHP, you'll probably lean on the standard array for everything. It's the Swiss Army knife of the language. But there comes a point where using a generic array for a specific data structure isn't just "not ideal"—it's actually a performance bottleneck or a source of architectural confusion. That's where the Standard PHP Library (SPL) data structures come in. Specifically, SplStack, SplQueue, and ArrayObject.

The Performance Trap of array_shift

Let's say you're building a simple background job processor. You have a list of emails to send, and you want to process them in the order they arrived (First-In, First-Out). The naive approach is to use a standard array with array_push() to add items and array_shift() to pull them off the front.

$queue = [];
$queue[] = 'job_1';
$queue[] = 'job_2';
$queue[] = 'job_3';

while (!empty($queue)) {
    $job = array_shift($queue);
    // Process job...
}

This works perfectly fine when you have ten jobs. But here is the catch: array_shift() is expensive. Every time you pull an element off the front of a standard array, PHP has to re-index every single remaining element. If you have 10,000 items in that queue, you're performing thousands of unnecessary operations just to move the pointer. It's a silent killer of performance.

I usually tell my juniors to switch to SplQueue the moment the "list" serves a specific purpose. SplQueue is implemented as a doubly linked list, meaning removing an element from the front is an O(1) operation. It doesn't matter if you have ten items or ten million; the cost is the same.

$queue = new SplQueue();
$queue->enqueue('job_1');
$queue->enqueue('job_2');
$queue->enqueue('job_3');

while (!$queue->isEmpty()) {
    $job = $queue->dequeue();
    // Process job...
}

The same logic applies to SplStack (Last-In, First-Out). While array_pop() is actually quite efficient, using SplStack makes your intent clear to anyone reading your code. When I see $stack->push() and $stack->pop(), I know exactly how the data is flowing without having to trace the array manipulations.

When Arrays Aren't Enough: The Object Wrapper

Now, ArrayObject is a different beast entirely. You'll run into a problem where you want an array, but you need it to behave like an object. This usually happens when you're passing a collection into a method that expects an object, or when you want to implement a specific interface (like Countable or IteratorAggregate) on your data set.

If you pass a standard array to a function, PHP passes it by value (mostly). Even with references, it can get messy. If you wrap that data in an ArrayObject, you're dealing with a real object. You can pass it around, and every part of your application is mutating the same instance.

$config = new ArrayObject([
    'db_host' => 'localhost',
    'db_user' => 'root'
]);

function updateConfig(ArrayObject $config) {
    $config['db_host'] = '127.0.0.1';
}

updateConfig($config);
echo $config['db_host']; // Outputs: 127.0.0.1

I find ArrayObject particularly useful when I'm building a Data Transfer Object (DTO) that needs to be flexible. You get the syntax of an array (the square brackets) but the power of an object. You can even override the offsetGet or offsetSet methods to add validation logic—something that's simply impossible with a primitive array.




📋 Practical Task

Implementation: Undo-Redo History Manager

You are building a text editor's history system. You need to implement a HistoryManager class that tracks changes to a document. Requirements:

  • Use an SplStack to store "Undo" actions.
  • Use an SplStack to store "Redo" actions.
  • Implement a saveAction($action) method: This should push the action onto the Undo stack and clear the Redo stack (since a new action invalidates the redo history).
  • Implement an undo() method: This should pop the last action from the Undo stack and push it onto the Redo stack.
  • Implement a redo() method: This should pop the last action from the Redo stack and push it back onto the Undo stack.

Create the class and demonstrate it by saving three different strings ("Typed 'Hello'", "Typed ' World'", "Deleted 'World'"), performing two undos, and then one redo. Print the current state of the Undo stack after each operation.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.