Проблема
GridColumnRules::isValidSqlIdentifier() проверяет только форму идентификатора и пропускает зарезервированные слова MySQL 8 — order, rank, groups, function, system и прочие.
core/components/minishop3/src/Services/Grid/GridColumnRules.php:26-29:
public static function isValidSqlIdentifier(string $name): bool
{
return (bool) preg_match(self::SQL_IDENTIFIER_PATTERN, $name);
}
Паттерн — /^[a-z0-9_]+$/i, то есть order его проходит.
При этом в правилах проекта (CLAUDE.md, раздел «Key Conventions → PHP») использование зарезервированных слов MySQL 8 в названиях колонок прямо запрещено, с рекомендацией альтернатив: sort_order вместо order, user_groups вместо groups, sort_index вместо rank.
Где это применяется
Валидатор используется в семи местах и покрывает не только extra fields:
ExtraFieldsService.php:239 — валидация key при создании пользовательского поля
GridColumnTypeValidator.php:86,93 — foreignKey и displayField в relation-колонках
GridRelationFieldExtractor.php:72-73
RelationColumnSpec.php:86
GridColumnRules.php:36 — внутренний вызов
То есть имя, пришедшее из пользовательского ввода, проходит эту проверку и дальше попадает в генерацию миграций и в построение SQL.
Насколько это опасно сегодня
Не критично — Phinx оборачивает идентификаторы в бэктики при DDL, поэтому ADD COLUMN \order`` сам по себе не упадёт. Но:
- любое место, где имя колонки попадает в сырой SQL без экранирования, сломается синтаксической ошибкой, причём в рантайме и не сразу;
- сортировка и фильтрация по такой колонке в грид-запросах — типичный кандидат на такую поломку;
- пользователь при этом не получит внятной ошибки на этапе создания поля, когда её ещё дёшево исправить.
Правило в CLAUDE.md появилось не на пустом месте — мы уже наступали на это (см. комментарий про rank в src/Processors/Product/GetList.php).
Предлагаемое решение
Добавить в GridColumnRules список зарезервированных слов MySQL 8 и проверять по нему в isValidSqlIdentifier() (либо отдельным методом, если для части вызовов проверка избыточна — тогда явно решить, для каких).
Готового списка в кодовой базе нет — сейчас есть только точечный комментарий про rank. Полный перечень зарезервированных слов есть в документации MySQL 8; достаточно захардкодить его константой.
Отдельно стоит решить, что делать с уже созданными полями: валидация на входе новые случаи закроет, но существующие записи не тронет.
Критерии приёмки
Контекст
Найдено при ревью PR #646 (issue #645). Сам PR корректен — он добавил валидацию формы идентификатора там, где её вообще не было; проверка на зарезервированные слова просто выходила за его рамки.
Проблема
GridColumnRules::isValidSqlIdentifier()проверяет только форму идентификатора и пропускает зарезервированные слова MySQL 8 —order,rank,groups,function,systemи прочие.core/components/minishop3/src/Services/Grid/GridColumnRules.php:26-29:Паттерн —
/^[a-z0-9_]+$/i, то естьorderего проходит.При этом в правилах проекта (
CLAUDE.md, раздел «Key Conventions → PHP») использование зарезервированных слов MySQL 8 в названиях колонок прямо запрещено, с рекомендацией альтернатив:sort_orderвместоorder,user_groupsвместоgroups,sort_indexвместоrank.Где это применяется
Валидатор используется в семи местах и покрывает не только extra fields:
ExtraFieldsService.php:239— валидацияkeyпри создании пользовательского поляGridColumnTypeValidator.php:86,93—foreignKeyиdisplayFieldв relation-колонкахGridRelationFieldExtractor.php:72-73RelationColumnSpec.php:86GridColumnRules.php:36— внутренний вызовТо есть имя, пришедшее из пользовательского ввода, проходит эту проверку и дальше попадает в генерацию миграций и в построение SQL.
Насколько это опасно сегодня
Не критично — Phinx оборачивает идентификаторы в бэктики при DDL, поэтому
ADD COLUMN \order`` сам по себе не упадёт. Но:Правило в CLAUDE.md появилось не на пустом месте — мы уже наступали на это (см. комментарий про
rankвsrc/Processors/Product/GetList.php).Предлагаемое решение
Добавить в
GridColumnRulesсписок зарезервированных слов MySQL 8 и проверять по нему вisValidSqlIdentifier()(либо отдельным методом, если для части вызовов проверка избыточна — тогда явно решить, для каких).Готового списка в кодовой базе нет — сейчас есть только точечный комментарий про
rank. Полный перечень зарезервированных слов есть в документации MySQL 8; достаточно захардкодить его константой.Отдельно стоит решить, что делать с уже созданными полями: валидация на входе новые случаи закроет, но существующие записи не тронет.
Критерии приёмки
isValidSqlIdentifier('order'),('rank'),('groups')возвращаютfalsesort_order,preview_url,category_name) продолжают проходитьКонтекст
Найдено при ревью PR #646 (issue #645). Сам PR корректен — он добавил валидацию формы идентификатора там, где её вообще не было; проверка на зарезервированные слова просто выходила за его рамки.