W3docs

popen()

Функция popen() в PHP выполняет команду оболочки и открывает канал для потокового чтения или записи данных.

Введение

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

На этой странице рассматривается, что возвращает popen(), режимы r и w, почему необходимо всегда использовать её в паре с pclose(), чем она отличается от exec() и shell_exec(), а также правила безопасности, которые нужно соблюдать перед передачей пользовательских данных в оболочку.

Когда использовать popen()

Используйте popen(), когда вам нужен поток, а не единовременный результат:

  • Режим чтения ("r") — обработка вывода команды постепенно, например, чтение хвоста лога, большого списка find или строк из CLI базы данных без буферизации всего в памяти.
  • Режим записи ("w") — передача данных на стандартный ввод команды, например, направление текста в gzip, mail или пользовательский фильтр.

Если вам просто нужен полный вывод команды в виде строки, проще воспользоваться shell_exec() или exec(). Если нужно одновременно читать и писать в один процесс, используйте proc_open() — каналы popen() однонаправленные.

Синтаксис

popen(string $command, string $mode): resource|false
  • $command — команда оболочки для выполнения, в точности так, как вы вводите её в терминале.
  • $mode — направление канала: "r" для чтения стандартного вывода команды или "w" для записи на её стандартный ввод. На некоторых системах можно добавить "b" (двоичный) или "t" (текстовый), например "rb".

Возвращаемое значение: ресурс-указатель на файл, который передаётся в функции fgets() и fwrite(), либо false, если канал не удалось открыть. Этот указатель — не обычный дескриптор файла: закрывать его нужно с помощью pclose(), а не fclose().

Принцип работы

При вызове popen() PHP создаёт дочерний процесс, который выполняет $command через оболочку и подключает один из его стандартных потоков к каналу:

  • В режиме "r" канал подключён к stdout команды — вы читаете то, что команда выводит.
  • В режиме "w" канал подключён к stdin команды — то, что вы пишете, становится входными данными команды.

Поскольку данные передаются потоком, вы можете начать обработку вывода ещё до завершения команды, что позволяет сохранять плоское потребление памяти даже при огромных объёмах данных.

Примеры

Пример 1: Чтение вывода команды

<?php

// Read mode: stream the output of a directory listing line by line.
$handle = popen('ls -l', 'r');

if ($handle === false) {
    exit("Could not open the pipe.\n");
}

while (!feof($handle)) {
    $line = fgets($handle);
    echo $line;
}

pclose($handle);

feof() проверяет, достигнут ли конец потока, fgets() читает по одной строке за раз, а pclose() закрывает канал и ожидает завершения дочернего процесса. Всегда проверяйте возвращаемое значение: если popen() завершается неудачно, она возвращает false, а чтение из false вызывает ошибки.

На Windows замените ls -l на эквивалентную команду, например dir.

Пример 2: Запись на вход команды

<?php

// Write mode: pipe a line of text into grep's standard input.
$handle = popen('grep "example"', 'w');

if ($handle === false) {
    exit("Could not open the pipe.\n");
}

fwrite($handle, "This line has the word example.\n");
fwrite($handle, "This line does not match.\n");

pclose($handle);

Здесь fwrite() отправляет две строки на стандартный ввод grep. grep фильтрует слово example, поэтому выводится только первая строка. Затем pclose() закрывает канал.

Пример 3: Сжатие данных на лету

<?php

// Stream text straight into gzip and save a compressed file.
$handle = popen('gzip > output.txt.gz', 'w');

if ($handle !== false) {
    fwrite($handle, "Some data to compress.\n");
    pclose($handle);
}

Данные передаются напрямую в gzip без создания промежуточного несжатого файла.

popen() и exec() и shell_exec()

ФункцияВозвращаетПотоковая?Направление
popen()ресурс-каналда (чтение или запись)однонаправленный (stdin или stdout)
exec()последняя строка + массив выводанеттолько вывод
shell_exec()полный вывод в виде строкинеттолько вывод
proc_open()ресурс процессададвунаправленный (stdin и stdout)

Используйте popen() для потоковой передачи; остальные функции — когда нужен готовый результат.

Безопасность: никогда не передавайте необработанные данные пользователя

popen() передаёт аргумент через оболочку, поэтому любые неэкранированные данные пользователя создают риск внедрения команд. Всегда экранируйте аргументы перед построением команды:

<?php

$userInput = $_GET['name'] ?? 'world';

// escapeshellarg() wraps the value in quotes and neutralizes shell metacharacters.
$command = 'echo Hello ' . escapeshellarg($userInput);

$handle = popen($command, 'r');
echo fgets($handle);
pclose($handle);

Используйте escapeshellarg() для отдельных аргументов и escapeshellcmd() для целых команд. Ещё лучше — вообще не передавать пользовательские данные в оболочку, если с задачей справится встроенная функция PHP.

Распространённые ошибки

  • Забыть вызвать pclose(). Незакрытый канал утечёт ресурс, и вы никогда не получите код завершения дочернего процесса. pclose() возвращает код завершения команды.
  • Использовать fclose() вместо pclose(). Ресурс popen() — это канал процесса, а не обычный файл; закрывайте его с помощью pclose().
  • Игнорировать возврат false. Если команда не может запуститься, popen() возвращает false; проверяйте это перед чтением или записью.
  • Смешивать направления. Один канал popen() работает только в режиме чтения или только в режиме записи. Для обоих направлений используйте proc_open().

Заключение

popen() открывает однонаправленный канал к команде оболочки, позволяя потоково читать её вывод ("r") или передавать ей входные данные ("w") без буферизации всего в памяти. Всегда проверяйте возвращаемое значение, закрывайте канал с помощью pclose() и экранируйте пользовательские данные с помощью escapeshellarg() перед передачей в оболочку. Чтобы узнать о вспомогательных функциях чтения и записи, используемых с каналом, см. fgets() и fwrite().

Практика

Практика
Каково назначение функции 'popen' в PHP?
Каково назначение функции 'popen' в PHP?
Was this page helpful?