Разработка собственного PHP-модуля для интеграции сайта с системой 1С по стандартному протоколу CommerceML (используемому в модулях обмена с «1С-Битрикс») — это отличный способ настроить гибкий обмен данными без покупки дорогостоящих CMS. Однако при реализации протокола «с нуля» разработчики часто сталкиваются со скрытыми особенностями и недокументированным поведением стандартных модулей со стороны 1С.
В этой статье мы подробно разберем одну из самых коварных ошибок инициализации обмена, которая звучит как «Не удалось получить версию XML. Загрузка с сайта невозможна». Выясним причину ее возникновения на стороне 1С, проанализируем код модуля обмена и напишем корректный PHP-обработчик, который позволит успешно пройти все этапы авторизации и инициализации.
Прежде чем перейти к исправлению ошибки, давайте восстановим общую картину взаимодействия между 1С и сайтом. Обмен начинается со стороны 1С, которая отправляет последовательные HTTP-запросы на определенный URL (скрипт-приемник на вашем сайте, например, /1c_exchange.php).
Рассмотрим стандартный сценарий для контура заказов (type=sale) или каталога (type=catalog):
success, имя сессионной куки и значение этой куки.Проблема, которую мы рассматриваем, возникает именно на втором шаге — при попытке обработать ответ сервера на запрос mode=init.
Проанализируем ситуацию глазами PHP-разработчика. Допустим, мы написали метод инициализации обмена, который формирует ответ согласно официальной документации:
public function initSale() {
return 'zip=no' . PHP_EOL .
'file_limit=1000000' . PHP_EOL .
'sessid=' . session_id() . PHP_EOL .
'version=3.1';
}
Вроде бы всё сделано строго по спецификации: возвращаются четыре требуемые строки. Однако при запуске обмена из 1С мы стабильно получаем ошибку: «Не удалось получить версию XML. Загрузка с сайта невозможна».
Давайте заглянем «под капот» платформы 1С. Если мы откроем конфигуратор типового модуля обмена (например, общего модуля Б_ОбменССайтомСервер), то найдем там следующий участок кода, отвечающий за чтение ответа сервера при инициализации:
Попытка
ВерсияXML = СтрПолучитьСтроку(ОтветСервераОИнициализации, 4);
ВерсияЧислом = Число(Прав(ВерсияXML, 4));
Исключение
Б_ОбменССайтомСервер.СообщитьПодробно("Не удалось получить версию XML. Загрузка с сайта невозможна", ПараметрыОбмена);
Б_ОбменССайтомСервер.СообщитьПодробно("Ответ сервера: " + ОтветСервераОИнициализации, ПараметрыОбмена);
Возврат;
КонецПопытки;
Давайте детально разберем этот фрагмент кода 1С:
СтрПолучитьСтроку(ОтветСервераОИнициализации, 4) извлекает ровно четвертую строку из ответа вашего сайта.Прав(ВерсияXML, 4), которая берет ровно 4 последних символа из этой четвертой строки.Число() для приведения к числовому типу.А теперь посмотрим, что происходит, когда ваш PHP-скрипт возвращает строку version=3.1. Четвертая строка вашего ответа — это version=3.1. Функция Прав("version=3.1", 4) возвращает строку n=3.1. При попытке преобразовать n=3.1 в число функция Число() закономерно генерирует системное исключение. 1С переходит в блок Исключение, выводит ошибку в лог и аварийно завершает сеанс обмена.
Если же мы вернем строку version=3.01, то 1С вырежет последние 4 символа, получив строку 3.01, которая успешно и без ошибок преобразуется в число 3.01.
Для того чтобы 1С успешно распознала версию протокола CommerceML, значение версии в вашем PHP-обработчике должно иметь строго 4-значное представление в конце строки. Например, вместо 3.1 следует передавать 3.01 или 2.09.
Также важно обратить внимание на спецсимволы переноса строк. В PHP константа PHP_EOL выводит разделитель строк текущей операционной системы сервера (для Linux это \n). Но 1С под управлением Windows иногда жестко требует формат Windows-переноса \r\n. Если разделители будут прочитаны некорректно, 1С воспримет весь ваш ответ как одну длинную строку, четвертая строка окажется пустой, и парсинг завершится той же ошибкой.
Ниже приведено эталонное решение на PHP для методов авторизации и инициализации:
class OneCExchangeHandler {
// Шаг 1: Авторизация
public function checkauthSale() {
// Стартуем сессию перед выводом
if (session_status() == PHP_SESSION_NONE) {
session_start();
}
// Возвращаем три строки через явный \r\n для надежности
$response = "success\r\n";
$response .= session_name() . "\r\n";
$response .= session_id();
return $response;
}
// Шаг 2: Инициализация с корректной версией XML
public function initSale() {
if (session_status() == PHP_SESSION_NONE) {
session_start();
}
// Обратите внимание: версия должна заканчиваться ровно 4 символами числа (например, 3.01)
$response = "zip=no\r\n";
$response .= "file_limit=1000000\r\n";
$response .= "sessid=" . session_id() . "\r\n";
$response .= "version=3.01"; // Последние 4 символа: '3.01' — успешно преобразуются в число
return $response;
}
}
После успешной инициализации обмена для типа type=catalog 1С передаст на сайт XML-файлы с каталогом товаров (обычно это import.xml и offers.xml). Одним из первых шагов при их импорте является построение дерева категорий (групп товаров).
Рассмотрим структуру категорий в XML-файле формата CommerceML 2. Категории описываются вложенными тегами <Группы> и <Группа>:
<Группы>
<Группа>
<Ид>a1b2c3d4-e5f6-7a8b-9c0d-e1f2a3b4c5d6</Ид>
<Наименование>Бытовая техника</Наименование>
<Группы>
<Группа>
<Ид>f5e4d3c2-b1a0-9f8e-7d6c-5b4a3f2e1d0c</Ид>
<Наименование>Холодильники</Наименование>
</Группа>
</Группы>
</Группа>
</Группы>
Для корректного импорта структуры категорий в базу данных вашего сайта (например, в MySQL) необходимо реализовать рекурсивный обход XML-дерева. Важно сохранять уникальный идентификатор (UUID) группы из 1С, так как именно по нему товары в последующем будут привязываться к своим категориям.
Ниже представлен пример рекурсивной функции на PHP для разбора категорий с использованием стандартного расширения SimpleXML:
function importCategories(SimpleXMLElement $xmlGroups, $parentId = null) {
foreach ($xmlGroups->Группа as $group) {
$uuid = (string)$group->Ид;
$name = (string)$group->Наименование;
// Здесь должна быть логика сохранения категории в вашу БД
// Пример вымышленного SQL-запроса:
// $db->query("INSERT INTO categories (uuid, parent_uuid, name) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE name = ?", [$uuid, $parentId, $name, $name]);
// Отладочный вывод структуры в лог
echo "Импорт категории: {$name} (UUID: {$uuid}, Родитель UUID: {$parentId})\n";
// Если у текущей группы есть вложенные группы, вызываем функцию рекурсивно
if (isset($group->Группы)) {
importCategories($group->Группы, $uuid);
}
}
}
// Пример вызова парсера для загруженного файла import.xml
$xml = simplexml_load_file('import.xml');
if (isset($xml->Классификатор->Группы)) {
importCategories($xml->Классификатор->Группы);
}
Если в процессе разработки у вас возникают другие ошибки на стороне 1С, лучшим способом их локализации является отладка кода в Конфигураторе 1С. Выясним, где искать нужные механизмы:
ОбменССайтомСервер или ОбменССайтомВызовСервера. Если у вас установлен внешний дополнительный модуль от Битрикс, найдите модули, начинающиеся с префикса Б_ (например, Б_ОбменССайтомСервер).Ctrl + Shift + F) с поисковым запросом Режим=init или Режим=checkauth.F9) в начале процедуры обработки ответа и запустите сеанс 1С в режиме отладки. Теперь при запуске синхронизации выполнение кода остановится на вашей точке, и вы сможете в режиме реального времени посмотреть содержимое переменной ОтветСервераОИнициализации и понять, что именно не нравится парсеру 1С.Используя предложенные рекомендации и внимательно контролируя структуру и длину текстовых ответов вашего PHP-обработчика, вы сможете построить стабильный и быстрый мост интеграции между любой самописной CMS и системой 1С.