
# Кэшированный поиск по AST

Кэшированный поиск по AST дополняет режим с прогретым кэшем [индекса фактов](https://greph.inline0.dev/ru/docs/indexed/ast). Вместо хранения извлеченных фактов и повторного анализа найденных файлов кэш сохраняет сериализованные деревья разбора на диске. Последующие запросы десериализуют деревья и запускают средство сопоставления напрямую, полностью пропуская конвейер PHP-Parser.

Кэш доступен через подкоманду `greph-index ast-cache` CLI и методы фасада `Greph::buildAstCache()` / `Greph::searchAstCached()`. Также поддерживается поиск по нескольким индексам и наборам на основе манифеста.

## Создание кэша

```bash
# Full build at the current directory
./vendor/bin/greph-index ast-cache build .

# Use a non-default cache directory
./vendor/bin/greph-index ast-cache build . --index-dir /tmp/greph-ast-cache

# Build a cache that may refresh automatically for small changes
./vendor/bin/greph-index ast-cache build . \
  --lifecycle opportunistic-refresh \
  --auto-refresh-max-files 32 \
  --auto-refresh-max-bytes 1048576
```

По умолчанию кэш хранится в `<root>/.greph-ast-cache/`. Создание кэша обычно занимает больше всего времени среди трех режимов индексирования, поскольку каждый файл нужно разобрать один раз.

После успешного создания выводится сводка в одну строку:

```text
Built AST cache for 2547 files in .greph-ast-cache (2541 cached trees, +2541 ~0 -0 =0)
```

Количество кэшированных деревьев обычно немного меньше числа файлов: файлы, которые не удалось разобрать (и которые пропускаются без предупреждения), не попадают в кэш. Передайте `--strict-parse` в команде поиска, если нужно выводить ошибки разбора.

## Обновление кэша

```bash
./vendor/bin/greph-index ast-cache refresh .
```

`refresh` повторно обходит корневой каталог индекса, заново анализирует измененные файлы и перезаписывает только изменившиеся кэшированные деревья.

Профили жизненного цикла работают так же, как в других прогретых режимах:

- `static`: никогда автоматически не проверяет актуальность и не изменяет данные
- `manual-refresh`: сообщает об устаревшем состоянии в `stats`, но не обновляет кэш во время поиска
- `opportunistic-refresh`: обновляет кэш во время поиска, только если набор изменений не превышает заданные пороги
- `strict-stale-check`: отклоняет поиск с устаревшими данными вместо обновления

## Запросы к кэшу

```bash
./vendor/bin/greph-index ast-cache search 'new $CLASS()' src
./vendor/bin/greph-index ast-cache search '$obj->$method($$$ARGS)' src
./vendor/bin/greph-index ast-cache search --json 'array($$$ITEMS)' src
./vendor/bin/greph-index ast-cache search --fallback scan 'new $CLASS()' src
./vendor/bin/greph-index ast-cache search --trace-plan 'array($$$ITEMS)' src
```

Набор параметров такой же, как в [`ast-index search`](https://greph.inline0.dev/ru/docs/indexed/ast#querying-the-index), включая `--fallback scan` для отсутствующего кэша.

### Поиск по кэшу нескольких индексов

Повторите `--index-dir`, чтобы выполнить запрос сразу к нескольким прогретым кэшам:

```bash
./vendor/bin/greph-index ast-cache search \
  --index-dir ../wordpress/.greph-ast-cache \
  --index-dir ./.greph-ast-cache \
  --show-index-origin \
  'array($$$ITEMS)' .
```

### Наборы кэшей на основе манифеста

Если вы уже используете именованные наборы прогретых индексов, тот же манифест можно применить для поиска по кэшированному AST:

```bash
./vendor/bin/greph-index set build --mode ast-cache
./vendor/bin/greph-index set stats --mode ast-cache --dry-refresh
./vendor/bin/greph-index set search --mode ast-cache --show-index-origin 'array($$$ITEMS)' .
```

### Диагностика планировщика

`--trace-plan` выводит решения планировщика кэшированного AST в `stderr`, включая:

- какие прогретые кэши были проверены
- запускалась ли логика обновления устаревших данных
- количество файлов-кандидатов после сужения на уровне файлов
- сколько кэшированных деревьев было десериализовано для точного структурного сопоставления

## Кэш и индекс фактов

Оба режима ускоряют поиск по AST, но оптимизированы для разных типов задач.

| Аспект | Индекс фактов (`ast-index`) | Кэш (`ast-cache`) |
|---|---|---|
| Что хранится | Извлеченные факты (имена вызовов, имена классов, объявления) | Сериализованные разобранные деревья |
| Стоимость построения | Ниже | Выше (разбирает всё) |
| Размер при построении | Меньше | Больше |
| Путь запроса | Фильтрация по фактам, затем разбор и сопоставление кандидатов | Десериализация дерева, затем сопоставление |
| Лучше всего подходит для | Шаблонов с распознаваемой начальной частью (имя вызова, имя класса) | Шаблонов, отличительный признак которых структурный, а не именной |
| Хуже всего подходит для | Шаблонов, начальная часть которых — метапеременная | Холодных запросов к дереву, которое больше не помещается в кэш страниц ОС |

На практике индекс фактов обеспечивает самый быстрый холодный запрос, а кэш — самый быстрый прогретый запрос. Выберите вариант, подходящий для вашей нагрузки, или создайте оба и позвольте своим инструментам выбрать.

## Программное использование

```php
use Greph\Greph;
use Greph\Ast\AstSearchOptions;
use Greph\Index\IndexLifecycleProfile;

Greph::buildAstCache('.', lifecycle: IndexLifecycleProfile::Static);

$matches = Greph::searchAstCached(
    'array($$$ITEMS)',
    'src',
    new AstSearchOptions(jobs: 4, tracePlan: true),
);
```

`searchAstCached` принимает те же `AstSearchOptions`, что и нативный поиск по AST, и возвращает результаты в той же форме `list<AstMatch>`. Отличие только в том, что для файлов, уже находящихся в кэше, конвейер парсера обходится. Для потоков с несколькими индексами или на основе манифеста используйте `searchAstCachedMany(...)` и `searchAstCachedSet(...)`.

## Когда использовать

Режим кэшированного AST подходит, когда:

- Вы многократно выполняете структурные запросы к одной и той же кодовой базе.
- Узким местом в предыдущих запусках было PHP-Parser (в профилях видно время, затраченное лексером и парсером).
- Вы можете выделить место на диске для хранилища сериализованных деревьев.

Если ваши запросы обычно охватывают небольшую часть кодовой базы, а совпадения сосредоточены вокруг распознаваемых имен вызовов или классов, [индекс фактов](https://greph.inline0.dev/ru/docs/indexed/ast) обычно подходит лучше. Если запросы охватывают большую часть кодовой базы, но вы готовы один раз заплатить за разбор, лучше использовать кэш.
