cloudcastle / collection
Production-ready PHP 8.1+ package (CloudCastle Collection).
Requires
- php: >=8.1
- cloudcastle/serialize: ^1.0
Requires (Dev)
- aimeos/map: ^3 || ^4
- deptrac/deptrac: ^3.0 || ^4.0
- doctrine/collections: ^2
- ergebnis/composer-normalize: ^2.45
- friendsofphp/php-cs-fixer: ^3.75
- icanhazstring/composer-unused: ^0.9
- illuminate/collections: ^10
- infection/infection: ^0.29 || ^0.33
- loophp/collection: ^7.6
- php-parallel-lint/php-parallel-lint: ^1.4
- phpdocumentor/shim: ^3.7
- phpmd/phpmd: ^2.15
- phpstan/extension-installer: ^1.4
- phpstan/phpstan: ^1.12 || ^2.1
- phpstan/phpstan-deprecation-rules: ^1.2 || ^2.0
- phpstan/phpstan-phpunit: ^1.4 || ^2.0
- phpstan/phpstan-strict-rules: ^1.6 || ^2.0
- phpunit/phpunit: ^10.5 || ^11.5
- psalm/plugin-phpunit: ^0.19 || ^0.20
- ramsey/collection: ^2
- rector/rector: ^1.2 || ^2.0
- roave/security-advisories: dev-latest
- squizlabs/php_codesniffer: ^3.12 || ^4.0
- vimeo/psalm: ^6.0
- webmozart/assert: ^1.11
This package is auto-updated.
Last update: 2026-07-16 17:24:07 UTC
README
Коллекции для PHP 8.1+ со строгой семантикой сравнений:
contains('1')≠contains(1), строка из данных никогда не выполняется как код, JSON падает исключением, а не тихимfalse. ~85 методов — функциональный суперсет четырёх популярных библиотек, ноль runtime-зависимостей.
Установка
composer require cloudcastle/collection
Требуется PHP 8.1+. Runtime-зависимостей нет.
Быстрый старт
<?php use CloudCastle\Collection\Collection; $orders = Collection::make([ ['id' => 1, 'customer' => 'alice', 'status' => 'paid', 'total' => 150], ['id' => 2, 'customer' => 'bob', 'status' => 'pending', 'total' => 70], ['id' => 3, 'customer' => 'alice', 'status' => 'paid', 'total' => 220], ]); // Цепочки: фильтр → группировка → агрегат → сортировка $report = $orders ->where('status', 'paid') // строгое сравнение: '1' не пройдёт за 1 ->groupBy('customer') ->map(static fn ($group) => $group->sum('total')) ->sortDesc(); // ['alice' => 370] // Статистика $orders->pluck('total')->avg(); // 146.66… $orders->pluck('total')->median(); // 150 // Безопасный JSON-цикл $json = $orders->toJson(); // исключение вместо тихого false // сериализация через cloudcastle/serialize: JSON по умолчанию, // PHP-формат с белым списком классов, DoS-лимиты, анти-POP $payload = $orders->toSerialized(); $restored = Collection::fromSerialized($payload); $restored = Collection::fromJson($json); // исключение на битом JSON, лимит глубины // Строгость как фича: классические ловушки PHP исключены Collection::make([0, '1', null])->contains('admin'); // false (а не true, как при ==) Collection::make([1, '1', true])->unique()->count(); // 3 (типы не схлопываются) // Строка из данных — данные, а не код Collection::make(['system', 'exec'])->contains('system'); // true — просто поиск значения
Возможности
- Создание —
make/wrapиз массива,Traversable,Arrayable, скаляра илиnull; генерацияtimes/range;fromJsonс fail-safe-валидацией и лимитом глубины. - Доступ и поиск —
first/last/getс default и предикатом,firstOrFail/soleс контрактными исключениями,firstWhere, строгийsearch. - Проверки —
has/hasAny, строгиеcontains/containsStrict/doesntContain,every/some,isEmpty/isNotEmpty. - Трансформация —
map/mapWithKeys/flatMap/pluck/keys/values/flip/collapse/flatten/pad/reverse/shuffle. - Фильтрация —
filter/reject, всё where-семейство (where/whereIn/whereBetween/whereNull/whereInstanceOf…), типобезопасныеunique/duplicates,only/except. - Срезы и разбиение —
slice/take*/skip*/nth/chunk/split/partition/groupBy/keyBy/countBy. - Объединение —
merge/mergeRecursive/combine/union/concat/zip/crossJoin, строгиеdiff*/intersect*. - Агрегация —
reduce/sum/avg/min/max/median/mode/implode/join; числовые строки считаются без потери типа и точности. - Сортировка —
sort/sortDesc/sortBy/sortByDesc/sortKeys*(sortBy — по Шварцу: ключ вычисляется один раз на элемент). - Мутаторы —
push/put/pull/forget/prepend/pop/shift/splice. - Higher-order —
each/tap/pipe/when/unless/whenEmpty/whenNotEmpty. - Интеграция —
ArrayAccess,Countable,IteratorAggregate,JsonSerializable, собственные контрактыArrayable/Jsonable; класс открыт для наследования (protected-хелперы — зафиксированный тестами контракт).
Функционально пакет покрывает возможности четырёх сравниваемых аналогов вместе взятых:
illuminate/collections, doctrine/collections, ramsey/collection, aimeos/map.
Безопасность как дифференциатор
- Строгие сравнения (
===) по умолчанию во всех операциях поиска и сравнения:contains/search/where*/diff/intersect/unique. Классические ловушки type juggling (0 == 'admin','0e123' == '0e456') исключены по построению. - Строка из данных никогда не вызывается как код:
contains('system')— это поиск значения, аgroupBy('trim')— извлечение поляtrim, не вызов функции. - Строковый callable получает только значение:
map('intval')не передаёт ключ вторым аргументом (уintvalэто база системы счисления — ловушка аналогов),filter('is_int')не падаетArgumentCountError. - Сериализация через cloudcastle/serialize:
toSerialized/fromSerialized— JSON/PHP/igbinary/подписанный форматы с fail-safe контрактом, DoS-лимитами и анти-POP защитой (белый список классов). - JSON fail-safe:
toJson()бросает исключение вместо тихогоfalse;fromJson()проверяет корректность, тип верхнего уровня и клэмпит глубину.
Возможности против аналогов
Сводная таблица (генерируется автоматически из tools/features-data.php; значения выверены
по коду и документации аналогов). Легенда: ✅ полностью · ⚠️ частично · 🔌 адаптер · ❌ нет · — неприменимо.
Сгенерировано из tools/features-data.php (факты о поддержке). Легенда: ✅ полностью · ⚠️ частично · 🔌 адаптер · ❌ нет · — неприменимо.
Создание
| Функция | Collection | illuminate | doctrine | ramsey | aimeos | 🏆 Победитель |
|---|---|---|---|---|---|---|
| make / wrap из любых источников (iterable, Arrayable, скаляр, null) | ✅ | ✅ | ⚠️ | ⚠️ | ✅ | Паритет |
| times / range (генерация) | ✅ | ✅ | ❌ | ❌ | ✅ | Паритет |
| fromJson с fail-safe валидацией (исключение, лимит глубины) | ✅ | ❌ | ❌ | ❌ | ⚠️ | Collection |
Доступ и поиск
| Функция | Collection | illuminate | doctrine | ramsey | aimeos | 🏆 Победитель |
|---|---|---|---|---|---|---|
| first / last / get с default и предикатом | ✅ | ✅ | ⚠️ | ⚠️ | ✅ | Паритет |
| firstOrFail / sole (контрактные исключения) | ✅ | ✅ | ❌ | ❌ | ⚠️ | Collection, illuminate |
| search строгий по умолчанию | ✅ | ⚠️ | ✅ | ❌ | ⚠️ | Collection |
Трансформация и фильтрация
| Функция | Collection | illuminate | doctrine | ramsey | aimeos | 🏆 Победитель |
|---|---|---|---|---|---|---|
| map / mapWithKeys / flatMap / pluck | ✅ | ✅ | ⚠️ | ⚠️ | ✅ | Паритет |
| where-семейство (In / Between / Null / InstanceOf) | ✅ | ✅ | ⚠️ | ⚠️ | ⚠️ | Collection, illuminate |
| unique типобезопасный (1 ≠ '1' ≠ true) | ✅ | ⚠️ | ❌ | ❌ | ⚠️ | Collection |
| Строковый callable в map не получает ключ (нет ловушки intval) | ✅ | ❌ | — | — | ❌ | Collection |
Агрегация и статистика
| Функция | Collection | illuminate | doctrine | ramsey | aimeos | 🏆 Победитель |
|---|---|---|---|---|---|---|
| sum / avg / min / max по полю | ✅ | ✅ | ❌ | ⚠️ | ✅ | Паритет |
| median / mode | ✅ | ✅ | ❌ | ❌ | ⚠️ | Collection, illuminate |
| Числовые строки без потери типа и точности | ✅ | ⚠️ | — | — | ⚠️ | Collection |
Группировка и разбиение
| Функция | Collection | illuminate | doctrine | ramsey | aimeos | 🏆 Победитель |
|---|---|---|---|---|---|---|
| groupBy / keyBy / countBy | ✅ | ✅ | ⚠️ | ❌ | ✅ | Паритет |
| chunk / split / partition (вложенные коллекции) | ✅ | ✅ | ⚠️ | ❌ | ⚠️ | Collection, illuminate |
Объединение множеств
| Функция | Collection | illuminate | doctrine | ramsey | aimeos | 🏆 Победитель |
|---|---|---|---|---|---|---|
| merge / union / combine / concat / zip / crossJoin | ✅ | ✅ | ❌ | ⚠️ | ✅ | Паритет |
| diff / intersect со строгим сравнением | ✅ | ⚠️ | — | ⚠️ | ⚠️ | Collection |
Безопасность
| Функция | Collection | illuminate | doctrine | ramsey | aimeos | 🏆 Победитель |
|---|---|---|---|---|---|---|
| Строгие сравнения по умолчанию (анти type juggling) | ✅ | ❌ | ⚠️ | ⚠️ | ⚠️ | Collection |
| Строка из данных никогда не вызывается как функция | ✅ | ⚠️ | — | — | ⚠️ | Collection |
| toJson бросает исключение вместо тихого false | ✅ | ⚠️ | — | ❌ | ⚠️ | Collection |
| Zero runtime-зависимостей | ✅ | ❌ | ⚠️ | ⚠️ | ✅ | Collection, aimeos |
Производительность
| Функция | Collection | illuminate | doctrine | ramsey | aimeos | 🏆 Победитель |
|---|---|---|---|---|---|---|
| C-fast-path для замыканий без ключа (map / filter / reduce) | ✅ | ❌ | ⚠️ | ❌ | ❌ | Collection |
| array_column-путь для pluck / sum по полю | ✅ | ❌ | — | ✅ | ✅ | Collection, ramsey, aimeos |
| Schwartzian-сортировка sortBy (n вызовов вместо n·log n) | ✅ | ✅ | — | ❌ | ❌ | Collection, illuminate |
Нагрузка и стабильность
| Функция | Collection | illuminate | doctrine | ramsey | aimeos | 🏆 Победитель |
|---|---|---|---|---|---|---|
| Нагрузочные тесты в CI (100k элементов, бюджет памяти) | ✅ | ❌ | ❌ | ❌ | ❌ | Collection |
| Перф-бюджеты всех групп методов в CI | ✅ | ❌ | ❌ | ❌ | ❌ | Collection |
| Мутационное тестирование (MSI 100%) | ✅ | ❌ | ❌ | ❌ | ❌ | Collection |
Расширяемость
| Функция | Collection | illuminate | doctrine | ramsey | aimeos | 🏆 Победитель |
|---|---|---|---|---|---|---|
| Наследование с protected-хелперами (контракт зафиксирован тестами) | ✅ | ⚠️ | ⚠️ | ✅ | ⚠️ | Collection, ramsey |
| ArrayAccess + Countable + IteratorAggregate + JsonSerializable | ✅ | ✅ | ⚠️ | ⚠️ | ✅ | Паритет |
Производительность
Изолированные замеры без Xdebug (composer compare), эквивалентные операции над одинаковыми
данными. Метрика — ops/sec (больше — лучше); «—» — операция не поддерживается.
Сгенерировано из composer compare (PHP 8.1.34). Правки в блоке будут перезаписаны.
| Сценарий | cloudcastle/collection | illuminate/collections | doctrine/collections | ramsey/collection | aimeos/map | loophp/collection | 🏆 Победитель |
|---|---|---|---|---|---|---|---|
| Создание из массива (100 эл.) | 10485760 ops/s | 7741425 ops/s | 10776732 ops/s | 58189 ops/s | 7309697 ops/s | 162882 ops/s | doctrine/collections |
| map (×100) | 217592 ops/s | 149494 ops/s | 229928 ops/s | 26438 ops/s | 147828 ops/s | 51155 ops/s | doctrine/collections |
| filter (×100) | 144179 ops/s | 124079 ops/s | 124960 ops/s | 42030 ops/s | 121987 ops/s | 15715 ops/s | cloudcastle/collection |
| where поле=значение (×100 структур) | 87912 ops/s | 11315 ops/s | — | 31897 ops/s | 42288 ops/s | — | cloudcastle/collection |
| pluck поля из структур (×100) | 1220339 ops/s | 40254 ops/s | — | 41788 ops/s | 889830 ops/s | — | cloudcastle/collection |
| groupBy полю (×100 структур) | 75158 ops/s | 18844 ops/s | — | — | 69605 ops/s | — | cloudcastle/collection |
| sum по полю (×100 структур) | 329316 ops/s | 25386 ops/s | — | — | 223732 ops/s | — | cloudcastle/collection |
| sortBy полю (×100 структур) | 66873 ops/s | 27390 ops/s | — | — | 33674 ops/s | — | cloudcastle/collection |
| unique (×100) | 321772 ops/s | 317399 ops/s | — | — | 297937 ops/s | — | cloudcastle/collection |
| contains строгий (×100) | 4930995 ops/s | 2927348 ops/s | 5092647 ops/s | 59065 ops/s | 2955400 ops/s | 8104 ops/s | doctrine/collections |
| merge двух наборов (100 + 100) | 1583832 ops/s | 1468902 ops/s | — | 17919 ops/s | 1465003 ops/s | — | cloudcastle/collection |
| chunk по 10 (×100) | 474877 ops/s | 431406 ops/s | — | — | 304359 ops/s | — | cloudcastle/collection |
| first по предикату (×100) | 215899 ops/s | 208100 ops/s | 133213 ops/s | — | 201102 ops/s | 15780 ops/s | cloudcastle/collection |
| reduce суммой (×100) | 204616 ops/s | 102043 ops/s | 207366 ops/s | 45738 ops/s | 200650 ops/s | 36209 ops/s | doctrine/collections |
| JSON-цикл (encode + decode, ×100) | 142195 ops/s | 93062 ops/s | — | — | 152560 ops/s | — | aimeos/map |
В микросценариях
создание/map/contains/reducedoctrine опережает на 1–4%: его API опускает декларации возвращаемых типов (нет opcode-проверки на каждом вызове) и не передаёт ключи в callback. Мы сознательно сохраняем полную строгую типизацию и key-aware map. loophp — ленивые коллекции: в сценариях с материализацией это честно отражено вызовомall().
Полное сравнение по каждой возможности — в Wiki: Сравнение.
Заключение: когда что выбирать
Сильные стороны cloudcastle/collection — строгая семантика сравнений как встроенная
защита от type juggling, суперсет API четырёх библиотек, ноль runtime-зависимостей и
production-обвязка (100 % покрытие пятью видами тестов, Infection MSI 100 %, полный
статанализ). Все выборки по полю (where/pluck/groupBy/sum/sortBy) — быстрейшие
среди аналогов за счёт array_column-путей и сортировки по Шварцу.
Производительность: победитель в большинстве сценариев таблицы выше (см. графу
«🏆 Победитель»): where — в ~7× быстрее illuminate, pluck — в ~29×, groupBy — в ~4×,
sum по полю — в ~12×, sortBy — в ~2.4×. Замыкания без параметра ключа уходят в
C-реализации array_map/array_filter/array_reduce (кэш арности на WeakMap).
Честные слабые стороны — там, где аналог выигрывает, причина семантическая, и мы осознанно платим за неё:
map/reduce/containsуdoctrine/collectionsбыстрее на ~3–5 %: он не поддерживает callback с ключом и не проверяет арность;uniqueуilluminateбыстрее на ~15 %: егоarray_uniqueнестрогий и схлопнул бы1,'1'иtrueв одно значение;chunkуaimeos/mapбыстрее в ~2×: его чанки — сырые массивы, наши — полноценные вложенные коллекции;- JSON-цикл у
aimeosбыстрее на ~8 %: без валидации типа верхнего уровня и клэмпа глубины.
Честные рекомендации:
- Строгая семантика, финтех, работа с недоверенными данными —
cloudcastle/collection. - Экосистема Laravel целиком —
illuminate/collections(нативен для Laravel). - Типизированные DDD-коллекции сущностей —
ramsey/collectionилиdoctrine/collections(Criteria-запросы к ORM). - Максимальный перф на сырых массивах без обёрток —
aimeos/mapили нативныеarray_*.
Документация
- Wiki — руководство пользователя
- Быстрый старт
- Возможности (Features)
- Сравнение с аналогами
- Архитектура
- API-документация
Качество
Пакет придерживается production-стандартов: строгая типизация, полный статический анализ (PHPStan max, Psalm level 1, PHPMD, Deptrac, Rector), покрытие тестами 100 % на каждый файл и мутационное тестирование (Infection MSI 100 %). Матрица CI — PHP 8.1–8.5.
composer ci # полный пайплайн качества composer check # быстрые проверки перед коммитом composer test # тесты (unit + integration + security + load) composer compare # сравнение производительности с аналогами
Контакты
- Email — alex-4-17@yandex.ru
- VK — https://vk.com/leha_zorin
- Telegram — @CloudCastle85
- Телефон — +79872763991
- Сетка — https://set.ki/LdYRz76
Поддержать проект
Реквизиты для поддержки автора — в DONATE.md.
Contributors
Спасибо всем, кто вносит вклад в проект:
Список ведётся по спецификации all-contributors.
Лицензия
MIT © CloudCastle