Popen: примеры (PHP)

Функция popen в PHP: практическое применение
Раздел: Процессы
popen(string $command, string $mode): resource|false

Основные сведения о popen

Функция popen() открывает односторонний канал для выполнения внешней команды. Она позволяет запустить процесс и взаимодействовать с ним через файловый указатель, используя стандартный ввод (stdin) или вывод (stdout).

Когда используется функция

Функция применяется для асинхронного выполнения внешних программ, обработки больших объемов данных потоком без загрузки в память, логирования или непрерывного взаимодействия с сервисами.

Аргументы функции

command (string): Строка команды для выполнения в оболочке.
mode (string): Режим открытия канала. Допустимые значения: 'r' (чтение из stdout процесса), 'w' (запись в stdin процесса).

Функция возвращает файловый указатель или false в случае ошибки.

Простые примеры использования

Чтение вывода команды
<?php
$handle = popen('ls -la', 'r');
if ($handle) {
    while (!feof($handle)) {
        echo fgets($handle, 4096);
    }
    pclose($handle);
}
?>
total 12
drwxr-xr-x  2 user user 4096 Jan  1 10:00 .
drwxr-xr-x 10 user user 4096 Jan  1 09:55 ..
-rw-r--r--  1 user user    0 Jan  1 10:00 index.php
Запись в процесс
<?php
$handle = popen('grep "error"', 'w');
fwrite($handle, "error: file not found\nwarning: deprecated\n");
pclose($handle);
?>
// В консоли будет выведено: error: file not found

Альтернативы в PHP

Предоставляет расширенный контроль над процессом, включая stdin, stdout, stderr и управление дескрипторами. Предпочтительнее для сложных взаимодействий.

exec(), system(), passthru()

Функции для простого выполнения команд с захватом или прямым выводом результата. Используются для синхронных операций без двустороннего обмена данными.

shell_exec()

Возвращает весь вывод команды как строку. Подходит для коротких команд с небольшим выводом.

Аналоги в других языках

Python (subprocess.Popen)
import subprocess
proc = subprocess.Popen(['ls', '-la'], stdout=subprocess.PIPE)
output, error = proc.communicate()
print(output.decode())
JavaScript (Node.js, child_process.spawn)
const { spawn } = require('child_process');
const ls = spawn('ls', ['-la']);
ls.stdout.on('data', (data) => {
  console.log(data.toString());
});
Bash (pipes)
ls -la | grep "example"

Типичные ошибки

Игнорирование проверки результата
<?php
$handle = popen('несуществующая_команда', 'r');
// $handle будет false, но без проверки вызов fgets() вызовет ошибку
?>
Неправильный режим доступа
<?php
// Попытка чтения из канала, открытого для записи
$handle = popen('ls', 'w');
$data = fread($handle, 1024); // Не будет данных
?>
Забытое закрытие канала

Невызов pclose() может оставить процессы зависшими.

Изменения в новых версиях PHP

В PHP 8.0 улучшена обработка ошибок: при сбое вызова функции теперь выбрасывается исключение типа ValueError для некорректных параметров. В более ранних версиях возвращалось false без деталей.

<?php
// PHP 8+
try {
    $handle = popen([], 'r'); // Неверный аргумент
} catch (ValueError $e) {
    echo $e->getMessage();
}
?>

Расширенные сценарии

Длительное взаимодействие с процессом
Пример php
<?php
$handle = popen('python3 -u interactive_script.py', 'w');
// -u в Python отключает буферизацию вывода
fwrite($handle, "input_data\n");
fflush($handle); // Принудительная отправка
sleep(1);
$output = fread($handle, 4096);
pclose($handle);
?>
Параллельная обработка логов
Пример php
<?php
$logFile = 'app.log';
$handle = popen("tail -f $logFile | grep 'CRITICAL'", 'r');
while (!feof($handle)) {
    $line = fgets($handle);
    if ($line) {
        // Отправка уведомления или обработка
        echo "Критическая ошибка: $line";
    }
}
?>
Запись в несколько процессов
Пример php
<?php
// Запись одного набора данных в две команды параллельно
$sourceData = "sample data\n";
$handles = [
    popen('gzip > output.gz', 'w'),
    popen('tee raw_output.txt', 'w')
];
foreach ($handles as $handle) {
    fwrite($handle, $sourceData);
    pclose($handle);
}
?>

PHP popen function comments

En
Popen Opens process file pointer