Почему PHP получает "кривой" JSON из 1С и как это исправить?

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

Интеграция между платформой 1С:Предприятие и веб-серверами, работающими на PHP — одна из самых частых задач для разработчиков. И зачастую при передаче данных в формате JSON возникает классическая проблема: 1С успешно формирует и отправляет JSON, однако на стороне PHP разработчик видит «кривой» или пустой массив. Разберем по шагам, почему возникает эта ошибка, проанализируем исходный код и найдем правильные пути решения.

Проанализируем исходную ситуацию

Посмотрим на типичный пример, с которым сталкиваются разработчики. Со стороны 1С формируется структура, сериализуется в JSON и отправляется POST-запросом на сервер:


тДанные = Новый Структура;
тДанные.Вставить("onec_id_order", "ЦВ000001381");
тДанные.Вставить("id_order", "111");
тДанные.Вставить("phone", "79999999999");
тДанные.Вставить("post_track", Массив);

ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON, тДанные);
СтрокаПараметров = ЗаписьJSON.Закрыть();

ЗаголовокHTTP = Новый Соответствие();
ЗаголовокHTTP.Вставить("Content-type", "application/x-www-form-urlencoded");

HTTPЗапрос = Новый HTTPЗапрос("/api/connector/", ЗаголовокHTTP);
HTTPЗапрос.УстановитьТелоИзСтроки(СтрокаПараметров);

HTTPСоединение = Новый HTTPСоединение("example.com", , , , , , Новый ЗащищенноеСоединениеOpenSSL);
Ответ = HTTPСоединение.ОтправитьДляОбработки(HTTPЗапрос);

На стороне PHP скрипт пытается получить переданные данные классическим способом через суперглобальный массив $_POST и записывает их в файл:


file_put_contents(__DIR__ . '/post.txt', print_r($_POST, true) . PHP_EOL, FILE_APPEND);

В результате в лог-файле мы видим очень странную картину:


Array
(
    [{
"onec_id_order":_"ЦВ000001381",
"id_order":_"111",
"phone":_"_79999999999",
"post_track":_] => Array
        (
            [
"80111457622359",
"80111457621468"
] => 
        )
 )

Вместо нормального ассоциативного массива весь наш JSON-текст разбился на куски и превратился в ключи массива. Выясним причину такого поведения и рассмотрим детально каждую ошибку.

Причина 1: Некорректный заголовок Content-Type

Если мы посмотрим на код 1С, то увидим использование заголовка application/x-www-form-urlencoded. Этот заголовок сообщает веб-серверу и интерпретатору PHP, что данные в теле запроса передаются в формате стандартной веб-формы, то есть в виде пар ключ=значение&ключ2=значение2.

PHP, увидев такой заголовок, послушно пытается разобрать тело запроса по правилам URL-кодирования. Но 1С отправляет чистую строку JSON, которая начинается с символа открывающей фигурной скобки и не содержит знаков равенства (в нужном контексте). Из-за этого PHP берет огромный кусок строки JSON до ближайшего пробела, амперсанда или другого спецсимвола, и делает его ключом массива $_POST. Именно поэтому мы видим "разорванный" JSON в ключах массива.

Причина 2: Использование $_POST вместо сырого потока

Когда вы передаете данные с правильным заголовком для JSON (например, application/json), суперглобальный массив $_POST в PHP будет абсолютно пустым. Это нормальное поведение языка PHP: он автоматически заполняет $_POST только для типов application/x-www-form-urlencoded и multipart/form-data.

Причина 3: Скрытый символ BOM в строке 1С

Если внимательно присмотреться к дампу массива в начале статьи, перед первой открывающей скобкой JSON стоит невидимый спецсимвол: [{. Это маркер последовательности байтов или BOM (Byte Order Mark).

Платформа 1С:Предприятие часто добавляет символ BOM по умолчанию при формировании строк или текстовых файлов в кодировке UTF-8. Наличие этого символа является фатальным для стандартной PHP функции json_decode(). Она воспримет строку как синтаксически неверный JSON и вернет ошибку.

Решение 1: Правильная передача сырого JSON (Рекомендуется)

Рассмотрим подробнее, как необходимо переписать код на обеих сторонах, чтобы наладить корректный обмен данными.

Шаг 1. Исправляем код в 1С:

  1. Изменим заголовок Content-Type на правильный.
  2. Явно запретим платформе 1С добавлять символ BOM при установке тела запроса.

// 1. Указываем правильный тип контента
ЗаголовокHTTP = Новый Соответствие();
ЗаголовокHTTP.Вставить("Content-type", "application/json");

HTTPЗапрос = Новый HTTPЗапрос("/api/connector/", ЗаголовокHTTP);

// 2. Устанавливаем тело без символа BOM
HTTPЗапрос.УстановитьТелоИзСтроки(СтрокаПараметров, КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);

Шаг 2. Исправляем код на стороне PHP:

Поскольку $_POST будет пуст, нам необходимо получить "сырое" тело запроса, обратившись к потоку ввода php://input.


// 1. Читаем сырые данные из тела запроса
$json_string = file_get_contents('php://input');

// 2. Декодируем JSON-строку в ассоциативный массив (второй параметр true)
$data = json_decode($json_string, true);

// 3. Теперь массив $data содержит корректную структуру
file_put_contents(__DIR__ . '/post.txt', print_r($data, true) . PHP_EOL, FILE_APPEND);

После применения этих правок вы получите на стороне PHP идеально структурированный ассоциативный массив, с которым легко работать.

Решение 2: Передача JSON как параметра формы (Альтернатива)

Иногда возникают ситуации, когда API на стороне PHP строго требует заголовок application/x-www-form-urlencoded и разработчик PHP не может или не хочет переписывать механизм приема данных. Разберем по шагам, как действовать разработчику 1С в таком случае.

Вам необходимо отправить JSON не как «голое» тело запроса, а как значение определенного параметра, например data. При этом саму строку нужно обязательно URL-кодировать.

Код на стороне 1С:


ЗаголовокHTTP = Новый Соответствие();
ЗаголовокHTTP.Вставить("Content-type", "application/x-www-form-urlencoded");

HTTPЗапрос = Новый HTTPЗапрос("/api/connector/", ЗаголовокHTTP);

// Формируем строку в формате ключ=кодированное_значение
ТелоЗапроса = "data=" + КодироватьСтроку(СтрокаПараметров, СпособКодированияСтроки.КодировкаURL);

// BOM здесь не так страшен, но лучше все равно его отключать
HTTPЗапрос.УстановитьТелоИзСтроки(ТелоЗапроса, КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);

Код на стороне PHP:

В этом случае данные корректно попадут в массив $_POST, и интерпретатор PHP сам выполнит URL-декодирование.


// Проверяем, существует ли наш ключ
if (isset($_POST['data'])) {
    // Декодируем строку JSON
    $data = json_decode($_POST['data'], true);
    
    // Записываем результат
    file_put_contents(__DIR__ . '/post.txt', print_r($data, true) . PHP_EOL, FILE_APPEND);
}

Подведем итоги

Мы проанализировали частую проблему, возникающую при интеграции систем. Чтобы избежать «кривого» JSON на приемной стороне, запомните три главных правила:

  1. Всегда следите за соответствием отправляемых данных и заголовка Content-Type. Для JSON это application/json.
  2. В 1С при передаче строк через HTTP-запросы всегда используйте метод ИспользованиеByteOrderMark.НеИспользовать, чтобы скрыть маркер BOM.
  3. На стороне PHP при работе с application/json читайте данные исключительно через поток php://input.

Соблюдение этих простых правил сэкономит вам массу времени и убережет от долгих поисков ошибок интеграции!

← На главную