popen()
The popen() function is a popular feature of the PHP programming language that allows you to execute shell commands and interact with them from within your PHP
Introduction
The popen() function runs a shell command in a separate child process and opens a pipe to it — a one-way stream you can either read the command's output from or write input into. It's PHP's tool for streaming data to or from an external program line by line, instead of waiting for the whole command to finish.
This page covers what popen() returns, its r and w modes, why you must always pair it with pclose(), how it differs from exec() and shell_exec(), and the security rules you must follow before passing any user input to a shell.
When to Use popen()
Reach for popen() when you need a stream, not a one-shot result:
- Read mode (
"r") — process a command's output incrementally, e.g. tailing a log, reading a largefindlisting, or piping rows out of a database CLI without buffering everything in memory. - Write mode (
"w") — feed data into a command's standard input, e.g. piping text intogzip,mail, or a custom filter.
If you just want the full output of a command as a string, shell_exec() or exec() is simpler. If you need to read and write the same process at once, use proc_open() — popen() pipes are one-directional.
Syntax
popen(string $command, string $mode): resource|false$command— the shell command to run, exactly as you'd type it in a terminal.$mode— the pipe direction:"r"to read the command's standard output, or"w"to write to its standard input. On some systems you may add"b"(binary) or"t"(text), e.g."rb".
Return value: a file-pointer resource you pass to functions like fgets() and fwrite(), or false if the pipe could not be opened. The pointer is not a normal file handle — you must close it with pclose(), never fclose().
How It Works
When you call popen(), PHP forks a child process that runs $command through the shell and connects one of its standard streams to a pipe:
- In
"r"mode, the pipe is wired to the command's stdout — you read what the command prints. - In
"w"mode, the pipe is wired to the command's stdin — what you write becomes the command's input.
Because it streams, you can start handling output before the command has finished, which keeps memory flat even for huge outputs.
Examples
Example 1: Reading a Command's Output
<?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() checks whether the end of the stream has been reached, fgets() reads one line at a time, and pclose() closes the pipe and waits for the child process to exit. Always check the return value: if popen() fails it returns false, and reading from false triggers errors.
On Windows, replace
ls -lwith the equivalent command, e.g.dir.
Example 2: Writing to a Command's Input
<?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);Here fwrite() sends two lines to grep's standard input. grep filters for the word example, so only the first line is printed. pclose() then closes the pipe.
Example 3: Compressing Data on the Fly
<?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);
}This pipes data directly into gzip without writing an intermediate uncompressed file.
popen() vs. exec() and shell_exec()
| Function | Returns | Streaming? | Direction |
|---|---|---|---|
popen() | a pipe resource | yes (read or write) | one-way (stdin or stdout) |
exec() | last line + array of output | no | output only |
shell_exec() | full output as a string | no | output only |
proc_open() | process resource | yes | two-way (stdin and stdout) |
Use popen() when you want to stream; use the others when you just need the finished result.
Security: Never Pass Raw User Input
popen() runs its argument through the shell, so any unescaped user input is a command-injection risk. Always escape arguments before building a command:
<?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);Use escapeshellarg() for individual arguments and escapeshellcmd() for whole commands. Better still, avoid passing user data to the shell at all when a native PHP function can do the job.
Common Pitfalls
- Forgetting
pclose(). Leaving the pipe open leaks the resource and you never get the child's exit status.pclose()returns the command's exit code. - Using
fclose()instead ofpclose(). Apopen()resource is a process pipe, not a plain file — close it withpclose(). - Ignoring a
falsereturn. If the command can't start,popen()returnsfalse; check for it before reading or writing. - Mixing directions. A single
popen()pipe is read-only or write-only. For both, useproc_open().
Conclusion
popen() opens a one-way pipe to a shell command so you can stream its output ("r") or feed it input ("w") without buffering everything in memory. Always check the return value, close the pipe with pclose(), and escape any user input with escapeshellarg() before it reaches the shell. To learn the read and write helpers used with the pipe, see fgets() and fwrite().