Skip to content

Feature Safety

github-actions[bot] edited this page Jul 16, 2026 · 1 revision

🛡️ Строгая семантика, JSON и поток

← Возможности · Главная · Сравнение

Свойства, отличающие пакет от аналогов на уровне дизайна: строгие сравнения, «строка — данные, не код», fail-safe JSON; плюс мутаторы и higher-order методы.

Строгая семантика (===)

Все операции сравнения — contains/search/where*/diff/intersect/unique — используют строгое сравнение. Классические PHP-ловушки исключены по построению:

use CloudCastle\Collection\Collection;

// 0 == 'admin' → true при loose; здесь — false
Collection::make([0, '1', null])->contains('admin'); // false

// '0e123' == '0e456' → true при loose (оба «нулевые» float) — вектор обхода сравнения хэшей
Collection::make(['0e123'])->search('0e456');        // false

// int 1 и string '1' — разные идентификаторы (критично для проверок прав)
$users->where('id', 1);   // только int-совпадения
$users->whereIn('id', ['1']); // не найдёт int 1

Строка из данных — данные, не код

Имя функции, пришедшее в данных ('system', 'phpinfo'), никогда не выполняется:

Collection::make(['system', 'exec'])->contains('system'); // true — поиск значения
$rows->groupBy('trim');  // извлечение поля 'trim', не вызов trim()

Строковый callable, переданный как callback в map/filter/reduce, получает только значение — без ключа, который ломал бы встроенные функции:

Collection::make(['1', '2'])->map('intval');   // [1, 2] — ключ не попал в базу счисления
Collection::make([1, 'a'])->filter('is_int');  // [1] — без ArgumentCountError
Collection::make([1, 9, 3])->reduce('max', 0); // 9 — ключ не участвует в max()

JSON fail-safe

$c->toJson();                       // InvalidJsonException вместо тихого false
Collection::fromJson('{broken');    // InvalidJsonException (с previous от json-декодера)
Collection::fromJson('42');         // InvalidJsonException: скаляр на верхнем уровне
Collection::fromJson($json, 64);    // лимит глубины; враждебные значения клэмпятся

Мутаторы и higher-order

Мутаторы изменяют текущую коллекцию (семантика illuminate): push/put/pull/forget/ prepend/pop/shift/splice. Higher-order управляют цепочкой:

$c->push(4, 5)->put('k', 6);
$removed = $c->splice(1, 2, [99]); // вырезанное — новой коллекцией

$c->each(fn ($v) => $v === 3 ? false : process($v)); // false прерывает обход
$c->tap(fn ($col) => logger()->debug('size', [$col->count()]))
  ->when($withDiscount, fn ($col) => $col->map($applyDiscount))
  ->whenEmpty(fn ($col) => $col->push($default));
$total = $c->pipe(fn ($col) => $col->sum());

Плюсы

  • Каждая ловушка type juggling закрыта на уровне библиотеки, а не код-ревью.
  • Мутанты и границы поведения зафиксированы: 100 % покрытие, Infection MSI 100 %.
  • Наследование поддержано контрактом protected-хелперов (см. тесты ExtensibilityTest).

Минусы

  • Нестрогих вариантов нет намеренно: код, рассчитывающий на '1' == 1, придётся привести к явным типам (это считается достоинством).

✅ Рекомендуется

  • Пропускать недоверенный JSON только через fromJson с разумным лимитом глубины.
  • Проверки прав и идентификаторов — только строгими методами (они здесь все строгие).

❌ Антипаттерны

  • Приводить типы «на глаз» перед contains/whereIn — приводите явно и осознанно.
  • Ловить InvalidJsonException и продолжать с пустой коллекцией без логирования — fail-safe не означает «молча проглотить».

Clone this wiki locally