Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Встроенные интерфейсы

Функция count(), цикл foreach, квадратные скобки и json_encode() работают не только с массивами. Объект тоже может их поддерживать — для этого класс реализует один из встроенных интерфейсов PHP.

Countable

Интерфейс Countable позволяет передать объект в 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('чай');
$cart->add('кофе');
 
echo count($cart); // 2

IteratorAggregate

Чтобы объект можно было перебирать в foreach, он возвращает итератор из метода getIterator():

<?php
 
class Playlist implements IteratorAggregate
{
    public function __construct(private array $songs) {}
 
    public function getIterator(): Iterator
    {
        return new ArrayIterator($this->songs);
    }
}
 
foreach (new Playlist(['Вступление', 'Финал']) as $number => $song) {
    echo $number + 1, '. ', $song, "\n";
}

ArrayIterator — готовый итератор по массиву. Вместо него метод может быть генератором: yield from $this->songs;.

ArrayAccess

ArrayAccess даёт объекту квадратные скобки. Нужны четыре метода — проверка, чтение, запись и удаление:

<?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']) ? 'есть' : 'нет'; // нет

JsonSerializable

По умолчанию json_encode() выводит только открытые свойства объекта. Метод jsonSerialize() позволяет решить самому, что попадёт в 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('Анна', 'secret'), JSON_UNESCAPED_UNICODE); // {"name":"Анна"}

Пароль в ответ не попал.

Stringable

Класс с методом __toString() автоматически реализует Stringable. Этот тип удобно указывать в параметрах: функция примет и строку, и объект, умеющий стать строкой:

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

Сводка

ИнтерфейсЧто начинает работать
Countablecount($object)
IteratorAggregate, Iteratorforeach ($object as …)
ArrayAccess$object['key']
JsonSerializablejson_encode($object)
Stringableecho $object, приведение к строке

Частая ошибка

Объект с этими интерфейсами похож на массив, но массивом не является. Функции array_map(), in_array(), implode() его не примут. Сначала превратите объект в массив функцией iterator_to_array():

<?php
 
$list = new ArrayObject(['чай', 'кофе']);
 
try {
    echo implode(', ', array_map('strtoupper', $list));
} catch (TypeError $e) {
    echo "Ошибка: объект — не массив\n";
}
 
echo implode(', ', iterator_to_array($list)); // чай, кофе

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.