Управление коллекциями объектов в PHP
Массив классов в PHP означает хранение экземпляров (объектов) классов в массиве. Это позволяет обрабатывать множество однотипных или разнотипных объектов единообразно. Рассмотрим несколько подходов с примерами.
Основное решение: типизированный массив объектов одного класса
Как создать массив, содержащий объекты одного класса, и обеспечить типобезопасность?
Самый эффективный способ – использовать PHP 8+ с указанием типа элементов через PHPDoc @var array<ClassName> или в аргументах функций с проверкой. Классический подход – хранить объекты в обычном массиве, но при добавлении проверять тип.
class User {
public function __construct(
public string $name,
public int $age
) {}
}
$users = [];
$users[] = new User('Анна', 25);
$users[] = new User('Иван', 30);
foreach ($users as $user) {
echo $user->name . ' - ' . $user->age . ' лет' . PHP_EOL;
}
массив классов php (массив классов в php)
Анна - 25 лет Иван - 30 лет
Пояснение: создаётся пустой массив, в который добавляются объекты класса User. При итерации каждый элемент гарантированно является объектом User (если не примешивать другие типы). При попытке добавить строку возникнет ошибка только во время выполнения.
Проблема: объекты хранятся по ссылке, поэтому изменение объекта через переменную изменяет и элемент массива. Для копирования используйте clone. Другая ошибка – попытка обратиться к несуществующему свойству или методу, если массив содержит объекты разных классов.
Решение: при неоднородном массиве проверяйте класс через instanceof.
Вариант 1: Ассоциативный массив объектов
Как организовать быстрый доступ к объектам по уникальному ключу (например, ID)?
class Product {
public function __construct(
public int $id,
public string $title,
public float $price
) {}
}
$products = [];
$products[101] = new Product(101, 'Книга', 500);
$products[102] = new Product(102, 'Ручка', 30);
// Получение объекта по ключу
echo $products[101]->title; // Книга
Цель: использование идентификатора в качестве ключа для прямого доступа (без цикла). Удобно для поиска и обновления.
Проблема: дублирование ключа приведёт к перезаписи объекта. Решение: проверять существование ключа через isset() или array_key_exists().
Вариант 2: Массив объектов разных классов (полиморфизм)
Как хранить объекты, реализующие общий интерфейс или наследующие общий класс?
interface Animal {
public function sound(): string;
}
class Dog implements Animal {
public function sound(): string {
return 'Гав';
}
}
class Cat implements Animal {
public function sound(): string {
return 'Мяу';
}
}
$animals = [new Dog(), new Cat()];
foreach ($animals as $animal) {
echo $animal->sound() . PHP_EOL;
}
Гав Мяу
Цель: единообразная обработка объектов разных классов через общий интерфейс. Тип элементов можно задать как array<Animal>.
Проблема: если добавить объект, не реализующий интерфейс, возникнет ошибка при вызове метода. Решение: использовать проверку instanceof Animal перед добавлением или типовые подсказки в IDE.
Вариант 3: Использование SplObjectStorage
Как хранить объекты с гарантией уникальности (без дубликатов) и возможностью привязки данных?
$storage = new SplObjectStorage();
$user1 = new User('Анна', 25);
$user2 = new User('Иван', 30);
$storage->attach($user1, 'данные1');
$storage->attach($user2, 'данные2');
// Попытка добавить тот же объект игнорируется
$storage->attach($user1);
echo $storage->count(); // 2
// Доступ к данным
$storage->rewind();
while ($storage->valid()) {
$obj = $storage->current();
$data = $storage->getInfo();
echo $obj->name . ': ' . $data . PHP_EOL;
$storage->next();
}
Цель: когда требуется коллекция объектов, где каждый объект может встречаться только один раз, и нужно сопоставить объекту произвольные данные. SplObjectStorage эффективен для управления ссылками.
Проблема: объекты не сериализуются по умолчанию, возможны утечки памяти при длительном хранении. Решение: очищать storage или использовать WeakMap.
Вариант 4: Массив строк с именами классов (динамическое создание)
Как генерировать объекты из заранее определённого списка классов?
$classNames = ['User', 'Admin', 'Guest'];
$objects = [];
foreach ($classNames as $className) {
if (class_exists($className)) {
$objects[] = new $className();
}
}
Цель: гибкость для систем с подключаемыми модулями, когда классы неизвестны заранее. Требует осторожной проверки существования класса.
Проблема: если класс не существует, возникает фатальная ошибка. Решение: использовать class_exists() и try/catch. Также возможны проблемы с пространством имён.
Расширенные примеры работы с массивами объектов
Далее приведены продвинутые сценарии: фильтрация, сортировка, трансформация, итерация с генераторами.
Фильтрация массива объектов с array_filter
class User {
public function __construct(
public string $name,
public int $age,
public string $role
) {}
}
$users = [
new User('Анна', 25, 'admin'),
new User('Иван', 30, 'user'),
new User('Ольга', 20, 'moderator'),
];
$admins = array_filter($users, fn($u) => $u->role === 'admin');
foreach ($admins as $admin) {
echo $admin->name . PHP_EOL;
}
Анна
Пояснение: array_filter оставляет только элементы, для которых callback возвращает true. Ключи сохраняются – можно использовать array_values для переиндексации.
Сортировка объектов с usort
usort($users, fn($a, $b) => $a->age <=> $b->age);
// Результат
foreach ($users as $u) {
echo $u->name . ' (' . $u->age . ')' . PHP_EOL;
}
Ольга (20) Анна (25) Иван (30)
Пояснение: usort изменяет оригинальный массив. Для неизменяемости сначала клонируйте объекты или создайте копию массива.
Трансформация объектов с array_map
$names = array_map(fn($u) => $u->name, $users);
print_r($names);
Array
(
[0] => Ольга
[1] => Анна
[2] => Иван
)
Пояснение: array_map возвращает новый массив с результатами callback. Не изменяет исходный.
Использование генератора для ленивой загрузки
function getUsersFromDb(): Generator {
$ids = [1, 2, 3];
foreach ($ids as $id) {
// Имитация загрузки объекта
yield new User("User{$id}", 20 + $id, 'user');
}
}
foreach (getUsersFromDb() as $user) {
echo $user->name . PHP_EOL;
}
User1 User2 User3
Пояснение: генераторы не создают весь массив сразу, экономят память при работе с большими наборами.
Коллекция с типизацией через ArrayObject
class UserCollection extends ArrayObject {
public function offsetSet(mixed $key, mixed $value): void {
if (!$value instanceof User) {
throw new InvalidArgumentException('Only User objects allowed');
}
parent::offsetSet($key, $value);
}
}
$collection = new UserCollection();
$collection[] = new User('Анна', 25);
// $collection[] = 'string'; // вызовет исключение
Создание собственного класса-коллекции гарантирует тип элементов во время выполнения. ArrayObject даёт доступ как к массиву и поддерживает итерацию.
Обработка смешанных типов и безопасное обращение
$mixed = [new User('Анна', 25), 'строки', null];
foreach ($mixed as $item) {
if ($item instanceof User) {
echo $item->name . PHP_EOL;
} else {
echo 'Не пользователь' . PHP_EOL;
}
}
Анна Не пользователь Не пользователь
Всегда проверяйте тип с instanceof перед вызовом методов, чтобы избежать фатальных ошибок.