PHP 7 Type Safe Arrays
PHP 7 introduced scalar type declarations and return type hints, which let you enforce types on function parameters and return values. But what about arrays? PHP still allows you to mix any data type inside a single array, which can lead to subtle bugs when your code assumes all elements are of a certain type.
This post shows how to create type-safe array collections in PHP 7 - inspired by Java Generics - so your code can guarantee the type of data stored and retrieved from arrays.
The Problem
Without type safety, an array can contain anything:
$items = [new Product(), "string", 42, null];Later, when you iterate over $items assuming they're all Product objects, you'll get a TypeError or unexpected behaviour. PHP won't stop you from mixing types in the first place.
The Solution: A Generic TypedList Class
Here's a simple implementation that enforces type safety using PHP 7's type declarations:
<?php declare(strict_types=1);
class TypedList {
private array $items = [];
private string $expectedType;
public function __construct(string $expectedType) {
$this->expectedType = $expectedType;
}
public function add(object $item): void {
if (!$item instanceof $this->expectedType) {
throw new TypeError(
"Expected item of type {$this->expectedType}, " .
"got " . get_class($item)
);
}
$this->items[] = $item;
}
public function get(int $index): ?object {
return $this->items[$index] ?? null;
}
public function getAll(): array {
return $this->items;
}
public function count(): int {
return count($this->items);
}
}Usage Example
First, define your class:
<?php
class Product {
public function __construct(
private string $name,
private float $price
) {}
public function getName(): string {
return $this->name;
}
public function getPrice(): float {
return $this->price;
}
}Then use it with your type-safe list:
<?php
$products = new TypedList(Product::class);
// This works
$products->add(new Product("Laptop", 999.99));
$products->add(new Product("Mouse", 29.99));
// This throws a TypeError
$products->add("not a product"); // TypeError: Expected item of type Product, got String
// Retrieve safely
$product = $products->get(0);
echo $product->getName(); // "Laptop"Why This Matters
With this pattern:
- Early failure - Type mismatches are caught at the point of insertion, not deep in your business logic.
- Self-documenting code - The TypedList constructor makes it clear what type of items the collection expects.
- Refactoring safety - If you change the expected type, every wrong usage throws immediately.
- No runtime surprises - Your iteration loops can trust that every element is the correct type.
Extending for Scalar Types
If you want to store scalar types (strings, ints, etc.) instead of objects, you can modify the add() method:
<?php
class ScalarList {
private array $items = [];
private string $expectedType;
public function __construct(string $expectedType) {
$this->expectedType = $expectedType;
}
public function add(mixed $item): void {
if (gettype($item) !== $this->expectedType) {
throw new TypeError(
"Expected type {$this->expectedType}, " .
"got " . gettype($item)
);
}
$this->items[] = $item;
}
public function get(int $index): mixed {
return $this->items[$index] ?? null;
}
}
// Usage
$names = new ScalarList('string');
$names->add("Alice");
$names->add("Bob");
// $names->add(42); // TypeError