Функция "sqlsrv_fetch_array" возвращает символьные строки как UTF-8, а не как Latin1.
Строковые данные, хранящиеся в СЕРВЕРЕ MS SQL, представлены на латинице 1. Когда инструменты базы данных MS VS, AquaDataStudio и т. Д. Отображают данные VARCHAR, они отображаются как Latin1, который является типом данных в базе данных. Существующий код PHP 5 (который использует FreeTDS) также правильно отображает данные. Это важно, потому что некоторые поля содержат двоичные данные, хранящиеся в схеме кодирования Base128, в которой используются однобайтовые символы печати в диапазоне от 128 до 255, с функцией PHP ORD, используемой для декодирования в строки VARCHAR.
Теперь нам нужно перейти к PHP 7, и драйверы Microsoft (функции, начинающиеся с «sqlsrv», например «sqlsrv_connect», «sqlsrv_query», «sqlsrv_fetch_array» и т. Д.) Используются, и они НЕ возвращают строки (VARCHAR) как Latin 1, но как многобайтовый UTF-8, и, конечно, это имеет два значения:
1) возвращаемые строки символов намного длиннее исходных строк, поэтому проверки не пройдены, и / или строки обрезаются, и
2) функция PHP ORD возвращает правильное значение только для символов в десятичном диапазоне 0-127, поэтому код декодирования / дешифрования завершается неудачей.
Я создал таблицу в локальной базе данных на PHP 5, которая содержит записи для каждого символа в диапазоне 1-255. Когда я читаю записи в PHP 5 и отображаю символы и их значение «ORD ()», я вижу правильные результаты. Версия кода PHP 7 отображает символы в диапазоне 1-127 правильно, но 128-255 показывают в виде вопросительных знаков, однобайтовые символы с неправильным значением ORD () или две или три байтовые строки, где "ORD" Возвращено неверное значение.
Похоже, проблема в том, что функция "sqlsrv_fetch_array" принудительно переносит любые строковые данные в UTF-8 и возвращает только UTF-8.
Переход на UTF-8 не возможен, поскольку данные не наши, и у нас есть только доступ для чтения к базам данных.
Кроме того, приложение содержит более полумиллиона строк кода PHP 5, поэтому попытка выполнить и использовать iconv () для каждого поля, возвращаемого в каждом запросе, нецелесообразна.
Как мы можем получить данные Latin1, возвращаемые в PHP 7, из базы данных, где данные фактически хранятся в Latin1?
В PHP 7 с использованием функций Microsoft "sqlsrv" ничего из того, что я пробовал, пока не работает. Вот некоторые из вещей, которые я пробовал:
SELECT COLLATIONPROPERTY('SQL_Latin1_General_CP1_CI_AS','CodePage')
… не имеет никакого эффекта, возможно, потому что это база данных по умолчанию.
SELECT * FROM z_php5_charset ORDER BY id
COLLATE Latin1_General_CP1_CI_AS
… возвращает сообщение об ошибке «Тип выражения int недопустим для предложения COLLATE».
меняется на:
COLLATE SQL_Latin1_General_CP1_CI_AS
… вернул ту же ошибку.
Думая, что это может быть как-то связано с настройками «mbstring» в файле php.ini, мы установили модуль, попробовали по крайней мере дюжину настроек, включающих вариации «English» и «ISO-8859-1» из всего установить «Английский» для всего, установленного на «ISO-8859-1», перезапуская Apache между каждым тестом комбинации, и хотя я видел несколько меньше вопросительных знаков для некоторых комбинаций, вывод все еще был UTF-8.
mb_internal_encoding('ISO-8859-1');
… тоже не дало эффекта.
Похоже, проблема в том, что функция "sqlsrv_fetch_array" принудительно переносит любые строковые данные в UTF-8 и возвращает только UTF-8.
Minimum code (PHP 7 version)
…
mb_internal_encoding('ISO-8859-1');
$db_con = @sqlsrv_connect($server, $connectionInfo);
$sql = "SELECT COLLATIONPROPERTY('SQL_Latin1_General_CP1_CI_AS','CodePage'); ".
// 'COLLATE Latin1_General_CP1_CI_AS '.
"\n";
$sql = 'SELECT * FROM z_php5_charset ORDER BY id '.
// 'COLLATE Latin1_General_CP1_CI_AS '.
"\n";
$result_id = sqlsrv_query($db_con, $sql, $parms, $options);
while ($row = sqlsrv_fetch_array($result_id, SQLSRV_FETCH_ASSOC)) {
$c = $row['id'];
$char = $row['charset'];
$outbuf = $c.': '.$char.' '.ord($char);
if (strlen($char) > 1) {
$outbuf .= ' --- more than one char returned ['.strlen($char).' chars returned]';
}
echo $outbuf."\n";
} // end while
Please note:
Column 1 is the ORDinal value of the character written to the VARCHAR field in the database by a PHP program.
Column 2 is the character returned when retrieved from the database.
Column 3 is the value returned by the PHP ORD() function.
column 4 (if any) is additional information about the character retrieved.
Sample of PHP 5 output:
160: † 160
161: ° 161
162: ¢ 162
163: £ 163
164: § 164
165: • 165
166: ¶ 166
167: ß 167
168: ® 168
169: © 169
170: ™ 170
171: ´ 171
172: ¨ 172
173: ≠ 173
174: Æ 174
175: Ø 175
176: ∞ 176
177: ± 177
178: ≤ 178
179: ≥ 179
Sample of PHP 7 output:
160: Â 194 --- more than one char returned [2 chars returned]
161: ¡ 194 --- more than one char returned [2 chars returned]
162: ¢ 194 --- more than one char returned [2 chars returned]
163: £ 194 --- more than one char returned [2 chars returned]
164: ¤ 194 --- more than one char returned [2 chars returned]
165: ¥ 194 --- more than one char returned [2 chars returned]
166: ¦ 194 --- more than one char returned [2 chars returned]
167: § 194 --- more than one char returned [2 chars returned]
168: ¨ 194 --- more than one char returned [2 chars returned]
169: © 194 --- more than one char returned [2 chars returned]
170: ª 194 --- more than one char returned [2 chars returned]
171: « 194 --- more than one char returned [2 chars returned]
172: ¬ 194 --- more than one char returned [2 chars returned]
173: Â 194 --- more than one char returned [2 chars returned]
174: ® 194 --- more than one char returned [2 chars returned]
175: ¯ 194 --- more than one char returned [2 chars returned]
176: ° 194 --- more than one char returned [2 chars returned]
177: ± 194 --- more than one char returned [2 chars returned]
178: ² 194 --- more than one char returned [2 chars returned]
179: ³ 194 --- more than one char returned [2 chars returned]