skip to content

In PHP, what does SplFixedArray give up compared with a plain array, and when is it actually worth using?

level: seniorimportance: nice to knowfreq 14%

answer

  1. integer indexes 0 to size-1 only
  2. no [] append; setSize() to grow
  3. OutOfBoundsException since 8.4
  4. an object: handle, not copy-on-write
  5. array_* functions reject it

basics

~20 s

SplFixedArray gives up string keys, appending with [], the array_* functions and copy-on-write value semantics. In return it holds exactly the requested number of integer-indexed slots, which can save memory and time for large numeric lists of known size.

solid answer

~40 s

`SplFixedArray` is a fixed-size list: indexes run from `0` to `getSize() - 1`, you size it up front (`new SplFixedArray(1000)` or `setSize()`), and `$fa[] = $x` throws `Error` because appending is not supported. An out-of-range index throws `OutOfBoundsException` (since PHP 8.4; a `RuntimeException` before, its parent class). It is an **object**, so passing it to a function shares it instead of copying on write, and functions like `array_map()` or `sort()` reject it; you convert with `toArray()` first. What you gain is exact allocation and a tight index path, useful for large numeric buffers in long-running or CLI code. In current PHP a list-shaped array already stores its values compactly, so I measure with `memory_get_usage()` before switching; for most application code the plain array wins on ergonomics.

code

php · 17 lines
php
<?php
declare(strict_types=1);

$window = new SplFixedArray(3);
foreach ([12, 15, 11] as $i => $ms) {
    $window[$i] = $ms;
}

try {
    $window[3] = 20;               // index outside 0..2
} catch (OutOfBoundsException $e) {
    echo $e->getMessage(), "\n"; // Index invalid or out of range
}

$window->setSize(4);
$window[3] = 20;                   // fine after resizing
echo array_sum($window->toArray()), "\n"; // 58

go deeper

for a junior

Know that SplFixedArray has a fixed size, integer indexes from zero, and needs toArray() before using array functions.

for a middle

Explain the exceptions it throws, the Error on [] append, and that it is shared as an object handle rather than copied on write.

for a senior

Justify it only with measured memory or speed gains on large numeric buffers, knowing packed arrays already store values compactly in current PHP.

for a principal

Treat micro-structures as a last resort after algorithmic and architectural fixes, and require a benchmark before trading array ergonomics for them.

## What SplFixedArray is `SplFixedArray` is an SPL class holding a **fixed number of slots** indexed by integers from `0` to `size - 1`. It behaves like an array for reads and writes by index (`$fa[3] = 'x'`), counts with `count()`, and is iterable with `foreach`. Everything else about it follows from two facts: its size is explicit, and it is an object rather than an array. ```php $buffer = new SplFixedArray(3); // [null, null, null] $buffer[0] = 10; $buffer->setSize(5); // grow; new slots are null $list = SplFixedArray::fromArray([4, 5, 6]); $plain = $list->toArray(); // back to a PHP array ``` ## What it gives up | Plain array can | `SplFixedArray` instead | |---|---| | use string keys and sparse integer keys | only integers `0` to `size - 1` | | grow with `$a[] = $x` | `$fa[] = $x` throws `Error`; call `setSize()` | | return `null` with a warning for a missing key | throws `OutOfBoundsException` for an out-of-range index | | go through `array_map()`, `sort()`, `in_array()` | rejected with `TypeError`; convert with `toArray()` | | be copied on write when passed or assigned | shared as an object handle | Two of these deserve emphasis. - **Value semantics.** A PHP array assigned to another variable or passed to a function behaves as an independent copy (copied lazily, on first write). An `SplFixedArray` is an object: every variable holds a handle to the same instance, so a function that writes into it changes the caller's data. - **`fromArray()` needs integer keys.** `SplFixedArray::fromArray(['a' => 1])` throws `InvalidArgumentException` ("array must contain only positive integer keys"). With `$preserveKeys` left at `true`, integer keys keep their positions and gaps become `null`; pass `false` to pack the values from index `0`. ## What it gains 1. **Exact allocation.** A PHP array that grows by appending doubles its capacity when full, so it can carry unused slots. `SplFixedArray` allocates exactly the size you ask for. 2. **No key bookkeeping.** There are no keys to store or hash; an index maps straight to a slot. PHP 8.5 also sped up its dimension accessors and methods. 3. **Enforced shape.** Out-of-range writes fail loudly instead of silently creating a new key, which can be the point in numeric code. The memory argument is weaker than older blog posts suggest. In current PHP a **list-shaped (packed) array** already stores bare values contiguously, without per-element keys, so the difference is mostly spare capacity and a small header. A hash-shaped array (string or sparse keys) costs noticeably more per element. Measure with `memory_get_usage()` on your real data rather than assuming. ## Version history worth knowing - **PHP 8.0**: `SplFixedArray` became an `IteratorAggregate` instead of an `Iterator`; `foreach` gets a fresh iterator from `getIterator()`, so nested loops over one instance work correctly. Code that called `rewind()`, `current()` or `next()` on it directly broke. - **PHP 8.1**: `json_encode()` encodes it like an array. - **PHP 8.4**: out-of-bounds access throws `OutOfBoundsException` instead of `RuntimeException`; since the new class extends the old one, existing `catch (RuntimeException $e)` blocks still work. `__wakeup()` was deprecated in favour of `__serialize()`/`__unserialize()`. - **PHP 8.5**: faster dimension access and methods. ## Iterating and converting - `foreach` over an `SplFixedArray` yields every slot, including the `null` ones you never wrote, with integer keys from `0`. - `toArray()` returns a list of the same size, and `jsonSerialize()` is an alias of it, which is why JSON output looks like an array. - `getSize()` and `count()` both report the slot count, not the number of non-null values. ## When it is worth it - A **large numeric buffer of known size** in a CLI job or long-running worker: a sample window, a lookup table indexed `0..n`, a grid. - Code where an **out-of-range index is a bug** you want to surface immediately. It is not worth it for everyday application data: request payloads, rows from a database, anything with string keys, or anything you will filter, sort or map. There the plain array's functions and value semantics are worth far more than a few kilobytes, and the conversion cost of `toArray()` eats the gain.

  • What changed in PHP 8.0 that made nested foreach loops over one SplFixedArray safe?
    Before 8.0, `SplFixedArray` implemented `Iterator`, so the instance itself held the loop position and nested loops interfered. Since 8.0 it implements `IteratorAggregate`: each `foreach` calls `getIterator()` and gets its own cursor. Code that called `current()` or `next()` on the object directly had to switch to `getIterator()`.
  • Does catching RuntimeException still work for SplFixedArray index errors in PHP 8.5?
    Yes. Since PHP 8.4 out-of-range access throws `OutOfBoundsException`, which extends `RuntimeException`, so an older `catch (RuntimeException $e)` still catches it. New code can catch the narrower class.

saying these in an interview costs you the question

  • SplFixedArray always uses far less memory than any array
  • $fixed[] = $value appends and grows the SplFixedArray
  • array_map() and sort() accept an SplFixedArray directly
  • Passing an SplFixedArray to a function copies it like an array
  • Out-of-range reads return null with a warning