Сортировка массива объектов в PHP: от новичка до профи
Представьте ситуацию: вы пишете интернет-магазин на PHP. У вас есть массив товаров, каждый товар — это объект с полями price, name, rating. Пользователь нажимает «Сортировать по цене», и вам нужно мгновенно перестроить этот массив. Как это сделать правильно и красиво? Именно об этом наш сегодняшний разбор.
Тема сортировки массива объектов по одному свойству в PHP — один из самых популярных запросов на Stack Overflow (миллионы просмотров!). И неудивительно: работа с объектами — основа современного PHP, а умение гибко сортировать данные — must-have навык. Давайте разберем все от простого к сложному, с живыми примерами и пояснениями.
Почему нельзя просто использовать sort()?
Новички часто пытаются применить sort() или rsort() к массиву объектов. И получают ошибку или полную бессмыслицу. Почему? Потому что эти функции сравнивают элементы как простые значения (числа или строки), а объект — это сложная структура. PHP не знает, по какому полю вы хотите сортировать: по имени, цене или дате. Нужно явно указать правило сравнения.
Именно для этого существуют функции usort(), uasort() и uksort(). Буква «u» в названии означает «user-defined» — пользовательская функция сравнения. Мы сами пишем, как сравнивать два объекта.
Базовый пример: сортируем пользователей по возрасту
Допустим, у нас есть класс User и массив пользователей:
class User {
public $name;
public $age;
public function __construct($name, $age) {
$this->name = $name;
$this->age = $age;
}
}
$users = [
new User('Анна', 25),
new User('Иван', 30),
new User('Ольга', 22),
new User('Петр', 28)
];
Нам нужно отсортировать их по возрастанию возраста. Используем usort:
usort($users, function($a, $b) {
return $a->age <=> $b->age;
});
// Результат:
// Ольга (22), Анна (25), Петр (28), Иван (30)
Обратите внимание на spaceship operator ( <=> ) — он появился в PHP 7.0 и делает код невероятно лаконичным. Он возвращает -1, 0 или 1, что идеально подходит для функций сравнения.
Разбираем механизм работы usort
Функция usort() принимает два аргумента:
- Массив (передается по ссылке — исходный массив изменится)
- Callback-функцию, которая сравнивает два элемента
Callback должен вернуть:
- Отрицательное число (обычно -1) — если первый элемент меньше второго
- 0 — если элементы равны
- Положительное число (обычно 1) — если первый элемент больше второго
Spaceship operator делает это автоматически. Вот как выглядит та же сортировка без него:
usort($users, function($a, $b) {
if ($a->age == $b->age) return 0;
return ($a->age < $b->age) ? -1 : 1;
});
Разница очевидна: оператор <=> сокращает 4 строки до одной. Используйте его везде, где поддерживается версия PHP 7+.
Сортировка по строкам: учитываем регистр и локаль
С числами всё просто, но строки требуют внимания. Давайте отсортируем тех же пользователей по имени:
usort($users, function($a, $b) {
return strcmp($a->name, $b->name);
});
// Или с spaceship:
usort($users, function($a, $b) {
return $a->name <=> $b->name;
});
Но есть нюанс: spaceship учитывает регистр. Строка «apple» будет меньше «Banana», потому что код символа 'a' (97) меньше 'B' (66). Для регистронезависимой сортировки используйте strcasecmp():
usort($users, function($a, $b) {
return strcasecmp($a->name, $b->name);
});
А если у вас русские буквы и нужна сортировка по алфавиту с учетом локали — подключайте расширение intl и функцию Collator:
$collator = new Collator('ru_RU');
usort($users, function($a, $b) use ($collator) {
return $collator->compare($a->name, $b->name);
});
Сортировка по убыванию: просто меняем порядок
Чтобы отсортировать по убыванию, достаточно поменять местами $a и $b:
// По убыванию возраста
usort($users, function($a, $b) {
return $b->age <=> $a->age;
});
// Или просто умножьте результат на -1:
usort($users, function($a, $b) {
return -($a->age <=> $b->age);
});
Сохраняем ключи: uasort vs usort
Важное различие: usort() сбрасывает числовые ключи массива (индексы становятся 0,1,2…). Если вам нужно сохранить ассоциативные ключи или исходные индексы, используйте uasort():
$products = [
'id_5' => new Product('Ноутбук', 50000),
'id_3' => new Product('Мышь', 1500),
'id_8' => new Product('Клавиатура', 3000)
];
uasort($products, function($a, $b) {
return $a->price <=> $b->price;
});
// Ключи 'id_5', 'id_3', 'id_8' сохранятся!
А uksort() сортирует по ключам массива, а не по значениям — тоже полезно.
Продвинутый уровень: сортировка по нескольким полям
Часто нужно сортировать по одному полю, а при равенстве — по другому. Например, сначала по рейтингу (по убыванию), затем по цене (по возрастанию):
usort($products, function($a, $b) {
// Сначала сравниваем рейтинг (по убыванию)
$ratingCompare = $b->rating <=> $a->rating;
if ($ratingCompare !== 0) return $ratingCompare;
// Если рейтинг равен, сравниваем цену (