Почему exec() молча отбрасывает байты?
После долгих попыток я обнаружил поведение, которое не могу объяснить / решить, поэтому попросил помощи здесь. На нашем сервере (Ubuntu 16.04.5 LTS с PHP 7.0.30) мы используем некоторые инструменты за пределами httpdocs, которые вызываются с помощью exec()
чтобы получить их результаты. В данном случае это QRCode-генератор.
Однако некоторые QR-коды отображаться не будут. Мы получили вывод из инструмента (вывод данных в формате PNG), но когда мы показываем его как изображение, он по какой-то причине кажется поврежденным.
После многих отладок я обнаружил, что результат иногда отличается на 1 или 2 байта от выходных данных инструмента.
Я выполнил свою последнюю отладку, используя приведенный ниже QR-код (12345), который представляет собой файл размером 240 байт. Однако при его окончательном выводе длина оказывается равной 239 байтам, так что мы где-то потеряли байт?
Я узнал, используя exec()
Создаем массив вывода. Конечные пробелы, такие как \n, не включены в этот массив, поэтому implode(), чтобы приклеить массив обратно к строке, как показано ниже:
<?php
$cmd = 'cat qr.png';
$output = array();
$exit_code = 0;
exec($cmd, $output, $exit_code);
if($exit_code === 0)
{
header ("Content-type: image/png;");
print implode(PHP_EOL, $output);
}
die();
Но почему-то теряется байт в этом процессе? Я уже пробовал некоторые другие решения, используя shell_exec()
(но здесь нет return_var, поэтому мы не можем проверить валидацию процесса...)
<?php
$command = 'cat qr.png';
$output = shell_exec($command);
if($output !== null)
{
header ("Content-type: image/png;");
print $output;
}
die();
... и используя passthru()
(но это выводит содержимое напрямую, что нежелательно. Буферизация вывода, как в примере ниже, невозможна в реальном коде...)
<?php
$command = 'cat qr.png';
$return_var = 0;
ob_start();
passthru($command, $return_var);
if($return_var === 0)
{
header ("Content-type: image/png;");
$output = ob_get_clean();
print $output;
}
die();
Пока что exec()
-функция всегда работала довольно хорошо для нас, что приводило к выводу $ return_var AND $, но теперь я теряю байты. Я уже пробовал некоторые варианты PHP_EOL, мы теперь используем в качестве клея (\n, \ r и \ n \ r), но с первыми двумя окончаниями строки, я все еще получил только 239 байтов и с последним, Я получаю 241 байт, так что 1 байт - много.
Почему я теряю байт здесь? Как правильно преобразовать массив $ output обратно в строку? Есть ли способ вернуть 240-байтовый вывод обратно, взорвав массив? Или есть другие функции, которые я еще не нашел, чтобы выполнить команду, которая выдаст мне выходные данные, а также return_var?
1 ответ
Согласно ручной записи для exec
, функция возвращает последнюю строку вывода. Включена ли последняя строка в качестве последнего элемента $output
неясно.
Но что более важно, в руководстве говорится:
Конечные пробелы, такие как \n, не включены в этот массив.
Я бы предположил, что это для каждого элемента массива (т.е. для каждой строки вашего вывода), но в любом случае ваш пробел здесь будет значительным, потому что вы не имеете дело с текстом - все байты здесь важны и значимы, Помните, что пробел не только означает \n
, Это также может означать \t
или же (пробел), например.
Если строка заканчивается F\t\n
(заглавная буква F или любой другой непробельный символ, табуляция, затем новая строка), оба пробельных символа будут удалены с конца. Когда вы делаете implode
операция, вы можете положить \n
назад, но вы никогда не узнаете о \t
это было раздето.
Главное, что нужно осознать, это то, что exec
ожидает иметь дело с простым текстом, а не необработанными двоичными данными.
Я бы предложил, чтобы вместо использования cat qr.png
, ты используешь base64 qr.png
кодировать двоичные данные в строку ASCII, а затем декодировать их в PHP с base64_decode
, Если вы не собираетесь использовать cat
на самом деле вы все еще можете направить вывод команды QR-генерации через base64
как это: generate-qr-png |base64
, В этой ситуации не должно быть значительных пробелов, поэтому ничто не может быть exec
,