Как решить проблему «Не удалось получить версию XML» при написании своего PHP-обработчика для обмена с 1С?

Программист 1С v8.3 (Управляемые формы) 1С:Управление торговлей Управленческий учет IT и автоматизация бизнеса
← На главную

Разработка собственного PHP-модуля для интеграции сайта с системой 1С по стандартному протоколу CommerceML (используемому в модулях обмена с «1С-Битрикс») — это отличный способ настроить гибкий обмен данными без покупки дорогостоящих CMS. Однако при реализации протокола «с нуля» разработчики часто сталкиваются со скрытыми особенностями и недокументированным поведением стандартных модулей со стороны 1С.

В этой статье мы подробно разберем одну из самых коварных ошибок инициализации обмена, которая звучит как «Не удалось получить версию XML. Загрузка с сайта невозможна». Выясним причину ее возникновения на стороне 1С, проанализируем код модуля обмена и напишем корректный PHP-обработчик, который позволит успешно пройти все этапы авторизации и инициализации.

Как устроен процесс инициализации обмена (контур Sale/Catalog)

Прежде чем перейти к исправлению ошибки, давайте восстановим общую картину взаимодействия между 1С и сайтом. Обмен начинается со стороны 1С, которая отправляет последовательные HTTP-запросы на определенный URL (скрипт-приемник на вашем сайте, например, /1c_exchange.php).

Рассмотрим стандартный сценарий для контура заказов (type=sale) или каталога (type=catalog):

  1. Авторизация (mode=checkauth): 1С отправляет запрос для проверки учетных данных. Сайт проверяет логин/пароль и должен вернуть три строки ответа: слово success, имя сессионной куки и значение этой куки.
  2. Инициализация (mode=init): 1С отправляет запрос для согласования параметров обмена. На этом шаге сервер сообщает, поддерживает ли он ZIP-архивацию, какие установлены лимиты на размер файлов, а также возвращает идентификатор сессии и поддерживаемую версию протокола XML.
  3. Выгрузка/Получение данных: в зависимости от типа обмена (импорт товаров на сайт или выгрузка заказов с сайта в 1С) выполняются последующие шаги.

Проблема, которую мы рассматриваем, возникает именно на втором шаге — при попытке обработать ответ сервера на запрос mode=init.

В чем причина ошибки «Не удалось получить версию XML»?

Проанализируем ситуацию глазами 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С:

  1. Функция СтрПолучитьСтроку(ОтветСервераОИнициализации, 4) извлекает ровно четвертую строку из ответа вашего сайта.
  2. Далее выполняется функция Прав(ВерсияXML, 4), которая берет ровно 4 последних символа из этой четвертой строки.
  3. И, наконец, полученная подстрока передается в функцию Число() для приведения к числовому типу.

А теперь посмотрим, что происходит, когда ваш 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.

Решение проблемы с версией XML

Для того чтобы 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С, лучшим способом их локализации является отладка кода в Конфигураторе 1С. Выясним, где искать нужные механизмы:

  1. Откройте 1С в режиме Конфигуратор.
  2. В дереве метаданных найдите ветку Общие модули.
  3. Если вы используете типовые конфигурации (УТ 11, КА, ERP, УНФ), найдите модуль ОбменССайтомСервер или ОбменССайтомВызовСервера. Если у вас установлен внешний дополнительный модуль от Битрикс, найдите модули, начинающиеся с префикса Б_ (например, Б_ОбменССайтомСервер).
  4. Для поиска места, где происходит обработка ответов вашего сайта, воспользуйтесь глобальным поиском по конфигурации (сочетание клавиш Ctrl + Shift + F) с поисковым запросом Режим=init или Режим=checkauth.
  5. Установите точку останова (клавиша F9) в начале процедуры обработки ответа и запустите сеанс 1С в режиме отладки. Теперь при запуске синхронизации выполнение кода остановится на вашей точке, и вы сможете в режиме реального времени посмотреть содержимое переменной ОтветСервераОИнициализации и понять, что именно не нравится парсеру 1С.

Используя предложенные рекомендации и внимательно контролируя структуру и длину текстовых ответов вашего PHP-обработчика, вы сможете построить стабильный и быстрый мост интеграции между любой самописной CMS и системой 1С.

← На главную