Вместо кириллических символов в JSON запросе на PHP я получаю искаженную строку в кодировке ASCII?

Проект на 1С-Битрикс (open server). Использую PHP для обработки JSON-запросов, отправленных с фронтенда. Столкнулся с проблемой: вместо кириллических символов, отправленных в JSON, я получаю искажённые строки в массиве answers и в поле comment в формате ASCII.

Фронтенд отправляет запрос с корректными данными (в UTF-8), но на стороне PHP они выглядят как искажённые символы.

Код JS

// объект для отправки на сервер
const formData =
{
  "notification_id": data.id,
  "rating": ratingStar,
  "answers": ["привет"],
  "comment": sanitizeText(notificationFormText),
  "user_response": true,
  "closed_status": true,
  "notification_read": true
}

const response = await fetch(`${BASE_URL_SET_DATA}`, {
   method: 'POST',
   headers: {
     'Content-Type': 'application/json; charset=utf-8',
   },
   body: JSON.stringify(
   {
    action: 'add_notification_feedback', // для обработки запроса в php файле
    data: formData
   }
)
});

Пример данных, отправляемых с фронтенда (из консоли браузера):

{
  "action": "add_notification_feedback",
  "data": {
    "notification_id": 2,
    "rating": "2",
    "answers": ["привет"],
    "comment": "привет",
    "user_response": true,
    "closed_status": true,
    "notification_read": true
  }
}

Лог данных на сервере (сырые данные из php://input):

{
  "action":"add_notification_feedback",
  "data": {
    "notification_id":2,
    "rating":"2",
    "answers":["?@825B"],
    "comment":"?@825B",
    "user_response":true,
    "closed_status":true,
    "notification_read":true
  }
}

Код PHP:

<?php
header("Content-Type: application/json; charset=UTF-8");

// Получение входящих данных
$input = file_get_contents('php://input');
file_put_contents('debug_raw.log', $input); // Сырые данные запроса

// Преобразование в UTF-8
$input = mb_convert_encoding($input, 'UTF-8', 'auto');
file_put_contents('debug_utf8.log', $input); // Данные после приведения к UTF-8

// Декодирование JSON
$response = json_decode($input, true);
file_put_contents('debug_response.log', print_r($response, true)); // Результат декодирования JSON

// Вывод результата для теста
echo "<pre>";
var_dump(mb_detect_encoding($input)); // Ожидаю "UTF-8"
var_dump($response);
echo "</pre>";
?>

Лог вывода на сервере: Сырые данные (из php://input):

{"action":"add_notification_feedback","data":{"notification_id":2,"rating":"2","answers":["?@825B"],"comment":"?@825B","user_response":true,"closed_status":true,"notification_read":true}}

После преобразования в UTF-8:

{"action":"add_notification_feedback","data":{"notification_id":2,"rating":"2","answers":["?@825B"],"comment":"?@825B","user_response":true,"closed_status":true,"notification_read":true}}

После декодирования JSON:

    Array
    (
        [action] => add_notification_feedback
        [data] => Array
            (
                [notification_id] => 2
                [rating] => 2
                [answers] => Array
                    (
                        [0] => ?@825B
                    )
                [comment] => ?@825B
                [user_response] => 1
                [closed_status] => 1
                [notification_read] => 1
            )
    )
  1. На фронтенде данные отправляются в кодировке UTF-8. В консоли браузера данные выглядят корректно.
  2. Заголовок Content-Type на стороне клиента включает charset=utf-8
  3. Сайт работает в кодировке UTF-8 (define('BX_UTF', true))
  4. php.ini default_charset = "UTF-8"
  5. $string = mb_convert_encoding($string, 'HTML-ENTITIES', "UTF-8"); не помог
  6. Каждое слово тоже не получилось перекодировать
// Преобразуем содержимое массива в UTF-8
if (isset($response['data']['answers'])) {
    foreach ($response['data']['answers'] as &$answer) {
        $answer = mb_convert_encoding($answer, 'UTF-8', 'auto');
    }
}

При выводе

var_dump(mb_detect_encoding($input)); // Должно вернуть "UTF-8"

Получаю ASCII

Через PowerShell использовал команду - Format-Hex debug_raw.log. Результат ->

00000000: 7b 22 61 63 74 69 6f 6e 22 3a 22 61 64 64 5f 6e  {"action":"add_n
00000010: 6f 74 69 66 69 63 61 74 69 6f 6e 5f 66 65 65 64  otification_feed
00000020: 62 61 63 6b 22 2c 22 64 61 74 61 22 3a 7b 22 6e  back","data":{"n
00000030: 6f 74 69 66 69 63 61 74 69 6f 6e 5f 69 64 22 3a  otification_id":
00000040: 31 2c 22 72 61 74 69 6e 67 22 3a 22 33 22 2c 22  1,"rating":"3","
00000050: 61 6e 73 77 65 72 73 22 3a 5b 22 2e 40 43 33 3e  answers":[".@C3>
00000060: 35 22 2c 22 2e 30 3b 38 47 38 35 20 42 3e 32 30  5",".0;8G85 B>20
00000070: 40 30 22 5d 2c 22 63 6f 6d 6d 65 6e 74 22 3a 22  @0"],"comment":"
00000080: 30 32 30 22 2c 22 75 73 65 72 5f 72 65 73 70 6f  020","user_respo
00000090: 6e 73 65 22 3a 74 72 75 65 2c 22 63 6c 6f 73 65  nse":true,"close
000000a0: 64 5f 73 74 61 74 75 73 22 3a 74 72 75 65 2c 22  d_status":true,"
000000b0: 6e 6f 74 69 66 69 63 61 74 69 6f 6e 5f 72 65 61  notification_rea
000000c0: 64 22 3a 74 72 75 65 7d 7d                       d":true}}

Как правильно обработать запрос, чтобы сохранить оригинальные символы?


Ответы (1 шт):

Автор решения: Alexey Ten

Похоже ваш RAW всё-таки кем то преобразован. У вас в php.ini нет каких-нибудь настроек mbstring....?

Строка .0;8G85 B>20@0 это испорченное Наличие товара. Портится примерно так:

  • конвертируем из utf-8 в ucs2
  • получаем байты вида 04 1d 04 30 04 3b 04 38 04 47 04 38 04 35 00 20 04 42 04 3e 04 32 04 30 04 40 04 30
  • байты вида 0X удаляются
  • байты вида 1X заменяются на точку (2e)
  • получаем 2e 30 3b 38 47 38 35 20 42 3e 32 30 40 30
  • это и есть .0;8G85 B>20@0.

Остаётся только вопрос в какой момент кто-то это всё проделывает. Либо это PHP, либо какой-то сервер перед ним.

→ Ссылка