PHP json_decode не работает с одним непарным суррогатом, вызванным правильно сформированным JSON.stringify узла 12 - PullRequest
2 голосов
/ 02 ноября 2019

Узел 12 теперь использует Правильно сформированный JSON.stringify , который выводит escape-последовательности для одиноких суррогатов. PHP не может json_decode это, когда есть одинокий суррогат, который экранирован.

Возьмите следующий пример кода в узле.

var a = '???????? ??? ???????????';
JSON.stringify(a.slice(0, 15));

// Node 10 output: 
'"???????�"';

// Node 12 output:
'"???????\\ud835"'

Этот ответ затем отправляется на сервер PHP в формате JSON и декодируется. Вот где происходит ошибка. Вывод Node10 раньше работал нормально с PHP json_decode, но он больше не работает с выводом Node12.

Я упростил пример NODE-> PHP, см. Ниже.

<?php
$string = '{"string": "???????\\ud835"}';
var_dump(json_decode($string, false, 512, JSON_THROW_ON_ERROR | JSON_INVALID_UTF8_IGNORE | JSON_INVALID_UTF8_SUBSTITUTE));

// Output:
Fatal error: Uncaught JsonException: Single unpaired UTF-16 surrogate in unicode escape in phptest.php:36
Stack trace:
#0 phptest.php(36): json_decode('{"string": "\xF0\x9D\x98...', false, 512, 7340032)
#1 {main}
  thrown in phptest.php on line 36

Я ожидаю следующие опции JSON_INVALID_UTF8_IGNORE или JSON_INVALID_UTF8_SUBSTITUTE для работы с json_decode в PHP 7.3+, но это совсем не помогает. JSON_THROW_ON_ERROR на самом деле выдает ошибку, чтобы упростить отладку.

1 Ответ

0 голосов
/ 05 ноября 2019

Проблема в Javascript.

Ваша строка содержит многобайтовые символы. С помощью a.slice (0, 15) вы получите 15 байт, а не 15 символов, и создадите недопустимую строку utf-8. Многобайтовые символы могут быть не искажены.

Обновление:

Я думаю, что параметры JSON_INVALID_UTF8_IGNORE и JSON_INVALID_UTF8_SUBSTITUTE предназначены для json_encode (), а не для очистки недопустимого JSON для json_decode (). Чистым решением является предоставление действительного JSON на странице Javascript.

Грязное решение - попытка исправить недопустимый JSON с помощью PHP:

$json = preg_replace_callback(
  '~\\\u[a-d0-9]{4}~iu',
  function($found){
    if(json_decode('"'.$found[0].'"')){
      return $found[0];
    }
    return "";  //or "?"
  },
  $invalidJson
);
...