Я много работаю с проектами на веб-стеке и параллельно активно использую нейросети.
Со временем стало ясно: чтобы ИИ помогал не «вообще по 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.
Буду рад любым комментариям, предложениям по улучшению и даже просто фидбеку в духе «попробовал — вот где больно, а вот где зашло».
