Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Built-in interfaces

The count() function, the foreach loop, square brackets and json_encode() work with more than arrays. An object can support them too — the class implements one of PHP's built-in interfaces.

Countable

The Countable interface lets you pass an object to count():

<?php
 
class Cart implements Countable
{
    private array $items = [];
 
    public function add(string $item): void
    {
        $this->items[] = $item;
    }
 
    public function count(): int
    {
        return count($this->items);
    }
}
 
$cart = new Cart();
$cart->add('tea');
$cart->add('coffee');
 
echo count($cart); // 2

IteratorAggregate

For an object to be looped over with foreach, it returns an iterator from the getIterator() method:

<?php
 
class Playlist implements IteratorAggregate
{
    public function __construct(private array $songs) {}
 
    public function getIterator(): Iterator
    {
        return new ArrayIterator($this->songs);
    }
}
 
foreach (new Playlist(['Intro', 'Finale']) as $number => $song) {
    echo $number + 1, '. ', $song, "\n";
}

ArrayIterator is a ready-made iterator over an array. The method can be a generator instead: yield from $this->songs;.

ArrayAccess

ArrayAccess gives an object square brackets. Four methods are needed — checking, reading, writing and removing:

<?php
 
class Settings implements ArrayAccess
{
    private array $data = [];
 
    public function offsetExists(mixed $offset): bool
    {
        return isset($this->data[$offset]);
    }
 
    public function offsetGet(mixed $offset): mixed
    {
        return $this->data[$offset] ?? null;
    }
 
    public function offsetSet(mixed $offset, mixed $value): void
    {
        $this->data[$offset] = $value;
    }
 
    public function offsetUnset(mixed $offset): void
    {
        unset($this->data[$offset]);
    }
}
 
$settings = new Settings();
$settings['theme'] = 'dark';
 
echo $settings['theme'], "\n";                // dark
echo isset($settings['size']) ? 'yes' : 'no'; // no

JsonSerializable

By default json_encode() outputs only an object's public properties. The jsonSerialize() method lets you decide what goes into the JSON:

<?php
 
class User implements JsonSerializable
{
    public function __construct(
        private string $name,
        private string $password,
    ) {}
 
    public function jsonSerialize(): array
    {
        return ['name' => $this->name];
    }
}
 
echo json_encode(new User('Anna', 'secret')); // {"name":"Anna"}

The password did not get into the response.

Stringable

A class with a __toString() method implements Stringable automatically. The type is handy in parameters: a function accepts both a string and an object that can become one:

<?php
 
class Money
{
    public function __construct(private int $amount) {}
 
    public function __toString(): string
    {
        return '$' . number_format($this->amount);
    }
}
 
function show(string|Stringable $value): void
{
    echo $value;
}
 
show(new Money(12500)); // $12,500

Summary

InterfaceWhat starts working
Countablecount($object)
IteratorAggregate, Iteratorforeach ($object as …)
ArrayAccess$object['key']
JsonSerializablejson_encode($object)
Stringableecho $object, casting to a string

Common mistake

An object with these interfaces resembles an array but is not one. array_map(), in_array() and implode() do not accept it. Turn the object into an array with iterator_to_array() first:

<?php
 
$list = new ArrayObject(['tea', 'coffee']);
 
try {
    echo implode(', ', array_map('strtoupper', $list));
} catch (TypeError $e) {
    echo "Error: an object is not an array\n";
}
 
echo implode(', ', iterator_to_array($list)); // tea, coffee

Thanks for reading!

Donate to the site — we work hard for you.