Запуск внешних процессов в PHP: детальное описание методов

Раздел: -> Системные команды

Основное решение: функция exec()

Функция exec() позволяет выполнить внешнюю команду и получить последнюю строку её вывода. Это наиболее часто используемый метод для простого запуска системных команд и захвата результата.

Как выполнить команду и сохранить вывод в массив?


    <?php
    $output = [];
    $return_var = 0;
    exec('ls -la', $output, $return_var);
    print_r($output);
    ?>
  

Пояснение: первый параметр - команда, второй массив для строк вывода, третий - код возврата. Код 0 означает успех.

Типичные проблемы и их решения

  • Если вывод содержит много строк, exec() возвращает только последнюю. Используйте второй аргумент для получения всего вывода.
  • Команда может не выполняться из-за отключения функции в php.ini (disable_functions). Проверьте настройки сервера.
  • Проблемы с правами: пользователь www-data может не иметь доступа к некоторым командам. Настройте sudoers для определённых команд.

Как получить весь вывод команды в виде строки без разбиения на массив?

Используйте функцию shell_exec(). Она возвращает весь вывод команды в виде одной строки.


    <?php
    $result = shell_exec('whoami');
    echo $result; // например, 'www-data'
    ?>
  

При ошибке выполнения shell_exec() возвращает NULL. Проверяйте результат.

Как вывести вывод команды непосредственно в браузер в реальном времени?

Функция system() выполняет команду и сразу выводит результат. Подходит для потокового вывода.


    <?php
    system('ping -c 4 google.com');
    ?>
  

Вывод system() не может быть захвачен в переменную напрямую. Если нужен результат, используйте буферизацию ob_start().

Как выполнить команду и получить сырой бинарный вывод (например, изображение)?

passthru() аналогична system(), но передаёт необработанные данные. Подходит для запуска программ, выводящих бинарные данные.


    <?php
    header('Content-Type: image/png');
    passthru('cat image.png');
    ?>
  

Не забывайте устанавливать правильные заголовки HTTP перед выводом.

Как выполнить команду с возможностью чтения/записи в её потоки ввода-вывода?

Используйте proc_open() для полного контроля над процессами: stdin, stdout, stderr.


    <?php
    $descriptorspec = [
        0 => ['pipe', 'r'],
        1 => ['pipe', 'w'],
        2 => ['pipe', 'w']
    ];
    $process = proc_open('cat', $descriptorspec, $pipes);
    if (is_resource($process)) {
        fwrite($pipes[0], 'Hello from PHP');
        fclose($pipes[0]);
        echo stream_get_contents($pipes[1]);
        fclose($pipes[1]);
        proc_close($process);
    }
    ?>
  

Не забудьте закрыть все pipes и вызвать proc_close(). Иначе может произойти утечка процессов.

Как открыть однонаправленный канал для чтения или записи?

Функция popen() открывает канал к процессу. Режим 'r' для чтения, 'w' для записи.


    <?php
    $handle = popen('/usr/bin/uptime', 'r');
    while ($line = fgets($handle)) {
        echo $line;
    }
    pclose($handle);
    ?>
  

popen() не даёт доступа к stderr. Для этого используйте proc_open().

Расширенные примеры выполнения команд

Пример

<?php
// 1. Экранирование аргументов для безопасности
$filename = 'file with spaces.txt';
$escaped = escapeshellarg($filename);
$cmd = 'cat ' . $escaped;
echo system($cmd);
?>
Вывод: содержимое файла 'file with spaces.txt'
Пример

<?php
// 2. Выполнение команды с ограничением времени (timeout) через proc_open
$cmd = 'sleep 10; echo done';
$descriptorspec = [0 => ['pipe', 'r'], 1 => ['pipe', 'w'], 2 => ['pipe', 'w']];
$process = proc_open($cmd, $descriptorspec, $pipes);
if (is_resource($process)) {
    stream_set_blocking($pipes[1], 0);
    $timeout = 5;
    $start = time();
    $output = '';
    while (true) {
        $status = proc_get_status($process);
        if (!$status['running']) break;
        if (time() - $start > $timeout) {
            proc_terminate($process, 9);
            $output = 'Timeout';
            break;
        }
        usleep(100000);
        $output .= stream_get_contents($pipes[1]);
    }
    fclose($pipes[0]);
    fclose($pipes[1]);
    fclose($pipes[2]);
    proc_close($process);
    echo $output;
}
?>
Вывод: Timeout (команда прервана через 5 секунд)
Пример

<?php
// 3. Запуск фонового процесса без ожидания (linux)
$cmd = 'nohup php long_script.php > /dev/null 2>&1 &';
exec($cmd);
echo 'Фоновый процесс запущен';
?>
Вывод: Фоновый процесс запущен
Пример

<?php
// 4. Передача переменных окружения через proc_open
$env = ['MYVAR' => 'value123'];
$descriptorspec = [0 => ['pipe', 'r'], 1 => ['pipe', 'w'], 2 => ['pipe', 'w']];
$process = proc_open('echo $MYVAR', $descriptorspec, $pipes, null, $env);
if (is_resource($process)) {
    echo stream_get_contents($pipes[1]);
    fclose($pipes[1]);
    proc_close($process);
}
?>
Вывод: value123
Пример

<?php
// 5. Использование shell_exec с конвейером
$result = shell_exec('ps aux | grep php | grep -v grep | wc -l');
echo 'Количество процессов PHP: ' . (int)$result;
?>
Вывод: Количество процессов PHP: 4
Пример

<?php
// 6. Обработка ошибок stderr через proc_open
$cmd = 'ls /nonexistent';
$descriptorspec = [1 => ['pipe', 'w'], 2 => ['pipe', 'w']];
$process = proc_open($cmd, $descriptorspec, $pipes);
$stdout = stream_get_contents($pipes[1]);
$stderr = stream_get_contents($pipes[2]);
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($process);
echo 'STDOUT: ' . $stdout . PHP_EOL;
echo 'STDERR: ' . $stderr;
?>
STDOUT:
STDERR: ls: cannot access '/nonexistent': No such file or directory

Выполнение команд в PHP - comments

En
Php exe (php)