Почему exec() молча отбрасывает байты?

После долгих попыток я обнаружил поведение, которое не могу объяснить / решить, поэтому попросил помощи здесь. На нашем сервере (Ubuntu 16.04.5 LTS с PHP 7.0.30) мы используем некоторые инструменты за пределами httpdocs, которые вызываются с помощью exec() чтобы получить их результаты. В данном случае это QRCode-генератор.

Однако некоторые QR-коды отображаться не будут. Мы получили вывод из инструмента (вывод данных в формате PNG), но когда мы показываем его как изображение, он по какой-то причине кажется поврежденным.

После многих отладок я обнаружил, что результат иногда отличается на 1 или 2 байта от выходных данных инструмента.

Я выполнил свою последнюю отладку, используя приведенный ниже QR-код (12345), который представляет собой файл размером 240 байт. Однако при его окончательном выводе длина оказывается равной 239 байтам, так что мы где-то потеряли байт?

Пример QR-кода

Я узнал, используя 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,

Другие вопросы по тегам