Я много работаю с проектами на веб-стеке и параллельно активно использую нейросети.
Со временем стало ясно: чтобы ИИ помогал не «вообще по PHP», а по конкретному проекту, ему нужен нормальный контекст. Не один файл, не случайный фрагмент, а полноценный срез кода.
В какой-то момент меня это достало настолько, что я сел и сделал отдельный инструмент — scan2json.
Это маленький PHP-репозиторий, который:
-
в одну сторону: сканирует проект и превращает его в JSON/JSONL;
-
в другую: по этому JSONL умеет собрать обратно структуру папок и файлов.
Репозиторий лежит на GitHub: https://github.com/simai/scan2json. В статье расскажу, зачем я его делал, как он устроен и как им можно пользоваться у себя.
Зачем вообще городить JSON вокруг кода
Если вы хоть раз пытались обсуждать реальный проект с ИИ, сценарий примерно такой.
-
Открываем ChatGPT или другой LLM.
-
Копируем в чат один файл.
-
Получаем ответ.
-
Выясняется, что нужен ещё конфиг, ещё шаблон, ещё один класс.
-
Начинается бесконечный копипаст и объяснения «где что лежит».
Проблема в этом месте не в модели.
Проблема в том, что мы даём ей мозаику из обрывков, а не картину проекта.
Я хотел другого:
-
один раз собрать целостный снимок проекта;
-
чтобы в этом снимке были:
-
пути до файлов,
-
структура папок,
-
сам код;
-
-
и дальше уже разговаривать с ИИ, опираясь на этот снимок, а не таская куски руками.
Плагины в IDE помогают, но у них есть ограничения:
-
они живут внутри конкретного редактора;
-
не всегда удобно делиться этим контекстом с кем-то ещё (менеджером, аналитиком, подрядчиком);
-
не всегда просто привязать это к ассистентам GPT, которые работают через веб-интерфейс и файлы.
Мне хотелось иметь отдельный артефакт — файл или набор файлов, который:
-
можно положить в репозиторий,
-
отправить ассистенту,
-
отдать другому разработчику,
-
скормить своему RAG-сервису.
Так родилась идея scan2json.
Что такое scan2json
Вся логика собрана в два файла:
-
scan.php— сканирует проект в JSON/JSONL; -
restore.php— по JSONL разворачивает проект обратно в отдельную папку.
Оба скрипта работают через браузер и живут там же, где живёт ваш проект (под DOCUMENT_ROOT).
Bitrix обязателен не всегда: если он есть, можно использовать проверку на админа; если нет — используется обычный пароль.
Идеология простая:
-
scan.php— я запускаю, чтобы «сфотографировать» проект:-
выбираю, какую папку сканировать;
-
при необходимости выкидываю лишнее;
-
получаю JSONL/JSON + архивы;
-
-
restore.php— я запускаю, когда:-
хочу из этого JSONL собрать отдельную «лёгкую» копию проекта;
-
или передать кому-то набор JSONL + restore и дать ему возможность развернуть структуру у себя.
-
Дальше этим JSON можно кормить ассистентов, использовать в своих инструментах или сохранять как снапшот релиза.
Основные части инструмента
Я пойду от пользовательского сценария: что видит человек на экране и какие сущности за этим стоят.
Выбор корневой папки
Первое, что нужно сделать в scan.php, — выбрать, откуда сканировать.
Я сделал два варианта.
1. Ввести путь руками
Простой режим: у меня есть поле scan_folder.
Я пишу туда, например:
-
/— весь сайт целиком; -
/local— кастомный код в Bitrix; -
/bitrix/modules/fileman; -
/project/app— если это не Bitrix, а условный PHP-проект.
Этот способ удобен, когда я уже знаю, где лежит нужный модуль.
2. Выбрать из списка
Если путь не помню или лень вспоминать, включаю режим «Select from list».
Там появляется мини-навигация:
-
строка «Current folder: …»;
-
кнопка «Go up one level»;
-
выпадающий список подпапок текущей директории.
Я просто кликаю по подпапкам и «проваливаюсь» туда, пока не дойду до нужного уровня.
Текущее положение хранится в сессии, так что можно туда-сюда ходить и подбирать правильный корень.
В обоих случаях в итоге получаю одну конкретную папку, которая будет корнем скана.
Тонкая настройка: кнопка Choose items…
Следующая фишка — возможность точно сказать, что включать, а что нет.
По умолчанию сканер идёт по всем вложенным папкам. Плюс в коде есть глобальные исключения (vendor, upload, какие-то служебные директории, бинарные файлы и т.п.).
Но бывают ситуации:
-
нужно сканировать весь модуль, но без конкретной тяжёлой папки;
-
нужно исключить пару больших файлов;
-
хочется аккуратно «обрезать» верхний уровень.
Для этого я добавил кнопку Choose items….
Как это работает:
-
Я выбираю корневую папку (любой из двух способов).
-
Нажимаю
Choose items…. -
Открывается панель со списком всех подпапок и файлов первого уровня внутри корня.
-
Напротив каждого пункта — чекбокс.
-
По умолчанию всё включено.
-
Я снимаю галочки с того, что не хочу отдавать в JSON:
-
отдельные папки,
-
отдельные файлы.
-
-
Нажимаю
Apply selection.
Дальше сканер уже учитывает этот выбор:
-
на первом уровне он обходит только выбранные элементы,
-
внутрь выбранной папки заходит целиком и дальше работает как раньше,
-
поверх этого работают глобальные исключения (
excludeDirs,excludeFilesи т.д.).
Если я никогда не нажимаю Choose items…, скрипт ведёт себя как в самой первой версии — сканирует всё дерево, кроме общих исключений.
Куда складывается результат
Всё, что связано со сканом, кладётся в папку scan_tmp под DOCUMENT_ROOT:
-
туда пишет временный список файлов,
-
туда же складывает JSONL, JSON, части и ZIP-архивы.
На странице видно, есть ли готовые данные, и есть отдельная кнопка Delete data — она удаляет содержимое scan_tmp и даёт начать с чистого листа.
Форматы вывода: JSONL, JSON и части
На выходе меня интересовало сразу несколько сценариев:
-
работать с JSONL в своих пайплайнах;
-
загружать полный JSON/части в GPT-ассистентов (там нужен один валидный JSON);
-
не думать о лимитах ассистентов по размеру файла.
JSONL
Это основной формат для «машинной» обработки.
Каждая строка — отдельный файл:
{"file":"local/components/app/orders/class.php","content":"..."}
Плюсы:
-
можно читать построчно, не держа всё в памяти;
-
удобно обрабатывать большими порциями;
-
хорошо ложится на любые стриминговые сценарии и индексы.
Я использую JSONL, если хочу:
-
написать свой скрипт/агента, который анализирует проект;
-
делать векторизацию кода и складывать всё это в базу;
-
комбинировать это с RAG-подходами.
JSON (цельный)
Многим внешним инструментам нужен один JSON-документ.
Например, GPT-ассистентам при загрузке файлов.
Поэтому я делаю ещё один вариант — массив:
[
{"file":"...","content":"..."},
{"file":"...","content":"..."}
]
Этот формат удобно:
-
грузить прямо в ассистента как файл;
-
хранить снепшоты релиза;
-
использовать в тех местах, где JSONL не поддерживается.
JSON по частям
Для больших проектов один JSON может оказаться слишком объёмным.
У ассистентов есть лимиты по размеру файла и по токенам.
Поэтому я добавил автоматическую нарезку:
-
при достижении примерно
TOKEN_MAXслов текущий JSON закрывается; -
создаётся следующая часть;
-
итогом получается несколько файлов:
-
project_1.json,project_2.json,project_3.jsonи т.п.
-
Важный момент: режется по строкам/записям, а не по байтам.
То есть каждый объект {file, content} всегда целиком внутри одного файла.
Плюс сразу собирается ZIP со всеми частями — так удобно его скачать и загрузить в ассистента по одному файлу.
ZIP-архивы и подсчёт «токенов»
Дополнительно скрипт делает:
-
ZIP с JSONL;
-
ZIP с JSON;
-
ZIP с JSON-частями.
И выводит примерное количество «токенов» (по факту — слов) в данных.
Это не точный счётчик конкретной модели, но хороший ориентир по масштабу проекта.
Обратная сторона: restore.php и восстановление из JSONL
После того как я какое-то время пользовался только сканером, стало не хватать обратной операции.
Возникли сценарии:
-
«хочу у себя локально поднять минимальную копию проекта из снапшота»;
-
«хочу отдать подрядчику JSONL + скрипт, чтобы он сам развернул структуру у себя»;
-
«нужно быстро собрать песочницу только из тех файлов, которые попали в скан».
Так появился второй скрипт — restore.php.
Что он делает
В двух словах:
-
берёт JSONL, который сделал
scan.php; -
для каждой строки с
{file, content}:-
нормализует путь;
-
создаёт нужную папку;
-
записывает файл в выбранную директорию.
-
Важно:
-
restore.phpне пытается быть полноценным бэкапом; -
он честно восстанавливает только то, что было в JSONL:
-
если вы исключили
vendor, в восстановленный проект он не попадёт; -
если вы выкинули часть модулей через
Choose items…, их тоже не будет.
-
То есть это именно восстановление фильтрованного среза, а не «верни мне прод ровно как был».
Как им пользоваться
Процесс простой:
-
Кладу
restore.phpтуда же, гдеscan.php. -
Открываю в браузере.
-
Прохожу авторизацию:
-
либо через Bitrix-админа (если так настроено),
-
либо по паролю.
-
-
Выбираю исходный JSONL:
-
либо из списка файлов в
scan_tmp, -
либо указываю путь вручную.
-
-
Задаю целевую папку:
-
это директория под
DOCUMENT_ROOT, напримерrestored_project; -
если её нет — скрипт создаст.
-
-
Включаю или выключаю опции:
-
Dry run — просто посчитать, что было бы создано/перезаписано, без реальных записей;
-
Overwrite existing files — разрешить перезапись уже существующих файлов в целевой папке.
-
-
Запускаю.
Скрипт идёт по JSONL построчно, аккуратно проверяет:
-
что путь относительный;
-
что нет попыток выйти через
..; -
что конечный путь остаётся внутри целевой директории.
По итогу выдаёт статистику:
-
сколько строк обработано;
-
сколько валидных записей;
-
сколько файлов создано;
-
сколько перезаписано;
-
сколько пропущено;
-
сколько записей были некорректны.
Для чего это удобно
На практике restore.php оказался полезен в таких ситуациях:
-
Лёгкая копия проекта для ИИ-экспериментов
Не хочется тянуть продовый проект со всеми артефактами.
Я делаю скан с аккуратными исключениями и разворачиваю этот срез отдельно.
Там можно спокойно переписывать код, давать доступ ассистентам, ставить инструменты. -
Передача проекта внешнему разработчику
Вместо живого доступа к репо/серверу я могу:
-
сделать JSONL только из нужных частей,
-
отправить подрядчику JSONL +
restore.php, -
он у себя разворачивает структуру и работает.
-
-
Минимальный репро-проект
Если баг живёт в каком-то модуле, можно:
-
отсканировать только этот модуль,
-
развернуть его в отдельную папку,
-
добавить нужные зависимости,
-
и воспроизводить проблему уже в уменьшенном окружении.
-
Как это всё используется на практике (мой рабочий цикл)
Пример того, как я живу с этим инструментом день-в-день.
Сценарий 1. Обсудить с ИИ доработку в существующем проекте
-
Выбираю проект и добавляю
scan.php(если его там ещё нет). -
Настраиваю:
-
исключения
vendor,uploadи то, что точно не нужно; -
ограничиваю корень до нужного модуля;
-
при необходимости через
Choose items…выкидываю тяжёлые или неинтересные папки.
-
-
Запускаю скан. Получаю JSONL.
-
Пишу небольшой скрипт/агента, который:
-
читает JSONL,
-
ищет нужные файлы по маскам,
-
собирает контекст и формирует запрос к модели.
-
-
Дальше мы с ИИ обсуждаем уже не абстрактный PHP, а конкретный проект:
модель реально видит файлы, о которых я говорю.
Сценарий 2. Ассистент «по проекту» в GPT
-
Беру JSON-части (если проект большой) или один JSON-файл.
-
Создаю GPT-ассистента.
-
Загружаю эти файлы как «знания» ассистента.
-
В инструкциях пишу, что это конкретный проект, и объясняю, как с ним работать.
-
Разработчик приходит к ассистенту и спрашивает:
-
«Где у нас реализована регистрация?»
-
«Какие классы отвечают за оплату?»
-
«Помоги добавить ещё один статус заказа и не забыть про все места».
-
Ассистент отвечает уже на основе реального кода, а не по памяти модели.
Сценарий 3. Аудит legacy или чужого проекта
-
Кидаю
scan.phpна сервер (лучше в стейджинг/копию). -
Сканирую нужную часть проекта.
-
Получаю JSONL, вытаскиваю его к себе.
-
Пользуюсь либо:
-
ассистентом, либо
-
своим скриптом, который бегает по JSONL.
-
Можно попросить ИИ:
-
описать архитектуру по директориям;
-
найти «подозрительные» место в коде;
-
составить список технического долга по модулям.
Сценарий 4. Восстановить срез как отдельный проект
-
Беру уже готовый JSONL (например, старый снапшот релиза).
-
Через
restore.phpразворачиваю это вrestored_project/. -
Получаю отдельную структуру:
-
с теми же путями;
-
но без того, что я выкидывал при скане.
-
Дальше можно:
-
подключить туда свои инструменты;
-
дать доступ ассистентам;
-
использовать как «песочницу» для экспериментов.
Как попробовать у себя
Минимальный путь такой.
-
Клонировать репозиторий
git clone https://github.com/simai/scan2json.git -
Положить
scan.phpиrestore.phpв проектНапример, в корень сайта под
DOCUMENT_ROOT.
Я обычно не коммичу их в основной репозиторий проекта, а держу только на сервере/копии. -
Настроить доступ
В начале файлов есть константы:
-
ACCESS_BITRIX— использовать ли проверку через Bitrix-админа; -
ACCESS_PASSWORD— пароль для простого режима.
Я обязательно меняю пароль с дефолтного и не выкладываю скрипты наружу без защиты.
-
-
Открыть
scan.phpв браузере-
пройти авторизацию;
-
выбрать папку;
-
при необходимости нажать
Choose items…; -
запустить скан.
-
-
Скачать JSONL/JSON/части
Посмотреть, где появился
scan_tmp, и забрать оттуда файлы. -
При желании — протестировать
restore.phpНа отдельной тестовой директории:
-
указать JSONL;
-
задать новую папку;
-
сначала сделать
Dry run, потом настоящий запуск.
-
Не обязательно серьёзно интегрировать инструмент: достаточно одной тестовой сессии, чтобы почувствовать, «заходит» тебе такой подход или нет.
Финал
Для меня scan2json — живой эксперимент, который родился из реальной боли:
я устал объяснять ИИ проекты по кусочкам.
Сейчас это уже не просто скрипт «на коленке», а небольшой набор инструментов:
-
scan.php— чтобы снять структурированный снимок проекта в JSON/JSONL; -
restore.php— чтобы по этому снимку собрать отдельную копию.
Я сам активно пользуюсь им в своей работе и периодически его дорабатываю.
Если тебе эта идея откликается — можно:
-
взять репозиторий,
-
попробовать на своём проекте,
-
и, если будут мысли, завести issue или PR.
Буду рад любым комментариям, предложениям по улучшению и даже просто фидбеку в духе «попробовал — вот где больно, а вот где зашло».
Автор: zabarov
